Эффективный способ вычислить математическую константу e

Стандартное представление постоянного e как сумма бесконечного ряда очень неэффективно для вычисления из-за многих операций деления. Так есть ли какие-либо альтернативные способы вычислить константу эффективно?

Спасибо!

Править

После следования некоторым Вашим ссылкам я полагаю, что эффективность прибывает из техники, названной двоичным разделением (в то время как представление является все еще упомянутым рядом), с которым я не был знаком. Если кто-то знаком с ним, не стесняйтесь способствовать.

29
задан Cinnamon 24 June 2010 в 10:50
поделиться

9 ответов

Поскольку невозможно вычислить каждую цифру 'e', ​​вам придется выбрать точку остановки.

двойная точность: 16 десятичных цифр

Для практических приложений «64-битное значение с плавающей запятой двойной точности, которое максимально близко к истинному значению 'e' - примерно 16 десятичных цифр» больше, чем адекватный.

Как сказал Кенни ™, это значение уже было предварительно рассчитано для вас в математической библиотеке. Если вы хотите рассчитать это самостоятельно, как указал Ханс Пассан, факториал уже растет очень быстро.Первые 22 члена в серии уже слишком велики для вычисления с такой точностью - добавление дополнительных членов из серии не изменит результат, если он сохранен в 64-битной переменной с плавающей запятой двойной точности. Я думаю, вам понадобится больше времени, чтобы моргнуть, чем вашему компьютеру, чтобы сделать 22 деления. Поэтому я не вижу причин для дальнейшей оптимизации.

тысячи, миллионы или миллиарды десятичных цифр

Как отметил Матье М., это значение уже вычислено, и вы можете загрузить его с веб-сайта Йи.

Если вы хотите вычислить это самостоятельно, такое количество цифр не поместится в стандартное число двойной точности с плавающей запятой. Вам нужна библиотека "bignum". Как всегда, вы можете либо использовать одну из множества уже доступных бесплатных библиотек bignum, либо заново изобрести колесо, создав свою собственную еще одну библиотеку bignum со своими особенностями.

Результат - длинный файл цифр - не очень полезен, но программы для его вычисления иногда используются в качестве тестов производительности и точности программного обеспечения библиотеки "bignum", а также в качестве стресс-тестов для проверки стабильности. и охлаждающая способность нового машинного оборудования.

На одной странице очень кратко описаны алгоритмы, которые Йи использует для вычисления математических констант .

В статье Википедии о «двоичном расщеплении» содержится гораздо больше деталей.Я думаю, что вы ищете числовое представление: вместо внутреннего хранения всех чисел в виде длинной серии цифр до и после десятичной точки (или двоичной точки), Йи хранит каждый член и каждую частичную сумму как рациональное число - как два целых числа, каждое из которых представляет собой длинную серию цифр. Например, предположим, что одному из рабочих процессоров была назначена частичная сумма,

... 1/4! + 1/5! + 1/6! + ... .

Вместо того, чтобы сначала выполнять деление для каждого члена, а затем складывать, а затем возвращать один результат с фиксированной точкой из миллиона цифр в центральный процессор менеджера:

// extended to a million digits
1/24 + 1/120 + 1/720 => 0.0416666 + 0.0083333 + 0.00138888

, что ЦП может сначала сложить все члены ряда вместе с рациональной арифметикой и вернуть рациональный результат в ЦП менеджера: два целых числа, возможно, из нескольких сотен цифр каждое:

// faster
1/24 + 1/120 + 1/720 => 1/24 + 840/86400 => 106560/2073600

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

Не забывайте избегать Преждевременной оптимизации , и всегда ProfileBeforeOptimizing .

24
ответ дан 28 November 2019 в 01:15
поделиться

С моей точки зрения, наиболее эффективный способ вычислить e с желаемой точностью - использовать следующее представление:

e: = lim (n -> inf): ( 1 + (1 / n)) ^ n

Особенно, если вы выберете n = 2 ^ x , вы можете вычислить потенцию с умножением всего на x, поскольку:

a ^ n = (a ^ 2) ^ (n / 2), если n% 2 = 0

1
ответ дан 28 November 2019 в 01:15
поделиться

Метод двоичного деления хорошо поддается метапрограмме шаблона, которая производит тип, представляющий рациональный тип, соответствующий приближенному значению e. 13 итераций кажется максимумом - любое большее число вызовет ошибку "переполнение интегральной константы".

#include <iostream>
#include <iomanip>

template<int NUMER = 0, int DENOM = 1>
struct Rational
{
    enum {NUMERATOR = NUMER};
    enum {DENOMINATOR = DENOM};

    static double value;
};

template<int NUMER, int DENOM>
double Rational<NUMER, DENOM>::value = static_cast<double> (NUMER) / DENOM;

template<int ITERS, class APPROX = Rational<2, 1>, int I = 2>
struct CalcE
{
    typedef Rational<APPROX::NUMERATOR * I + 1, APPROX::DENOMINATOR * I> NewApprox;
    typedef typename CalcE<ITERS, NewApprox, I + 1>::Result Result;
};

template<int ITERS, class APPROX>
struct CalcE<ITERS, APPROX, ITERS>
{
    typedef APPROX Result;
};

int test (int argc, char* argv[])
{
    std::cout << std::setprecision (9);

    // ExpType is the type containing our approximation to e.
    typedef CalcE<13>::Result ExpType;

    // Call result() to produce the double value.
    std::cout << "e ~ " << ExpType::value << std::endl;

    return 0;
}

Другой (не метапрограммный) вариант шаблона при компиляции вычисляет двойное приближение к e. В этом варианте нет ограничения на количество итераций.

#include <iostream>
#include <iomanip>

template<int ITERS, long long NUMERATOR = 2, long long DENOMINATOR = 1, int I = 2>
struct CalcE
{
    static double result ()
    {
        return CalcE<ITERS, NUMERATOR * I + 1, DENOMINATOR * I, I + 1>::result ();
    }
};

template<int ITERS, long long NUMERATOR, long long DENOMINATOR>
struct CalcE<ITERS, NUMERATOR, DENOMINATOR, ITERS>
{
    static double result ()
    {
        return (double)NUMERATOR / DENOMINATOR;
    }
};

int main (int argc, char* argv[])
{
    std::cout << std::setprecision (16);

    std::cout << "e ~ " <<  CalcE<16>::result () << std::endl;

    return 0;
}

В оптимизированной сборке выражение CalcE<16>::result () будет заменено фактическим двойным значением.

Оба выражения, вероятно, достаточно эффективны, поскольку вычисляют e во время компиляции :-)

1
ответ дан 28 November 2019 в 01:15
поделиться

Если вы используете double или float , там будет M_E константа уже в math.h .

#define M_E         2.71828182845904523536028747135266250   /* e */

Есть и другие представления e в http://en.wikipedia.org/wiki/Representations_of_e#As_an_infinite_series ; все они будут связаны с разделением.

10
ответ дан 28 November 2019 в 01:15
поделиться

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

e = 1 + 1/1! + 1/2! + 1/3! ...  

Расширение уравнения:

e = 1 + 1/(1 * 1) + 1/(1 * 1 * 2) + 1/(1 * 2 * 3) ...

Вместо вычисления каждого факториала знаменатель умножается на следующее приращение. Таким образом, сохранение знаменателя как переменной и его умножение приведет к некоторой оптимизации.

3
ответ дан 28 November 2019 в 01:15
поделиться

Если вы согласны с приближением до семи цифр, используйте

3-sqrt(5/63)
2.7182819

Если вам нужно точное значение:

e = (-1)^(1/(j*pi))

где j - мнимая единица, а пи - хорошо известная математическая константа (тождество Эйлера)

2
ответ дан 28 November 2019 в 01:15
поделиться

Я не знаю ни одного "более быстрого" вычисления, чем разложение ряда Тейлора, т.е.:

e = 1/0! + 1/1! + 1/2! + ...

или

1/e = 1/0! - 1/1! + 1/2! - 1/3! + ...

Учитывая, что они были использованы A. Yee, который вычислил первые 500 миллиардов цифр e, я полагаю, что не нужно много оптимизировать (или лучше, это можно оптимизировать, но никто еще не нашел способ, AFAIK)

EDIT

Очень грубая реализация

#include <iostream>
#include <iomanip>

using namespace std;

double gete(int nsteps)
{
  // Let's skip the first two terms
  double res = 2.0;
  double fact = 1;

  for (int i=2; i<nsteps; i++)
  {
    fact *= i;
    res += 1/fact;
  }

  return res;
}

int main()
{
  cout << setprecision(50) << gete(10) << endl;
  cout << setprecision(50) << gete(50) << endl;
}

Выходные данные

2.71828152557319224769116772222332656383514404296875
2.71828182845904553488480814849026501178741455078125
10
ответ дан 28 November 2019 в 01:15
поделиться

На этой странице есть хороший обзор различных методов вычисления.

Это маленькая программа на языке Си от Ксавье Гурдона для вычисления 9000 десятичных цифр e на вашем компьютере. Подобная программа существует для π и для некоторых других констант, определяемых средним значением гипергеометрических рядов.

[degolfed version from https://codereview.stackexchange.com/a/33019 ]

#include 
int main() {
 int N = 9009, a[9009], x;
 for (int n = N - 1; n > 0; --n) {
 a[n] = 1;
 }
 a[1] = 2;
 while (N > 9) {
 int n = N--;
 while (--n) {
 a[n] = x % n;
 x = 10 * a[n-1] + x/n;
 }
 printf("%d", x);
 }
 return 0;
 }

Эта программа [при кодировании] содержит 117 символов. Ее можно изменить, чтобы вычислить больше цифр (изменить значение 9009 на большее) и чтобы она была быстрее (изменить константу 10 на другую силу 10 и команду printf). Не столь очевидный вопрос - найти используемый алгоритм.

8
ответ дан 28 November 2019 в 01:15
поделиться

Из Википедии замените x на 1

alt text

-2
ответ дан 28 November 2019 в 01:15
поделиться
Другие вопросы по тегам:

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