Точный расчет числа Фибоначчи в C ++? - PullRequest
4 голосов
/ 10 марта 2012

Я действительно смущен. Я пытаюсь вычислить числа Фибоначчи, но когда они становятся все больше и больше, числа начинают становиться неправильными. и я не знаю почему.

Как вы рассчитываете точные числа Фибоначчи, используя формулу Бине, насколько я понимаю, это всегда должно возвращать целое число?

Вот то, с чем я пытался работать.

http://ideone.com/e6t6h

Посмотрите, как число увеличивается. это становится все странно?

здесь я распечатываю его cout.precision (15);

http://ideone.com/XREh2

здесь я распечатываю его cout << fixed << бла-бла; </p>

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

Этот более точный, чем тот, который использует формулу Бине.

Во всяком случае. У кого-нибудь есть какой-нибудь код, на который я могу посмотреть, который может вычислить F (n) без необходимости итерации по каждому уровню (n) по формуле Бине?

Ответы [ 3 ]

15 голосов
/ 10 марта 2012

Чтобы точно рассчитать числа Фибоначчи по формуле Бине, вам нужна точная интерпретация & radic; 5. Поскольку & radic; 5 иррационально, его нельзя точно представить, используя double или float, поэтому формула Бине не работает с этими типами (однако округление в вычислениях приводит к точным результатам для некоторых небольших входных данных). Так как числа Фибоначчи являются целыми числами, вы можете получить точные результаты по формуле Бине, используя double или float для большего количества аргументов, округлив впоследствии,

double binet(unsigned int n)
{
    static const double phi = (1 + sqrt(5))*0.5;
    double fib = (pow(phi,n) - pow(1-phi,n))/sqrt(5);
    return round(fib);
}

Это вернет правильный результат для почти всех n достаточно малых, чтобы результат мог быть точно представлен как double. Это не много, однако. A double обычно имеет точность только 53 бита, поэтому только числа Фибоначчи, меньшие 2 53 , могут быть точно представлены как double (плюс несколько больших, делимых на достаточно большие степени 2). Последнее число Фибоначчи, меньшее 2 53 , равно F (77), но F (78) делится на 8, поэтому также может быть точно представлено как double с точностью 53 бита. Однако приведенное выше дает правильные результаты только для n <= 70, здесь, начиная с 71, ошибка округления слишком велика (кстати, результат формулы Бине, использующей doubles, здесь всегда слишком велик, поэтому вместо * используется floor 1020 * даст правильный результат также для F (71), но не дальше).

При стандартных типах данных не так уж много представляемых чисел Фибоначчи, последний из 64-битных типов (без знака) - F (93); для 128 бит последний F (186). Для столь малых индексов практически ничего нельзя получить по сравнению с простым итеративным алгоритмом

unsigned long long fibonacci(unsigned int n)
{
    unsigned long long a = 0, b = 1;
    for(; n > 0; --n)
    {
        b += a;
        a = b-a;
    }
    return a;
}

, если вы не используете справочную таблицу

static const unsigned long long fibs[94] = { 0, 1, 1, 2, ... , 12200160415121876738ull };

Для получения точных результатов нужно рассматривать & radic; 5 (и / или & phi;) как символическую константу и оценивать формулу, используя ее. Это равносильно оценке формулы в кольце

ℤ[φ] = { a + b*φ : a, b ∈ ℤ }

алгебраических целых чисел в ℚ(√5), используя тот факт, что φ² = 1 + φ. Эквивалент Бине формулы

φ^n = F(n-1) + φ*F(n)

, который можно использовать для эффективного вычисления чисел Фибоначчи путем многократного возведения в квадрат с O (log n) шагами (но учтите, что F (n) имеет & Theta; (n) битов, поэтому число битовых операций не может быть меньше, чем На)). Немного более эффективная версия, чем ванильный повторный квадрат

φ^(2n) = (φ^n)² = (F(n-1) + φ*F(n))² = F(n-1)² + φ*2*F(n-1)*F(n) + φ²*F(n)²
       = (F(n-1)² + F(n)²) + φ*(2*F(n-1)*F(n) + F(n)²)

найти F(2n) = 2*F(n)*F(n-1) + F(n)² = 2*F(n)*F(n+1) - F(n)² = F(n)*(F(n+1) + F(n-1)) и F(2n+1) = F(n)² + F(n+1)², используя φ² = 1 + φ. Эти формулы позволяют вычислять F (2n), F (2n + 1) и F (2n + 2) из ​​F (n) и F (n + 1) с не более чем двумя умножениями и двумя сложениями / вычитаниями на число, что дает Алгоритм для вычисления пары (F(n),F(n+1)) в O (log n) шагах только с двумя числами в качестве состояния (повторное возведение в квадрат ванили использует четыре числа в качестве состояния и требует еще нескольких умножений).

Итерационный алгоритм слева направо равен

unsigned long long fib(unsigned int n){
    if (n == 0) return 0;
    unsigned int h = n/2, mask = 1;
    // find highest set bit in n, can be done better
    while(mask <= h) mask <<= 1;
    mask >>= 1;
    unsigned long long a = 1, b = 1, c; // a = F(k), b = F(k+1), k = 1 initially
    while(mask)
    {
        c = a*a+b*b;        // F(2k+1)
        if (n&mask)
        {
            b = b*(b+2*a);  // F(2k+2)
            a = c;          // F(2k+1)
        } else {
            a = a*(2*b-a);  // F(2k)
            b = c;          // F(2k+1)
        }
        mask >>= 1;
    }
    return a;
}

С произвольным типом точности вместо unsigned long long, что позволяет быстро вычислять большие числа Фибоначчи. Но, разумеется, библиотеки произвольной точности часто поставляются с собственными оптимизированными функциями Фибоначчи, так что это своего рода самореализация для реализации самостоятельно.

2 голосов
/ 10 марта 2012

Как правило, числа с плавающей запятой и двойные числа не предназначены для точного представления чисел. Их цель - представлять реальные числа в широком диапазоне. Если вам нужна бесконечная точность, попробуйте заглянуть в http://gmplib.org/

1 голос
/ 10 марта 2012

Вы пробовали включить <cmath> вместо <math.h>, math.h может не иметь перегруженной версии sqrt, как для C

...