bc scale: Como evitar o arredondamento? (Calcular pequena probabilidade binomial)

1

O código a seguir calcula a Probabilidade Binomial de um evento de sucesso k de n tentativas:

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

Quando para $ passedno (= k) "5" é inserido, o resultado é mostrado como 0 (em vez de "0.03566482") enquanto que com "4" passado eu recebo ".07261898".

Como posso imprimir a saída com uma precisão de 8 dígitos decimais sem obter o valor arredondado da saída?

    
por Aliakbar Ahmadi 29.07.2015 / 12:58

3 respostas

0

FWIW,

prob=$(echo "0.0139" | bc)

é desnecessário - você pode apenas fazer

prob=0.0139

Por exemplo,

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

Existe outro problema com o seu código, além do problema de underflow. A aritmética de bash pode não ser adequada para lidar com números grandes na sua função nCk2 . Por exemplo, em um sistema de 32 bits, passar 10 para essa função retorna um número negativo , -133461297271.

Para lidar com o problema de underflow, você precisa calcular em uma escala maior, conforme mencionado nas outras respostas. Para os parâmetros dados no OP, uma escala de 25 a 30 é adequada.

Eu reescrevi seu código para fazer toda a aritmética em bc . Em vez de apenas direcionar comandos para bc via echo , escrevi um script bc completo como aqui documento dentro de um script Bash, já que isso facilita a passagem de parâmetros do Bash para 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

saída

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
    
por 29.07.2015 / 16:59
1
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 
    
por 29.07.2015 / 13:54
1

Como você sabe, é porque 0.0139^5 = .00000000051888844699 é muito pequeno e arredondado para 0 com scale=8 . Que tal fazer cálculos intermediários com maior escala, então arredondar o último valor com scale=8 ?

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

A divisão por 1 é comumente usada para arredondar um número com scale em bc.

    
por 29.07.2015 / 13:55