Использование логарифма вместо деления для больших чисел?

Я не мог придумать правильное название для своего вопроса, но позвольте мне представить свое дело; Я хочу рассчитать коэффициент значимости в виде: p = 1 - X / Y

Здесь X происходит из итеративного процесса; процесс выполняет большое количество шагов и подсчитывает, сколькими различными способами процесс может оказаться в разных состояниях (хранящихся в HashMap). После завершения итерации я выбираю несколько состояний и суммирую их значения. Трудно сказать, насколько велики эти числа, поэтому я намерен реализовать сумму как BigInteger.

Y, с другой стороны, получается из биномиального коэффициента с числами в тысячной шкале. Я склонен использовать logGamma для вычисления этих коэффициентов, которые в результате дают мне натуральный логарифм значения.

Меня интересует разделение X / Y наилучшим/наиболее эффективным способом. Если бы я мог получить X в натуральном логарифме, тогда я мог бы вычесть степени и получить результат как 1 - e ^ (lnX - lnY).

Я вижу, что BigInteger нельзя логарифмировать с помощью Math.log, что мне делать в этом случае?


person posdef    schedule 20.02.2011    source источник
comment
Поскольку вы будете вести журналы, результат не будет полностью точным, и преимущества BigInteger будут потеряны. Не могли бы вы тогда использовать двойные вместо этого?   -  person Hovercraft Full Of Eels    schedule 20.02.2011


Ответы (3)


Возможно, вы сможете использовать двойники. Двойник может быть очень большим, около 1,7e308. Чего ему не хватает, так это точности: он поддерживает только около 15 цифр. Но если вы можете жить с точностью до 15 знаков (другими словами, если вас не волнует разница между 1 000 000 000 000 000 и 1 000 000 000 000 001), то двойные числа могут подойти достаточно близко.

person Willis Blackburn    schedule 20.02.2011
comment
интересно, я не знал, что удвоения могут содержать такие большие числа, хотя я должен спросить, какой смысл хранить 300+ цифр, когда вы не можете использовать ничего, кроме первых 15? Что ж, моя проблема в том, что, поскольку я упомянул больше о делении, я думаю, это не должно иметь большого значения ... Я попробую и посмотрю, что произойдет :) - person posdef; 20.02.2011
comment
Просто чтобы быть ясным, я имею в виду, что нужно сложить все с помощью BigInteger, а затем преобразовать в double для создания журнала. - person Willis Blackburn; 20.02.2011
comment
Что касается вопроса, который вы задали, я думаю, что простой ответ заключается в том, что хранение более 300 цифр с точностью до 15 цифр полезно, когда вам нужно работать с числом такой величины, но вам не нужно иметь точность более 15 цифр. :-) Но если использовать реальный пример, государственный долг США в центах составляет около 15 цифр, и вы можете себе представить, что если ваша работа связана с отслеживанием долга, вы, вероятно, не слишком заботитесь о десятых или сотых долях цента. - person Willis Blackburn; 20.02.2011
comment
Дело в том, что точность для двойников не постоянна. Наибольшая возможная ошибка для математики fp в соответствии со стандартом IEEE-754 составляет 0,5 ULP (т. е. наилучший возможный результат — это довольно сложно в вычислительном отношении, поэтому библиотеки быстрой математики часто увеличивают его), но относительный коэффициент находится между: 0,5 * b^- p ‹= 0,5*ULP ‹= b/2 * b^-p. B (umn beta) является основанием, а p - точностью. Подробности см. в статье Рут Голдберг «Что должен знать каждый ученый-компьютерщик об арифметике с плавающей запятой». Google найдет более чем достаточно. - person Voo; 20.02.2011
comment
Ах, и обратите внимание, что стандартная математическая библиотека Java гарантирует, что результат будет в пределах 1-2 ulps, если важна точность, StrictMath будет следовать стандарту IEEE. - person Voo; 20.02.2011

Если вы вычисляете биномиальные коэффициенты для тысячных чисел, то Doubles будет недостаточно.

Вместо этого я был бы склонен вызывать метод toString для числа и вычислять журнал как log(10) * number.toString().length() + log(asFloat("0." + number.toString()), где asFloat принимает строковое представление числа и преобразует его в число с плавающей запятой.

person btilly    schedule 20.02.2011
comment
Я не уверен, что понимаю, не могли бы вы уточнить или, в качестве альтернативы, предоставить ресурс, где я могу немного прочитать ваше предложение? - person posdef; 21.02.2011
comment
Представьте, что у вас есть математика с бесконечной точностью. Тогда n-значное целое число N всегда можно переписать (N/10^n)*10^n. Первое число представляет собой число с плавающей запятой в форме 0 (цифры N), что мы можем достаточно точно представить внутри числа с плавающей запятой, а затем взять логарифм, а логарифм другого равен 10*log(10 ). Это действительно все мое предложение прямо здесь. - person btilly; 21.02.2011

Если вам нужна максимальная точность, как насчет преобразования BigIntegers в BigDecimals и выполнения над ними алгебры. Если точность не имеет первостепенного значения, то, возможно, вы можете преобразовать свои BigInteger в двойные числа и выполнить с ними простую алгебру. Возможно, вы могли бы рассказать нам больше о своей проблемной области и почему вы считаете, что логарифмы — лучший способ.

person Hovercraft Full Of Eels    schedule 20.02.2011
comment
ты имеешь в виду деление на BigDecimal с? Потенциальная проблема заключается в получении биномиального коэффициента из логарифмического значения в BigDecimal или BigInteger, если уж на то пошло. Кажется, было бы разумнее работать с логарифмической шкалой. Я не уверен в том, что вы подразумеваете под проблемной областью, но, как я уже упоминал в вопросе, я работаю над инструментом, который ищет возможные результаты многоэтапного процесса и вычисляет соотношение. Если вам нужны конкретные детали, я постараюсь уточнить... - person posdef; 20.02.2011