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个问题,但我无法理解这些更难的问题。
如何解决这个问题的最佳方法以及这些解决方案如何实施?
答案 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%1000000000
或fn = (fn1 + fn2) % tailcut;
正在做的事情。 %
是modulus operator,如果我将左边的数字除以右边,剩下的是什么?
用同等代码解释最简单:
def mod(a,b):
while a > b:
a -= b
return a
所以,有一个快速的加法循环,它将斐波那契数字的最后九位数加在一起,等待它们变成pandigital。如果是,则计算斐波纳契数的整数值,并检查前九位数。
如果我需要更详细地介绍任何内容,请告诉我。