どうもこんにちは
研究でシミュレート用のプログラムを書いています
大量の数を入力し、その平均値を求めるコードを書いているのですが、
誤差ができるだけ小さくなる方法はないでしょうか
入力する数はdouble型の実数値あるいはint型の整数値で、
個数は1億程度です。
最初は1つずつ足していたのですが、整数型の場合はオーバーフローしてしまい、実数型の場合も徐々に加算する値が相対的に小さくなり、誤差が大きくなっていきました。
100万個ずつに区切って平均を求め、それを後で合計する方法も考えましたが、あまりきれいな方法になりません
なにかいい方法はないでしょうか
No.2
- 回答日時:
C言語での変数の書式を知らないので、変なことを書いている場合があります。
ひとつの場合は、指数部が存在しない変数の場合には、
求めた和を保存する配列を作っておいて、
値を加える前の数値を保存しておいて、オーバーフローの割り込みが発生したときに、それまでの値を配列に保存します。そして加える値を最初の値として計算して行きます。
最後の計算として、配列の値を2のn乗で割って、オーバーフローしない桁に直して足します。
もうひとつの場合、指数部が存在する場合には、
求めた和を保存する配列を複数個作っておいて、
指数部が同じ値同士をたして行きます。指数部で桁落ちが発生するような状態になったときに、配列に保存します。
桁落ちの発生する範囲が指数部の値によって異なりますので、指数部の値によって配列を変える必要があります。ただ、この指数部の範囲が大きく変わるような状態での和を求めることは、「平均値」と呼べるものではなくなってしまう可能性が大きいです。
別法としては、指数部の持たない整数等に型変換をして、指数部が存在しない方法で計算して行く方法があります。
MS-DOSの時代ですと、多倍精度演算が各種公開されていました。当時の公開ライブラリを探すとよいものがあるかもしれません。また、多倍精度演算専用言語もあるようです。
http://search.yahoo.co.jp/search?p=%E5%A4%9A%E5% …
http://www.vector.co.jp/vpack/filearea/dos/prog/c/
どのような言語系をお使いかわかりませんが、ちょっと手直しをする程度で使えるはずです。
No.3ベストアンサー
- 回答日時:
No.5
- 回答日時:
例えば入力がint型 indtという変数で
int型が32bitなら
double a[32],b;
for(i=0;i<32:i++) a[i]=0.;
if(indt&0x80000000) a[0] += indt;
else if(indt&0x40000000) a[1] += indt;
else if(indt&0x20000000) a[2] += indt;
else if(indt&0x10000000) a[3] += indt;
else if(indt&0x08000000) a[4] += indt;
else if(indt&0x04000000) a[5] += indt;
else if(indt&0x02000000) a[6] += indt;
else if(indt&0x01000000) a[7] += indt;
...
else if(indt&0x00000002) a[30] += indt;
else a[31] += indt;
for(i=0;i<32:i++) b+=a[i];
でどうにかなるんでは?
つまり入力の精度によって、合計を求める変数を変えれば桁おちしにくいかも。
この回答への補足
a[0] に1億個とか値を加算すればa[0]の値が相対的にindtより大きくなっていき、精度が落ちていくと思います。
それに高々1つの値を加算するのに条件分岐を大量に使うのは避けたいです。
No.6
- 回答日時:
ほぼ同じ値の実数値が多数個あるなら、最初、単純に合計を求めて、それが大きな誤差を含んでいても、それを使って平均値を求め、次に、元の各データとこの平均値との差の合計を求める。
それに先ほどの平均値をデータの個数倍したものを加えれば、もう少し精度が上がります。別のやり方としては、最初データを隣り合った2つずつの組にして、それぞれの組の合計を、もとのデータ数の半分の大きさの配列の各要素に入れる、というのを繰り返せば、配列の長さが1回ごとに半分になり、最後は1になって、元の全データの合計が得られる、というのはどうですか。
回答ありがとうございます。
一度平均を求めてからあらためて平均値を求めるやり方は確かに精度が上がりました。
これも試してみようと思います。
2つずつ加算していく方法は考えましたが、処理がややこしくなりそうなので試してませんでした。
こちらも試してみようと思います。
No.7
- 回答日時:
>計算量が極端に多くなる処理は使えないのです。
だから、障害発生時のみの例外処理のある計算方法を答えたでしょう。
配列のアドレス(ポインター)の計算が増えますが、主記憶を内部レジスターに使っていたCPUが昔ありましたので、それぼとむちゃくちゃな計算方法ではありません。
>変数もintやdoubleなど基本のものしか使えません。
1ライン
アセンブラ
は使えませんか。補助レジスター(自由に使えるのは2つか3つですが)を使えば、今のCPUは32bitなので64-98bit演算が可能。
インテル系ならばSP, BP, IPを保護して、SI, DIを入出力ポインターに割り振り、EAXからEDXの4レジスターを使って128bit演算が可能なはず。
No.8
- 回答日時:
No.5 の回答者です。
1億個足すと精度が下がるとのことですが、
No.5のやりかたは、普通に足すのにくらべて31bit多く精度があります。
でも今きずいたけど、unsignedにしか対応してないな・・・。
signedに対応すると・・・。こんな感じ。
int i,j;
double a[32],b;
int indt,abs_dt;
int msk =0x4000000;
for( i=0;i<32;i++) a[i]=0.;
for (i=0;i<100000000;i++){
indt = rand(); //入力
abs_dt = (indt<0) ? ~indt : indt;
for(j=0;j<31;j++){
if(abs_dt & (msk>>j)){
a[j] += indt;
break;
}
}
}
for(i=0;i<32;i++) b+=a[i];
out = b/100000000.;
条件分岐が多いのなら、1ビット毎に分けているのを2ビットごととかにすれば、少なくなります。
No.10
- 回答日時:
>> それとも、int型かdouble型を使用しなさいという制約があるのでしょうか?
>そうです。
それでは、以下のような方法はいかがでしょうか。
合計を求める領域をint型とdouble型で用意します。(最初に0クリアしておきます)
int型に加算を繰り返していきますが、もし加算した結果がオーバーフローする場合は、加算する前のint型の値を、double型に加算します。
そして、int型を0クリアのち、int型に加算します。
上記の処理を繰り返していきます。
全て加算した後に、最後にint型の合計をdouble型に加算します。
そうするとdouble型に全ての合計が格納されています。
尚、オーバーフローしたかどうかは、加算前と加算後の値の大小を比較すれば判ります。加算前>加算後の場合、オーバーフローが起こっていると判断します。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- 統計学 標準誤差の求め方 2 2022/07/04 19:59
- 統計学 t検定について教えてください 2 2023/02/23 16:35
- C言語・C++・C# C言語 3 2022/10/04 15:07
- C言語・C++・C# このプログラミングの問題を教えてほしいです。 キーボードからデータ数nとn個のデータを入力し、平均値 3 2022/12/19 22:51
- C言語・C++・C# C言語 プログラミング 4 2022/05/22 11:53
- 建設業・製造業 土量算出 3 2022/09/26 19:57
- 統計学 統計学の問題です よろしくお願いします 代表値 次の15件のデータについて,以下の問いに答えよ。 結 1 2023/01/31 18:53
- 統計学 統計学の問題です よろしくお願いします 代表値 次の15件のデータについて,以下の問いに答えよ。 結 5 2023/01/31 23:35
- 統計学 不偏分散について 3 2022/03/29 15:57
- C言語・C++・C# C言語階乗の総和を求める 2 2023/03/04 23:31
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
プログラムでの数字につく”f”の...
-
float型とdouble型の変数の違い...
-
doubleの変数にintとintの割り...
-
C言語 関数プロトタイプ宣言の...
-
C言語で台形公式を使った二重積...
-
関数におけるif文とreturn文に...
-
C言語(プログラミング)関連の質...
-
C言語を実行すると-infが出てき...
-
C 開放してるのにエラー(doubl...
-
数値を指数部と仮数部に分離したい
-
マチンの公式による円周率のプ...
-
至急です! マクロ定義で #defi...
-
C# 分秒表示ついて
-
c言語で、繰り返し文の中で、0....
-
C言語でdouble型の小数点の引き...
-
学校の課題で2次方程式のプログ...
-
ニュートン法
-
C言語で表記についの質問です
-
c言語の問題
-
2分法で方程式の複数の解を自...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
プログラムでの数字につく”f”の...
-
float型とdouble型の変数の違い...
-
doubleの変数にintとintの割り...
-
C言語を実行すると-infが出てき...
-
C 開放してるのにエラー(doubl...
-
至急です! マクロ定義で #defi...
-
c言語で、繰り返し文の中で、0....
-
関数におけるif文とreturn文に...
-
C言語 関数プロトタイプ宣言の...
-
C言語初心者 構造体 課題について
-
C言語の型による処理速度の違い
-
Cで3乗根を求める方法
-
C言語で-23乗を取り扱うには
-
2分法で方程式の複数の解を自...
-
doubleは常に%lfとするべきなのか
-
c言語のコンパイルエラー canno...
-
C言語で直角三角形の斜辺を求め...
-
C言語のプログラムで#include<m...
-
int とdoubleの比較
-
C++で外積
おすすめ情報