如何更快地实现Project Euler 25,这样我才能真正计算答案?

时间:2011-07-11 05:05:31

标签: c++ performance

以下是Problem 25 - Project Euler的实现(请参阅代码中的注释以了解其工作原理):

#include <iostream> //Declare headers and use correct namespace
#include <math.h>

using namespace std;

//Variables for the equation F_n(newTerm) = F_n-1(prevTerm) + Fn_2(currentTerm)
unsigned long long newTerm = 0;
unsigned long long prevTerm = 1; //F_1 initially = 1
unsigned long long currentTerm = 1; //F_2 initially = 2

unsigned long long termNo = 2; //Current number for the term

void getNextTerms() { //Iterates through the Fib sequence, by changing the global variables.
    newTerm = prevTerm + currentTerm; //First run: newTerm = 2
    unsigned long long temp = currentTerm; //temp = 1
    currentTerm = newTerm; //currentTerm = 2
    prevTerm = temp; //prevTerm = 1
    termNo++; //termNo = 3
}

unsigned long long getLength(unsigned long long number) //Returns the length of the number
{
    unsigned long long length = 0;
    while (number >= 1) {
        number = number / 10;
        length++;
    }
    return length;
}

int main (int argc, const char * argv[])
{
    while (true) {
        getNextTerms(); //Gets next term in the Fib sequence
        if (getLength(currentTerm) < 1000) { //Checks if the next terms size is less than the desired length
        }
        else { //Otherwise if it is perfect print out the term.
            cout << termNo;
            break;
        }
    }
}

这适用于该示例,并且只要这一行就会快速运行:

        if (getLength(currentTerm) < 1000) { //Checks if the next term's size is less than the desired length

表示20或更低而不是1000.但是如果这个数字大于20就需要一个永远,我的耐心越来越好,我停止了程序,我怎样才能使这个算法更有效率?

如果您有任何问题,请在评论中提问。

7 个答案:

答案 0 :(得分:4)

Fibonachi数字(以及任何线性递归序列)都有一个封闭的公式。

所以F_n = C1 * a^n + C2 * b^n,其中C1,C2,a和b是可以从初始条件中找到的数字,即来自

的Fib案例

F_n + 2 = F_n + 1 + F_n

F_1 = 1

F_2 = 1

我不是故意在这里给出他们的价值观。这只是一个暗示。

答案 1 :(得分:3)

nth fibonacci number = =

(g1^n-g2^n)/sqrt(5). 
where g1 = (1+sqrt(5))/2 = 1.61803399
      g2 = (1-sqrt(5))/2 = -0.61803399

为了找到第n个斐波那契数的长度,我们可以计算log(第n个斐波纳契数)。那么,第n个斐波纳契数的长度是,

 log((g1^n-g2^n)/sqrt(5)) = log(g1^n-g2^n)-0.5*log(5).
 you can just ignore g2^n, since it is very small negative number.

因此,第n个斐波那契的长度是

n*log(g1)-0.5*log(5)

我们需要找到'n'的最小值,使得这个长度= 1000,所以我们可以找到长度大于999的n的值。

所以,

n*log(g1)-0.5*log(5) > 999
n*log(g1) > 999+0.5*log(5)
n > (999+0.5*log(5))/log(g1)
n > (999.3494850021680094)/(0.20898764058551)
n > 4781.859263075

因此,所需的最小n是4782.不使用任何编码,最简单的方法。

注意:在基数10中使用了所有日志。

答案 2 :(得分:1)

这可能会加快它的速度:

int getLength(unsigned long long number) //Returns the length of the number when expressed in base-10
{
    return (int)log10(number) + 1;
}

...但是,使用unsigned long long无法达到1000位数。我建议研究任意精度的算术库,或内置任意精度算术的语言。

答案 3 :(得分:0)

您可以尝试使用矩阵求幂来计算斐波纳契数。然后重复加倍以获得具有超过1000个数字的数字并在该范围内使用二分搜索来找到第一个。

答案 4 :(得分:0)

使用双打,你可以知道最高指数是308:

得到250的exp序列,然后将你的两个数字除以1e250。使用这两个数字重新启动算法

如果你这样做4次,你会得到正确的答案

答案 5 :(得分:0)

C ++代码可能如下:

#include "iostream"
#include "string.h"
#include "algorithm"
using namespace std;

string addTwoString(string a, string b)
{
    if (a.length() == 0)
    {
        return b;
    }

    if (b.length() == 0)
    {
        return a;
    }
    reverse(a.begin(), a.end());
    reverse(b.begin(), b.end());
    string result = "";
    string str_1, str_2;
    if (a.length() > b.length())
    {
        str_1 = b;
        str_2 = a;
    }
    else
    {
        str_1 = a;
        str_2 = b;
    }
    int index = 0;
    int value = 0, over_value = 0;
    for (; index < str_1.length(); ++index)
    {
        int temp_1 = (int)(str_1[index] - '0');
        int temp_2 = (int)(str_2[index] - '0');
        int temp = temp_1 + temp_2 + over_value;
        value = temp % 10; 
        over_value = temp / 10; 
        char c = (char)(value + '0');
        result += c;
    }
    for (; index < str_2.length(); ++index)
    {
        int temp_2 = (int)(str_2[index] - '0');
        int temp = temp_2 + over_value;
        value = temp % 10;
        over_value = temp / 10;
        char c = (char)(value + '0');
        result += c;
    }

    if (over_value > 0)
    {
        char c = (char)(over_value + '0');
        result += c;
    }
    reverse(result.begin(), result.end());
    return result;
}

int main()
{
    string a = "1";
    string b = "1";
    string c = addTwoString(a, b);
    int index = 3;
    while (c.length() < 1000)
    {
        a = b;
        b = c;
        c = addTwoString(a, b);
        ++ index;
    }
    cout << index << endl;
}

答案 6 :(得分:0)

我刚刚使用了一个递归函数,它可以垂直添加数组来完成问题。基本上零运行时间,少于50行代码。享受:

#include <stdio.h>

int Calc_Fib (int numA[], int numB[], int temp[], int index) {
    int i = 0;

    //Check 1000th digit for non-zero value.
    if (numB[999] != 0) return index;

    //Add arrays A and B vertically.
    for (i = 0; i < 1000; ++i)   {
        temp[i] += (numA[i] + numB[i]);

        if (temp[i] > 9)   {
            temp[i + 1] = temp[i] / 10;
            temp[i] %= 10;
        }

        numA[i] = numB[i];
        numB[i] = temp[i];
        temp[i] = 0;
    }
    Calc_Fib(numA, numB, temp, ++index);
}

int main()  {
    int numA[1000];   //Holds previous term.
    int numB[1000];   //Holds current term.
    int temp[1000];   //Holds temporary number for vertical addition.
    int i        = 0;
    int indexVal = 2;

    for (i = 0; i < 1000; ++i)  {
        numA[i] = 0;
        numB[i] = 0;
        temp[i] = 0;
    }

    //Initialize first two terms.
    numA[0] = (numB[0] = 1);

    indexVal = Calc_Fib(numA, numB, temp, indexVal);

    printf("Tada: %d\n", indexVal);

    return 0;
}