BCスケール:丸めを避ける方法は? (小二項確率計算)

BCスケール:丸めを避ける方法は? (小二項確率計算)

次のコードは、n回の試行でk回の成功イベントの2項確率を計算します。

n=144
prob=$(echo "0.0139" | bc)

echo -e "Enter no.:" 
read passedno

k=$passedno
nCk2() {
    num=1
    den=1
    for((i = 1; i <= $2; ++i)); do
        ((num *= $1 + 1 - i)) && ((den *= i))
    done
    echo $((num / den))
}

binomcoef=$(nCk2 $n $k)

binprobab=$(echo "scale=8; $binomcoef*($prob^$k)*((1-$prob)^($n-$k))" | bc)

echo $binprobab

$passedno(=k) を "5" と入力すると結果は "0.03566482" ではなく 0 と表示され、 "4" と入力すると結果は ".07261898" と表示されます。

出力の丸め値を取得せずに10進数8桁の指定された精度で出力を印刷するにはどうすればよいですか?

答え1

はじめに

116から7のパーセンテージを計算するとします。
7を116で割り、その結果に100を掛けます。結果は
次のようになります。

ここに画像の説明を入力してください。

bc -l <<< '(7/116)*100'
6.03448275862068965500

質問

小数点以下2桁まで正確にしたいので、scale=2;次のように追加します。

ここに画像の説明を入力してください。 ただし、次のような奇妙な結果が表示されます。

bc -l <<< 'scale=2; 100*(7/116)'
6.00

値を上げようとしていますscale

bc -l <<< 'scale=3; 100*(7/116)'
6.000

何らかの理由でscale=4;2つの位置まで正確ですが、次のような末尾の0sがあります。

bc -l <<< 'scale=4; 100*(7/116)'
6.0300

理由

計算の最初の部分は小数点以下の2桁まで正確で、残りは切り捨てられます。 ここに画像の説明を入力してください。 ここに画像の説明を入力してください。


解決策

理想的な解決策は、浮動小数点計算を基本的にサポートするbashことです。shなぜまだこれをやっていないのですか?わかりません。そうしないのが愚かなようです。それまでは、次のものを使用できます。

紀元前

  1. 変数を定義します。
  2. 設定scale
  3. 1で割ります。
bc -l <<< "x=(7/116)*100; scale=2; x/1"
6.03

印刷機能

  1. bcコマンドを置き換えるには、コマンドを...で"$(囲みます)"
  2. に渡してくださいprintf
  3. %.2f型文字列に使用されます(小数点以下の2桁までの精度を表す%f浮動小数点数を表します.2(例scale=2:))。
printf '%.2f\n' "$(bc -l <<< '(7/116)*100')"
6.03

アッ

  1. 注:awk操作もできます
  2. echo相互作用ループが何とか壊れていると思います。これを行うより良い方法があるかもしれません。
  3. 浮動小数点を制御することはできません。私はそれが何とか成し遂げられると確信しています。
echo | awk '{print 100*(7/116)}'
6.03448

答え2

今後、

prob=$(echo "0.0139" | bc)

必要ありません - できます

prob=0.0139

例えば、

$ prob=0.0139; echo "scale=5;1/$prob" | bc
71.94244

アンダーフローの問題に加えて、コードに別の問題があります。 Bash算術は、nCk2関数内の多数を処理するのに十分ではないかもしれません。たとえば、32ビットシステムでこの関数に10を渡すと、次のようになります。否定的な番号、-133461297271。

アンダーフローの問題を処理するには、他の回答で述べたように、より大きな計算を実行する必要があります。 OPに提供されているパラメータの場合、25〜30の範囲で十分です。

すべての算術演算を実行するようにコードを書き直しましたbc。私はbc次のように完全なスクリプトを作成しました。echobcここ文書Bashスクリプトでは、Bashからbc

#!/usr/bin/env bash

# Binomial probability calculations using bc
# Written by PM 2Ring 2015.07.30

n=144
p='1/72'
m=16
scale=30

bc << EOF
define ncr(n, r)
{
    auto v,i

    v = 1
    for(i=1; i<=r; i++)
    {
        v *= n--
        v /= i
    }
    return v
}

define binprob(p, n, r)
{
    auto v

    v = ncr(n, r)
    v *= (1 - p) ^ (n - r)
    v *= p ^ r
    return v
}

sc = $scale
scale = sc
outscale = 8

n = $n
p = $p
m = $m

for(i=0; i<=m; i++)
{
    v = binprob(p, n, i)
    scale = outscale
    print i,": ", v/1, "\n"
    scale = sc
}
EOF

出力

0: .13345127
1: .27066174
2: .27256781
3: .18171187
4: .09021610
5: .03557818
6: .01160884
7: .00322338
8: .00077747
9: .00016547
10: .00003146
11: .00000539
12: .00000084
13: .00000012
14: .00000001
15: 0
16: 0

答え3

n=144
prob=$(echo "0.0139" | bc)

echo -e "Enter no.:"
read passedno

k=$passedno
nCk2() {
    num=1
    den=1
    for((i = 1; i <= $2; ++i)); do
        ((num *= $1 + 1 - i)) && ((den *= i))
    done
    echo $((num / den))
}

binomcoef=$(nCk2 $n $k)
binprobab=$(echo "$binomcoef*($prob^$k)*((1-$prob)^($n-$k))" | bc -l)
printf "%0.8f\n" $binprobab 

答え4

説明してくれた@voicesに感謝します。 awkとprintfを使用したソリューション:

awk 'BEGIN {printf("%.2f\n",100*7/116)}'

関連情報