Работа с очень малыми числами в R

Мне нужно рассчитать список очень маленьких чисел, таких как

(0.1) ^ 1000, 0.2 ^ (1200),

а затем нормализуйте их, чтобы они суммировали до одного т.е.

a1 = 0,1 ^ 1000, a2 = 0,2 ^ 1200

И я хочу рассчитать a1 '= a1/(a1 + a2), a2' = a2 (a1 + a2).

У меня проблемы с потоком, так как я получаю a1 = 0. Как я могу обойти это? Теоретически я мог бы работать с журналами, а затем log (a1) = 1000 * log (0.1) был бы способом представления a1 без проблем с потоком. Но для нормализации мне нужно было бы получить log (a1 + a2) - который я не могу вычислить, так как я не могу напрямую представить a1.

Я программирую с R - насколько я могу судить, нет такого типа данных, как Decimal в С#, который позволяет вам лучше, чем значение двойной точности.

Любые предложения будут оценены, спасибо

Ответы

Ответ 1

Математически говоря, одним из этих чисел будет appx. ноль, а другой. Разница между вашими числами огромна, поэтому мне даже интересно, имеет ли это смысл.

Но для этого вы можете использовать идею из logspace_add C-функции, которая находится под капотом R. Можно определить logxpy ( =log(x+y) ), когда lx = log(x) и ly = log(y) как:

logxpy <- function(lx,ly) max(lx,ly) + log1p(exp(-abs(lx-ly)))

Это означает, что мы можем использовать:

> la1 <- 1000*log(0.1)
> la2 <- 1200*log(0.2)

> exp(la1 - logxpy(la1,la2))
[1] 5.807714e-162

> exp(la2 - logxpy(la1,la2))
[1] 1

Эта функция может быть вызвана рекурсивно, если у вас больше номеров. Имейте в виду, 1 все равно 1, а не 1 минус 5.807...e-162. Если вам действительно нужна более высокая точность, и ваша платформа поддерживает длинные двойные типы, вы можете закодировать все, например, на C или С++, и позже возвращать результаты. Но если я прав, R может - на данный момент - иметь дело только с нормальными удвоениями, поэтому в конечном итоге вы снова потеряете точность, когда будет показан результат.


ИЗМЕНИТЬ:

чтобы сделать математику для вас:

log(x+y) = log(exp(lx)+exp(ly))
         = log( exp(lx) * (1 + exp(ly-lx) )
         = lx + log ( 1 + exp(ly - lx)  )

Теперь вы просто берете наибольшее как lx, а затем вы приходите к выражению в logxpy().

ИЗМЕНИТЬ 2: Зачем брать максимум тогда? Легко, чтобы убедиться, что вы используете отрицательное число в exp (lx-ly). Если lx-ly становится слишком большим, то exp (lx-ly) вернет Inf. Это не правильный результат. exp (ly-lx) вернет 0, что позволит получить гораздо лучший результат:

Скажите lx = 1 и ly = 1000, затем:

> 1+log1p(exp(1000-1))
[1] Inf
> 1000+log1p(exp(1-1000))
[1] 1000

Ответ 2

Пакет Brobdingnag имеет дело с очень большими или маленькими числами, по сути, обертывая ответ Joris в удобную форму.

a1 <- as.brob(0.1)^1000
a2 <- as.brob(0.2)^1200
a1_dash <- a1 / (a1 + a2)
a2_dash <- a2 / (a1 + a2)
as.numeric(a1_dash)
as.numeric(a2_dash)

Ответ 3

Возможно, вы можете рассматривать a1 и a2 как фракции. В вашем примере с помощью

a1 = (a1num/a1denom)^1000  # 1/10
a2 = (a2num/a2denom)^1200  # 1/5

вы пришли бы к

a1' = (a1num^1000 * a2denom^1200)/(a1num^1000 * a2denom^1200 + a1denom^1000 * a2num^1200)
a2' = (a1denom^1000 * a2num^1200)/(a1num^1000 * a2denom^1200 + a1denom^1000 * a2num^1200)

который можно вычислить с помощью пакета gmp:

library(gmp)
a1 <- as.double(pow.bigz(5,1200) / (pow.bigz(5,1200)+ pow.bigz(10,1000)))

Ответ 4

Попробуйте произвольный пакет точности mpfr.

Ryacas также может выполнять произвольную точность.