Масштаб до н.э.: Как избежать округления? (Вычислить малую биномиальную вероятность)

Эти "пропускающие" строки совершенно нормальны.

Поиск некоторой строки намного быстрее, чем отображение каждой строки на экране. Поэтому, если вы ищете слово, less просканирует файл на предмет этого слова и, как только найдет строку, отобразит только ту страницу строк, где было найдено слово.

Если вы прокрутите назад с помощью терминала, вы увидите эти «пропускающие» строки.

Если вы хотите вернуться в тексте назад, просто используйте соответствующие клавиши, например клавиши со стрелками. Это будет перемещать вас по тексту без каких-либо «пропусков».

PS: Вы можете ввести h в less , чтобы просмотреть список ключей и их назначение.

2
19.11.2019, 21:51
5 ответов

FWIW,

prob=$(echo "0.0139" | bc)

не нужно - вы можете просто сделать

prob=0.0139

Eg,

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

Есть еще одна проблема с вашим кодом, помимо проблемы с переполнением. Арифметика Bash может быть неадекватной для обработки больших чисел в вашей функции nCk2. Например, на 32-битной системе передача 10 в эту функцию возвращает отрицательное число, -133461297271.

Чтобы справиться с проблемой неполного переполнения, вам нужно вычислять в большем масштабе, как упоминалось в других ответах. Для параметров, приведенных в ОП, достаточно масштаба от 25 до 30.

Я переписал ваш код, чтобы вся арифметика выполнялась в bc. Вместо того чтобы просто передавать команды в bc через echo, я написал полный сценарий bc как here document внутри сценария 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

output

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
1
29.04.2021, 00:25
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 
1
29.04.2021, 00:25

Как вы знаете, это потому, что 0,0139 ^ 5 = .00000000051888844699 слишком мало и округлено до 0 с масштабом = 8 . Как насчет того, чтобы выполнить промежуточные вычисления с большим масштабом, а затем округлить последнее значение с помощью scale = 8 ?

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

Деление на 1 обычно используется для округления числа с масштабом в bc.

1
29.04.2021, 00:25

Спасибо за объяснение, @voices. Решение awk с printf:

awk 'BEGIN {printf("%.2f\n",100*7/116)}'
-1
29.04.2021, 00:25

Введение

Допустим, вы хотите вычислить, сколько процентов составляет 7 от 116.
Вы просто делите 7 на 116 и умножаете результат на 100.
Это должно быть примерно так:

enter image description here

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

Проблема

Вам нужна точность до двух знаков после запятой, поэтому вы добавляете scale=2;, ожидая этого:

enter image description hereНо вы получаете какие-то странные результаты:

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

Вы пытаетесь увеличить значение scale:

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

По какой-то причине scale=4;имеет точность до 2-х знаков, но имеет эти конечные 0с

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

Причина

Первая часть вычислений имеет точность до 2 знаков после запятой, а остальные обрезаются.enter image description hereenter image description here


Решение

Идеальным решением было бы, чтобы 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в строке формата(%fговорит с плавающей запятой, .2говорит с точностью до 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
29.04.2021, 00:25

Теги

Похожие вопросы