项目Euler 104:需要帮助来理解解决方案

时间:2016-04-08 18:17:23

标签: python math

Project Euler Q104(https://projecteuler.net/problem=104)就是这样:

  

Fibonacci序列由递归关系定义:

     

Fn = Fn-1 + Fn-2,其中F1 = 1且F2 = 1.事实证明,F541,   它包含113个数字,是第一个斐波纳契数   最后九位是1-9 pandigital(包含所有数字1到9,   但不一定按顺序)。和F2749,包含575位数字,   是第一个斐波纳契数,前九位是1-9   pandigital。

     

鉴于Fk是前九个的第一个斐波纳契数   数字和最后九位数是1-9 pandigital,找到k。

我用Python编写了这个简单的代码:

def fibGen():
    a,b = 1,1
    while True:
        a,b = b,a+b
        yield a

k = 0
fibG = fibGen()
while True:
    k += 1
    x = str(fibG.next())
    if (set(x[-9:]) == set("123456789")):
        print x #debugging print statement
        if(set(x[:9]) == set("123456789")):
            break

print k

然而,它很好......永远。

让它运行30分钟后,感到困惑,我放弃了检查解决方案。

我在C#中遇到了这段代码:

long fn2 = 1;
long fn1 = 1;
long fn;

long tailcut = 1000000000;

int n = 2;
bool found = false;

while (!found) {
    n++;
    fn = (fn1 + fn2) % tailcut;
    fn2 = fn1;
    fn1 = fn;

    if (IsPandigital(fn)) {
        double t = (n * 0.20898764024997873 - 0.3494850021680094);
        if (IsPandigital((long)Math.Pow(10, t - (long)t + 8)))
            found = true;
    }
}

哪个......我几乎听不懂。我在VS中试了一下,得到了正确的答案并检查了线程的帮助。

我在Python中找到了这两个类似的答案。

在这里,http://blog.dreamshire.com/project-euler-104-solution/

一个来自线程:

from math import sqrt

def isPandigital(s):
    return set(s) == set('123456789')

rt5=sqrt(5)
def check_first_digits(n):
    def mypow( x, n ):
        res=1.0
        for i in xrange(n):
            res *= x
            # truncation to avoid overflow:
            if res>1E20: res*=1E-10
        return res
    # this is an approximation for large n:
    F = mypow( (1+rt5)/2, n )/rt5
    s = '%f' % F
    if isPandigital(s[:9]):
        print n
        return True

a, b, n = 1, 1, 1
while True:
    if isPandigital( str(a)[-9:] ):
        print a
        # Only when last digits are
        # pandigital check the first digits:
        if check_first_digits(n):
            break
    a, b = b, a+b
    b=b%1000000000
    n += 1

print n

这些工作非常快,不到1分钟! 我真的需要帮助理解这些解决方案。我真的不知道使用log之类的东西背后的含义或原因。虽然我可以轻松地完成前30个问题,但我无法理解这些更难的问题。

如何解决这个问题的最佳方法以及这些解决方案如何实施?

1 个答案:

答案 0 :(得分:1)

这两个解决方案的工作基础是,随着斐波纳契数越来越大,两个连续项之间的比率越接近Golden Ratio(1+sqrt(5))/2,大约为1.618。如果您有一个(大)斐波那契数,您可以轻松计算下一个,只需将其乘以该数。

我们从问题中知道只有大的斐波纳契数才能满足条件,所以我们可以用它来快速计算我们感兴趣的序列部分。

在您的实施中,要计算fib(n),您需要计算fib(n-1),需要计算fib(n-2),需要计算fib(n-3)等,它需要计算fib(n-2),计算fib(n-3)等。当n很大时,这是大量的函数调用。通过一次计算就可以知道接下来的数字是一个巨大的速度提升。计算机科学家会调用第一个方法O(n ^ 2)*:来计算fib(n),你需要n ^ 2个子计算。使用黄金均值,斐波那契序列变得(大约,但接近我们所需要的):

(using phi = (1+sqrt(5))/2)

1
1*phi
1*phi*phi = pow(phi, 2)
1*phi*phi*phi = pow(phi, 3)
...
1*phi*...*phi = pow(phi, n)
 \  n times /

因此,您可以进行O(1)计算:fib(n): return round(pow(golden_ratio, n)/(5**0.5))

接下来,有一些简化可以让你使用较小的数字。

如果我关注一个数字的最后九位数,那么进一步发生的事情并不是那么重要,所以我可以在第9位之后扔掉任何东西。这就是b=b%1000000000fn = (fn1 + fn2) % tailcut;正在做的事情。 %modulus operator,如果我将左边的数字除以右边,剩下的是什么?

用同等代码解释最简单:

def mod(a,b):
    while a > b:
        a -= b
    return a

所以,有一个快速的加法循环,它将斐波那契数字的最后九位数加在一起,等待它们变成pandigital。如果是,则计算斐波纳契数的整数值,并检查前九位数。

如果我需要更详细地介绍任何内容,请告诉我。

* https://en.wikipedia.org/wiki/Big_O_notation