我想计算像 n = 10^15 这样非常大的数字。
但是由于溢出错误,我无法进行计算。
xd = lambda n : ((((5+ sqrt(17)) * ((3 + sqrt(17)) ** n)) - ((5-sqrt(17))* ((3 - sqrt(17)) ** n)))/((2 ** (n+1)) * sqrt(17)))
即使n=1000,也无法进行计算。
不过,我应该提到我希望它的模数为1000000007。
那么,应该怎么解决呢?
我想计算像 n = 10^15 这样非常大的数字。
但是由于溢出错误,我无法进行计算。
xd = lambda n : ((((5+ sqrt(17)) * ((3 + sqrt(17)) ** n)) - ((5-sqrt(17))* ((3 - sqrt(17)) ** n)))/((2 ** (n+1)) * sqrt(17)))
查看数学堆栈交换上的答案,可以得知计算最简单的是a(n)。因此可以通过简单的递归进行计算,同时我们只使用乘法和加法,可以利用模算术规则,使操作的数字保持较小。
def s(n, mod):
a1 = 1
a2 = 3
for k in range(n-1):
a1, a2 = a2, (3*a2 + 2* a1) % mod
return (a1 + a2) % mod
mod = 1000000007
print(s(10, mod))
# 363314, as with the other formulas...
print(s(10**6, mod))
# 982192189
%timeit s(10**6, mod)
# 310 ms ± 6.46 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
%timeit s(10**7, mod)
# 3.39 s ± 93.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
s(10**7)
的计算时间仅比s(10**6)
多10倍。a[n]
的解决方案比使用浮点数更加简洁。需要注意的一点是,任何线性递归都有一个 O(log n)
的解决方案。 - user58697由于 n
的值非常高,整数溢出是显而易见的。
遵循以下模算术规则:
例如:对于 a^n,使用 a = (a%m * a%m)%m,n 次。
对于较大的 n
值,使用 Python 的 pow(x, e, m) 函数来计算模数,这需要更少的时间。
a^n
中的较小指数 n
,简单的乘法即可计算幂,但对于大幂次,则需要采用“平方法”(power by squaring)进行求解。 - Spektrea^b
中的a
是float
!因此需要固定或浮点幂...我会选择固定幂,因为可以在整数上进行计算,而不会出现任意精度log、exp引起溢出的问题。 - Spektre用整数计算的可行方法是使用二项式展开式来发展您的表达式。稍微重新排列一下后,我们可以得到一种相当简单的计算方法,对于偶数和奇数幂的项几乎有相同的公式:
def form(n, mod):
cnk = 1
total = 0
for k in range(n+1):
term = cnk * 3**k * 17**((n-k)//2)
if (n-k) % 2 == 1:
term *= 5
total += term
cnk *= (n-k)
cnk //= (k+1)
return (total // (2**n)) #% mod
from math import sqrt
def orig(n):
return ((((5+ sqrt(17)) * ((3 + sqrt(17)) ** n)) - ((5-sqrt(17))* ((3 - sqrt(17)) ** n)))/((2 ** (n+1)) * sqrt(17)))
for n in range(20):
print(n, orig(n), form(n, mod))
输出:
0 1.0000000000000002 1
1 4.0 4
2 14.000000000000002 14
3 50.0 50
4 178.0 178
5 634.0000000000001 634
6 2258.0 2258
7 8042.0 8042
8 28642.000000000004 28642
9 102010.00000000001 102010
10 363314.0 363314
11 1293962.0000000002 1293962
12 4608514.0 4608514
13 16413466.000000004 16413466
14 58457426.00000001 58457426
15 208199210.00000003 208199210
16 741512482.0000001 741512482
17 2640935866.000001 2640935866
18 9405832562.0 9405832562
19 33499369418.000004 33499369418
对于不太大的 n 值(在一台旧机器上测试过),它是“相当”快的:
#%timeit form(1000, mod)
# 9.34 ms ± 87.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
#%timeit form(10000, mod)
# 3.79 s ± 14.8 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
#%timeit form(20000, mod)
# 23.6 s ± 37.3 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
modpow(a,b,c)
比起pow(a,b) mod c
更快,因为你不需要使用大数。我看到一些人(年轻人或使用新语言如 JAVA 或 Python)使用powmod
名称来代替,但据我所知应该是modpow
。所以就像链接中的modpow
实现一样,使用平方法和模算术即可进行幂运算。请注意,不要改变原来的意思。 - Spektre