对于循环计算,递归关系需要很长时间

时间:2016-10-01 10:49:36

标签: python loops recurrence

Q(x)=[Q(x−1)+Q(x−2)]^2
Q(0)=0, Q(1)=1

我需要找到Q(29)。我在python中编写了一个代码,但这花了太长时间。如何获得输出(任何语言都可以)?

这是我写的代码:

a=0
b=1
for i in range(28):
    c=(a+b)*(a+b)
    a=b
    b=c
print(b)

4 个答案:

答案 0 :(得分:2)

我不认为这是一个易于编程的问题。你的代码很慢的原因是,增长非常中的数字很快,而python使用无限精度的整数,所以它花费时间来计算结果。

使用双精度浮点数尝试代码:

a=0.0
b=1.0
for i in range(28):
    c=(a+b)*(a+b)
    a=b
    b=c
print(b)

答案是inf。这是因为答案远大于最大可表示的双精度数,即rougly 10^308。您可以尝试使用有限精度整数,但这些整数将具有更小的可表示最大值。请注意,使用双精度会导致精度下降,但您肯定不想知道您的huuuge数字的每一位数(旁注:I happen to know that you do,使您的工作更加困难)。

所以我的怀疑主义是一些数学背景:你的复发关系

Q[k] = (Q[k-2] + Q[k-1])^2

您可以从此序列的平方根中制定更易处理的序列:

P[k] = sqrt(Q[k])
P[k] = P[k-2]^2 + P[k-1]^2

如果你能解决P,你就会知道Q = P^2

现在,请考虑以下顺序:

R[k] = R[k-1]^2

从相同的初始值开始,这将始终小于P[k],因为

P[k] = P[k-2]^2 + P[k-1]^2 >= P[k-1]^2

(但这将是"相当接近"下限,因为与第二项相比,第一项总是无关紧要的)。我们可以构建这个序列:

R[k] = R[k-1]^2 = R[k-2]^4 = R[k-3]^6 = R[k-m]^(2^m) = R[0]^(2^k)

由于P[1 give or take]以值2开头,我们应该考虑

R[k] = 2^(2^k)

作为P[k]的下限,给出或取几个指数2.对于k=28,这是

P[28] > 2^(2^28) = 2^(268435456) = 10^(log10(2)*2^28) ~ 10^80807124

80807124的最终值至少为P位数,这是您正在寻找的数字的平方根。这使Q[28]大于10^1.6e8。如果您将该数字打印到文本文件中,则需要超过150兆字节。

如果您认为自己正在尝试完全处理这些整数,那么您就会明白为什么需要这么长时间,以及为什么要重新考虑您的方法。如果你可以计算这个庞大的数字怎么办?你会怎么做? python在屏幕上打印这个数字需要多长时间?这些都不是微不足道的,所以我建议您尝试在纸上解决问题,或找到解决方法。

请注意,您可以在python中使用符号数学包,例如sympy,以了解您的问题有多难:

import sympy as sym
a,b,c,b0 = sym.symbols('a,b,c,b0')
a = 0
b = b0
for k in range(28):
    c = (a+b)**2
    a = b
    b = c
print(c)

这需要一段时间,但它会在屏幕上显示Q[k]的显式表达式,只有b0作为参数。你只会"只有"必须将您的值替换为该怪物以获得确切的结果。您也可以尝试sym.simplify表达式,但我无法等待返回任何有意义的内容。

在午餐时间我让你的循环运行,它就完成了。结果

>>> import math
>>> print(math.log10(c))
49287457.71120789

所以k=28的下限有点大,可能是由于指数中的一个错误。存储此整数所需的内存是

>>> import sys
>>> sys.getsizeof(c)
21830612

大约是20 MB。

答案 1 :(得分:1)

这可以通过暴力解决,但它仍然是一个有趣的问题,因为它使用两种不同的“慢”操作,并且在选择正确的方法时存在权衡。

有两个地方,本地Python实现的算法很慢:大数字的乘法和大数字转换为字符串。

Python使用Karatsuba算法进行乘法运算。它的运行时间为O(n ^ 1.585),其中n是数字的长度。随着数字变大,它确实会变慢,但你可以计算Q(29)。

将Python整数转换为十进制表示的算法要慢得多。它的运行时间为O(n ^ 2)。对于大数字,它比乘法慢得多。

注意:转换为字符串的时间也包括实际计算时间。

在我的电脑上,计算Q(25)需要约2.5秒,但转换为字符串需要约3分9秒。计算Q(26)需要约7.5秒,但转换为字符串需要约12分36秒。随着数字的大小加倍,乘法时间增加3倍,字符串转换的运行时间增加4倍。转换为字符串的运行时间占主导地位。计算Q(29)大约需要3分20秒,但转换为字符串将花费超过12小时(我实际上并没有等待那么久)。

一个选项是gmpy2模块,可以访问速度非常快的GMP库。使用gmpy2,Q(26)可以在~0.2秒内计算并在~1.2秒内转换为字符串。 Q(29)可以在~1.7秒内计算并在~15秒内转换成一个字符串。 GMP中的乘法是O(n * ln(n))。转换为十进制比Python的O(n ^ 2)算法更快,但仍然比乘法慢。

最快的选择是Python的decimal模块。它不使用基数-2或二进制内部表示,而是使用基数-10(实际上是10的幂)内部表示。计算稍慢,但转换为字符串非常快;它只是O(n)。计算Q(29)需要~9.2秒,但计算和转换一起只需要~9.5秒。转换为字符串的时间仅为0.3秒。

以下是使用decimal的示例程序。它还总结了最终值的各个数字。

import decimal

decimal.getcontext().prec = 200000000
decimal.getcontext().Emax = 200000000
decimal.getcontext().Emin = -200000000

def sum_of_digits(x):
    return sum(map(int, (t for t in str(x))))

a = decimal.Decimal(0)
b = decimal.Decimal(1)
for i in range(28):
    c = (a + b) * (a + b)
    a = b
    b = c


temp = str(b)
print(i, len(temp), sum_of_digits(temp))

我没有把数百万个数字转换为字符串并在上面的讨论中添加它们的时间。每个版本的时间应该相同。

答案 2 :(得分:0)

这将花费太长时间,因为它是一种趋于无限的几何级数。

示例:

a=0

b=1

c=1*1 = 1

a=1

b=1

c=2*2 = 4

a=1

b=4

c=5*5 = 25

a=4

b=25

c= 29*29 = 841

a=25

b=841

.
.
.

答案 3 :(得分:0)

你可以检查c%10 == 0然后除以它,并在最后的乘数中划分它的次数,但最后它将是相同的大数。如果你真的需要做这个计算尝试使用C ++,它应该比Python运行得快。

这是用C ++编写的代码

X = [[(0.5, 0.5, 0.5), (1.0, 1.0, 1.0)], [(0.5, 0.5, 0.52), (1.0, 1.0, 1.0)], [(0.5, 0.52, 0.52), (1.0, 1.0, 1.0)], [(0.52, 0.52, 0.52), (1.0, 1.0, 1.0)], [(0.52, 0.52, 0.52), (1.0, 1.0, 1.0)]]