C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[8]: 割合計算について


(過去ログ 91 を表示中)

[トピック内 10 記事 (1 - 10 表示)]  << 0 >>

■54359 / inTopicNo.1)  割合計算について
  
□投稿者/ ヤスト (1回)-(2010/10/14(Thu) 20:20:21)

分類:[Java] 

2010/10/14(Thu) 20:22:31 編集(投稿者)
2010/10/14(Thu) 20:22:23 編集(投稿者)

public static void main(String[] args) {

  //配列
  int data[]={150,25,25,14};
  
  for(int i=0; i<data.length; i++){

   total +=data[i];

  }

  int a = (int)Math.round((double)data[0]/total*100);
int b = (int)Math.round((double)data[1]/total*100);
int c = (int)Math.round((double)data[2]/total*100);
int d = (int)Math.round((double)data[3]/total*100);

  }

この実行結果は
int a = 70
int b = 12
int c = 12
int d = 7

合計:101

合計を100で出力させたいのですが、端数が出てきてしまいます。
このような場合は、どう対処すればよいのでしょうか。


よろしくお願いします。

引用返信 編集キー/
■54360 / inTopicNo.2)  Re[1]: 割合計算について
□投稿者/ やじゅ (1763回)-(2010/10/14(Thu) 20:58:01)
やじゅ さんの Web サイト
2010/10/14(Thu) 21:02:26 編集(投稿者)

No54359 (ヤスト さん) に返信
> この実行結果は
> int a = 70
> int b = 12
> int c = 12
> int d = 7
>
> 合計:101
>
> 合計を100で出力させたいのですが、端数が出てきてしまいます。
> このような場合は、どう対処すればよいのでしょうか。

基本は切り捨てにして、端数分をどこかに足すようにすればいいんじゃない。
もしく四捨五入にしたとしても、100からの差をどこかで調整する。
どこに調整するかは、仕様で決めておく。
一番少ないところに足すとか、一番多いところに足すとか、最後の値に足すとか

引用返信 編集キー/
■54361 / inTopicNo.3)  Re[2]: 割合計算について
□投稿者/ かたぎり (42回)-(2010/10/15(Fri) 01:47:34)
やり方は色々とありますけれど、
必ず整数でなければならない場合には、やじゅさんの方法が楽でしょうね。
もっと厳密にDouble値で、というのであれば、
 
  100<=で最も100に近く低い整数にして、100を引いた端数を要素数で割ってそれぞれから引く
or 100=>で最も100に近く高い整数にして、100から引いた端数を要素数で割ってそれぞれに足す

限りなく、100に近く、割合も偏らない結果になれるはずですが、必ず100になるとは保証できません。


引用返信 編集キー/
■54362 / inTopicNo.4)  Re[3]: 割合計算について
□投稿者/ なちゃ (472回)-(2010/10/15(Fri) 08:34:29)
No54361 (かたぎり さん) に返信
> もっと厳密にDouble値で、というのであれば、
>  
>   100<=で最も100に近く低い整数にして、100を引いた端数を要素数で割ってそれぞれから引く
> or 100=>で最も100に近く高い整数にして、100から引いた端数を要素数で割ってそれぞれに足す
>
> 限りなく、100に近く、割合も偏らない結果になれるはずですが、必ず100になるとは保証できません。

これが出来るなら最初から普通に計算するだけなのでは?

どうしても合わせたいなら、誤差のトータルから適切な要素に振り分けるしかないでしょうけど、
そもそも合わす必要が無いのでは?という気もします。

引用返信 編集キー/
■54366 / inTopicNo.5)  Re[4]: 割合計算について
□投稿者/ 中博俊 (1451回)-(2010/10/15(Fri) 11:54:56)
> どうしても合わせたいなら、誤差のトータルから適切な要素に振り分けるしかないでしょうけど、
> そもそも合わす必要が無いのでは?という気もします。

お金の配賦なんかじゃ絶対にあわせないといけませんからね
引用返信 編集キー/
■54368 / inTopicNo.6)  Re[5]: 割合計算について
□投稿者/ なちゃ (473回)-(2010/10/15(Fri) 12:41:57)
No54366 (中博俊 さん) に返信
> お金の配賦なんかじゃ絶対にあわせないといけませんからね

額の振り分けならそうですけど、今の計算は各金額のパーセント値を出したいだけに見えたので。

まあ会計的なことは分かりませんが、合計100になるように数値を操作するのも
それはそれでおかしい気がしたりもするんですけどね。
まあ用途によるでしょうけども。

引用返信 編集キー/
■54369 / inTopicNo.7)  Re[6]: 割合計算について
□投稿者/ ミラ (9回)-(2010/10/15(Fri) 13:15:50)
2010/10/15(Fri) 13:17:59 編集(投稿者)

とりあえず100になればいいのであれば最後の数字だけ調整すればいいんですかね

public static void main(String[] args) {

// 配列
int data[] = { 150, 25, 25, 14 };
int total = 0;

for (int i = 0; i < data.length; i++) {
total += data[i];

}

int result[] = new int[data.length];

int total_process = 0;

for (int i = 0; i < data.length - 1; i++) {
result[i] = (int)Math.round((double)data[i]/total*100);
total_process+=result[i];
}
result[data.length - 1] = 100 - total_process;

}

次に長いですが端数の小さいものを大きいものに移動する方法で考えてみました
javaは不勉強なのでコードに無駄が多少ありますがこんなのでどうでしょうか?

public static void main(String[] args) {

// 配列
int data[] = { 150, 25, 25, 14 };
int total = 0;

for (int i = 0; i < data.length; i++) {
total += data[i];

}

int result[] = new int[data.length];
double result_hasu[] = new double[data.length];

for (int i = 0; i < data.length; i++) {
result_hasu[i] = (double) data[i] / total * 100;
result[i] = (int) result_hasu[i];
result_hasu[i] -= result[i];
}

do {
int maxindex = -1;
int minindex = -1;
// 最大端数と最小端数のindexを算出
for (int i = 0; i < data.length; i++) {
if (result_hasu[i] == 0 || result_hasu[i] == 1) {
continue;
}
if (maxindex == -1) {
maxindex = i;
}
if (minindex == -1) {
minindex = i;
}
if (result_hasu[maxindex] < result_hasu[i]) {
maxindex = i;
}
if (result_hasu[minindex] > result_hasu[i]) {
minindex = i;
}
}
if (maxindex != -1) {
if (minindex != -1 && maxindex != minindex) {
// 最大端数が1になるまで最小端数を移動
double add = Math.min(1 - result_hasu[maxindex],
result_hasu[minindex]);
result_hasu[maxindex] += add;
result_hasu[minindex] -= add;
} else {
break;
}
}
} while (true);

for (int i = 0; i < data.length; i++) {
// 端数を結果に戻す
result[i] += Math.round(result_hasu[i]);
}
}
引用返信 編集キー/
■54374 / inTopicNo.8)  Re[6]: 割合計算について
□投稿者/ やじゅ (1765回)-(2010/10/15(Fri) 18:37:46)
やじゅ さんの Web サイト
なんか、サラミ法を使った犯罪を思い出しました。
■60年代末に発覚したニューヨーク在住の銀行員の犯行:
 顧客の預金利子を計算するプログラムで端数処理を四捨五入ではなく
 すべて切り捨て計算とし、剰余の利息を自分名義の口座に自動振込す
 るプログラムも付け加えて巨額の不正入金を得た。

設計時の見落とし − 按分処理後の端数補正
http://blog.yaju.jp/200812/article_12.html

引用返信 編集キー/
■54377 / inTopicNo.9)  Re[7]: 割合計算について
□投稿者/ れい (963回)-(2010/10/16(Sat) 01:56:15)
> 合計を100で出力させたいのですが、端数が出てきてしまいます。
> このような場合は、どう対処すればよいのでしょうか。

用途とか業界によって違います。
基本は「悪意」が無いということがわかるようにすることです。

やじゅさん。
> どこに調整するかは、仕様で決めておく。
> 一番少ないところに足すとか、一番多いところに足すとか、最後の値に足すとか

ほとんどの場合一番少ないところに足すことはないでしょう。
誤差が大きくなるので。

会計だとか経理だとかの場合、
四捨五入ではなく「最近接偶数への丸め&#8206;」を行って、
「その他」の項目がある場合はそこに含め、
ない場合は「一番大きいところ」に誤差を集めるのが普通でしょう。
一番大きいところは四捨五入の誤差がたまっても相対的に誤差が少なくなります。

数値計算などで、合計よりも個々の項目の絶対値が重要である場合は
合計を100にするのをあきらめるのが正しくなります。

項目が均等に分布しておらず、統計処理のためだけに用いる場合などでは
乱数を用いて丸めを行う場合もあります。
この場合、最後の項目でうまく帳尻が合うようにできます。

銀行の口座のように「サラミ」が問題になるような場合は「丸め誤差」という項目を入れることが正しい場合があります。

いずれにせよ、時と場合次第、です。
一般的な指針は最初に述べたように「悪意が入りこんでいない」ことがわかるようにすることです。

#最近接偶数丸めは「JIS Z 8401」を参照。
#会計・経理等に関しては規格かなにか決まっていと思うのですが、
#机をごそごそしても資料は見つかりませんでした。
引用返信 編集キー/
■54427 / inTopicNo.10)  Re[8]: 割合計算について
□投稿者/ ヤスト (2回)-(2010/10/19(Tue) 21:47:33)

ミラさん

ミラさんのコードで、割合計算が正常に行えるようになりました。
ありがとうございました。
解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -