どうもこんにちは
研究でシミュレート用のプログラムを書いています
大量の数を入力し、その平均値を求めるコードを書いているのですが、
誤差ができるだけ小さくなる方法はないでしょうか
入力する数はdouble型の実数値あるいはint型の整数値で、
個数は1億程度です。
最初は1つずつ足していたのですが、整数型の場合はオーバーフローしてしまい、実数型の場合も徐々に加算する値が相対的に小さくなり、誤差が大きくなっていきました。
100万個ずつに区切って平均を求め、それを後で合計する方法も考えましたが、あまりきれいな方法になりません
なにかいい方法はないでしょうか
No.3ベストアンサー
- 回答日時:
No.12
- 回答日時:
#10です。
int型より更に精度が上がる方法が有りました。
double型は整数値であれば15桁まで、正確に格納できます。
つまり、999999999999999(9が15個)は、正確な数値として保持できます。
double型の合計を2つ用意します。
一方は小計用、他方は合計用とします。
どちらも0でクリアして開始します。
小計用に加算し続けます。
小計が99999999999999(9が14個)を越えたとき、その値を
合計に加算し、小計を0クリアします。
加算した時の小計は999999999999999(9が15個)以下のはずので、小計は
正確な数値を保持しています。
上記を最後まで行い、最後に小計を、合計に加算します。
合計には、今までの数値の総和が格納されています。
No.11
- 回答日時:
#6で回答した者です。
別法です。合計を求める再帰関数
double sum( double x[], int i, int j) {
int k;
if ( i>=j ) return x[i];
k=i+(j-i)/2;
return sum(x, i, k)+sum(x, k+1, j);
}
を使って
average=sum(x, 0, n-1)/n;
で求めるというのはどうですか。
ループで求める場合の10倍ぐらいの所要時間がかかるかもしれませんが
データ数が1億個でも数秒以内でできると思います。
回答ありがとうございます。
再帰処理は考えていませんでした。
というのも、たぶんクライアントからOKがでないと。。。
それとは別の機会に使用するかもしれないので覚えておきます。
No.10
- 回答日時:
>> それとも、int型かdouble型を使用しなさいという制約があるのでしょうか?
>そうです。
それでは、以下のような方法はいかがでしょうか。
合計を求める領域をint型とdouble型で用意します。(最初に0クリアしておきます)
int型に加算を繰り返していきますが、もし加算した結果がオーバーフローする場合は、加算する前のint型の値を、double型に加算します。
そして、int型を0クリアのち、int型に加算します。
上記の処理を繰り返していきます。
全て加算した後に、最後にint型の合計をdouble型に加算します。
そうするとdouble型に全ての合計が格納されています。
尚、オーバーフローしたかどうかは、加算前と加算後の値の大小を比較すれば判ります。加算前>加算後の場合、オーバーフローが起こっていると判断します。
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.7
- 回答日時:
>計算量が極端に多くなる処理は使えないのです。
だから、障害発生時のみの例外処理のある計算方法を答えたでしょう。
配列のアドレス(ポインター)の計算が増えますが、主記憶を内部レジスターに使っていたCPUが昔ありましたので、それぼとむちゃくちゃな計算方法ではありません。
>変数もintやdoubleなど基本のものしか使えません。
1ライン
アセンブラ
は使えませんか。補助レジスター(自由に使えるのは2つか3つですが)を使えば、今のCPUは32bitなので64-98bit演算が可能。
インテル系ならばSP, BP, IPを保護して、SI, DIを入出力ポインターに割り振り、EAXからEDXの4レジスターを使って128bit演算が可能なはず。
No.6
- 回答日時:
ほぼ同じ値の実数値が多数個あるなら、最初、単純に合計を求めて、それが大きな誤差を含んでいても、それを使って平均値を求め、次に、元の各データとこの平均値との差の合計を求める。
それに先ほどの平均値をデータの個数倍したものを加えれば、もう少し精度が上がります。別のやり方としては、最初データを隣り合った2つずつの組にして、それぞれの組の合計を、もとのデータ数の半分の大きさの配列の各要素に入れる、というのを繰り返せば、配列の長さが1回ごとに半分になり、最後は1になって、元の全データの合計が得られる、というのはどうですか。
回答ありがとうございます。
一度平均を求めてからあらためて平均値を求めるやり方は確かに精度が上がりました。
これも試してみようと思います。
2つずつ加算していく方法は考えましたが、処理がややこしくなりそうなので試してませんでした。
こちらも試してみようと思います。
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つの値を加算するのに条件分岐を大量に使うのは避けたいです。
お探しの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を探す
おすすめ情報
- ・「みんな教えて! 選手権!!」開催のお知らせ
- ・漫画をレンタルでお得に読める!
- ・【選手権お題その2】この漫画の2コマ目を考えてください
- ・2024年に成し遂げたこと
- ・3分あったら何をしますか?
- ・何歳が一番楽しかった?
- ・治せない「クセ」を教えてください
- ・【大喜利】看板の文字を埋めてください
- ・【大喜利】【投稿~12/17】 ありそうだけど絶対に無いことわざ
- ・【選手権お題その1】これってもしかして自分だけかもしれないな…と思うあるあるを教えてください
- ・【穴埋めお題】恐竜の新説
- ・我がまちの「給食」自慢を聞かせてっ!
- ・冬の健康法を教えて!
- ・一番好きな「クリスマスソング」は?
- ・集合写真、どこに映る?
- ・自分の通っていた小学校のあるある
- ・フォントについて教えてください!
- ・これが怖いの自分だけ?というものありますか?
- ・スマホに会話を聞かれているな!?と思ったことありますか?
- ・それもChatGPT!?と驚いた使用方法を教えてください
- ・見学に行くとしたら【天国】と【地獄】どっち?
- ・これまでで一番「情けなかったとき」はいつですか?
- ・この人頭いいなと思ったエピソード
- ・あなたの「必」の書き順を教えてください
- ・10代と話して驚いたこと
- ・14歳の自分に衝撃の事実を告げてください
- ・人生最悪の忘れ物
- ・あなたの習慣について教えてください!!
- ・都道府県穴埋めゲーム
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
c言語で、繰り返し文の中で、0....
-
C言語を実行すると-infが出てき...
-
C 開放してるのにエラー(doubl...
-
プログラムでの数字につく”f”の...
-
doubleの変数にintとintの割り...
-
数値を指数部と仮数部に分離したい
-
float型とdouble型の変数の違い...
-
浮動小数点の定数
-
(C,C++言語)関数の引数は自動キ...
-
C言語 関数プロトタイプ宣言の...
-
int とdoubleの比較
-
C言語で直角三角形の斜辺を求め...
-
2次方程式の解を求めるプログ...
-
C#イベント中の戻り値の設定の...
-
プログラミング
-
LU分解について
-
プログラミング
-
極座標から直交座標に変換
-
fgets()による繰り返し入力(c言語)
-
atan2 オーバーロード関数の呼...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
プログラムでの数字につく”f”の...
-
C言語を実行すると-infが出てき...
-
c言語で、繰り返し文の中で、0....
-
C 開放してるのにエラー(doubl...
-
doubleの変数にintとintの割り...
-
至急です! マクロ定義で #defi...
-
C言語 関数プロトタイプ宣言の...
-
float型とdouble型の変数の違い...
-
2次方程式の解を求めるプログ...
-
int とdoubleの比較
-
difftime()について
-
関数におけるif文とreturn文に...
-
物体が往復する動きを作りたい
-
Cで3乗根を求める方法
-
-1.#IND00と出てしまうのですが...
-
C言語の型による処理速度の違い
-
2分法で方程式の複数の解を自...
-
浮動小数点の定数
-
listに構造体を格納
-
c言語のコンパイルエラー canno...
おすすめ情報