使求和幂级数更高效
Make summing power series more efficient
我在 hackerrank 中编码并遇到了这个问题:
https://www.hackerrank.com/challenges/power-calculation
我的代码适用于小文件和大数字。至于大文件,它会超时。有人可以让它更有效率吗?
我的代码:
z = []
def modexp(a, n, m):
bits = []
while n:
bits.append(n%2)
n /= 2
solution = 1
bits.reverse()
for x in bits:
solution = (solution*solution)%m
if x:
solution = (solution*a)%m
return solution
for _ in xrange(int(input())):
while True:
try:
x = raw_input()
sum =0
z = x.split(' ')
power = int(z[1])
limit = int(z[0])
for i in range(0,limit+1):
sum = sum%100 + modexp(i%100,power, pow(10,2))
if sum < 10:
print '%02d' % sum
if sum > 10:
print sum%100
except:
break
示例数据 - 输入:
10
487348 808701
204397 738749
814036 784709
713222 692670
890568 452450
686541 933150
935447 202322
559883 847002
468195 111274
833627 238704
示例输出:
76
13
76
75
24
51
20
54
90
42
通过观察其值 mod 100 的周期为 100,可以轻松减少幂评估的次数。因此
通过计算 M=K/100; L=K%100;
分解 K = M*100+L
。
然后
- 对于
k=0
到L
次幂modexp(k%100,N,100)
出现M+1
次,
- 对于
k=L+1
到 99
它在总和中出现 M
次。
这样每次幂和可以减少到99次幂计算
通过观察相同数字的递增幂在最后两位数字中是周期性的,可以进一步减少计算幂的工作量。一般序列
1, a % m, a**2 % m, a**3 % m, a**4 % m, ...
在素因子的最高重数给出的某个点之后变得周期性。一个周期长度由欧拉 totient 函数中 m
的值给出。
100=2²·5²
的总值是phi(100)=(2-1)·2·(5-1)·5=40
。在句点设置之前的偏移量最多为 2,因此对于所有整数 a
a**2 % 100 == a**42 % 100 = a**82 % 100 = ...
a**3 % 100 == a**43 % 100 = a**83 % 100 = ...
等等。
这意味着对于 N>41
可以将指数减少到 N=2+(N-2) % 40
。 (事实上 ,在该减少中可以将 40
替换为 20
。)
作为最后的评论,不会对 运行 次产生太大影响,只会影响代码的复杂性:
有一个更短的实现方式modexp
,这个算法也是识别循环不变量的标准练习:
def modexp(a, n, m):
solution = 1
apower = a
while n:
if (n%2): solution = (solution*apower) % m
n /= 2
apower = (apower*apower) % m
return solution
我在 hackerrank 中编码并遇到了这个问题: https://www.hackerrank.com/challenges/power-calculation
我的代码适用于小文件和大数字。至于大文件,它会超时。有人可以让它更有效率吗?
我的代码:
z = []
def modexp(a, n, m):
bits = []
while n:
bits.append(n%2)
n /= 2
solution = 1
bits.reverse()
for x in bits:
solution = (solution*solution)%m
if x:
solution = (solution*a)%m
return solution
for _ in xrange(int(input())):
while True:
try:
x = raw_input()
sum =0
z = x.split(' ')
power = int(z[1])
limit = int(z[0])
for i in range(0,limit+1):
sum = sum%100 + modexp(i%100,power, pow(10,2))
if sum < 10:
print '%02d' % sum
if sum > 10:
print sum%100
except:
break
示例数据 - 输入:
10
487348 808701
204397 738749
814036 784709
713222 692670
890568 452450
686541 933150
935447 202322
559883 847002
468195 111274
833627 238704
示例输出:
76
13
76
75
24
51
20
54
90
42
通过观察其值 mod 100 的周期为 100,可以轻松减少幂评估的次数。因此
通过计算 M=K/100; L=K%100;
分解 K = M*100+L
。
然后
- 对于
k=0
到L
次幂modexp(k%100,N,100)
出现M+1
次, - 对于
k=L+1
到99
它在总和中出现M
次。
这样每次幂和可以减少到99次幂计算
通过观察相同数字的递增幂在最后两位数字中是周期性的,可以进一步减少计算幂的工作量。一般序列
1, a % m, a**2 % m, a**3 % m, a**4 % m, ...
在素因子的最高重数给出的某个点之后变得周期性。一个周期长度由欧拉 totient 函数中 m
的值给出。
100=2²·5²
的总值是phi(100)=(2-1)·2·(5-1)·5=40
。在句点设置之前的偏移量最多为 2,因此对于所有整数 a
a**2 % 100 == a**42 % 100 = a**82 % 100 = ...
a**3 % 100 == a**43 % 100 = a**83 % 100 = ...
等等。
这意味着对于 N>41
可以将指数减少到 N=2+(N-2) % 40
。 (事实上 ,在该减少中可以将 40
替换为 20
。)
作为最后的评论,不会对 运行 次产生太大影响,只会影响代码的复杂性:
有一个更短的实现方式modexp
,这个算法也是识别循环不变量的标准练习:
def modexp(a, n, m):
solution = 1
apower = a
while n:
if (n%2): solution = (solution*apower) % m
n /= 2
apower = (apower*apower) % m
return solution