如何更快地计算(x ** 2-2)%n-Python

时间:2018-12-11 13:15:03

标签: python python-3.x

x = 2**1000000
n = 2**100000000

(x**2-2)%n太慢。我找到了pow(),但是我不能使用它,因为我不能减去2。(pow(x, 2)-2)%n(x*x-2)%n也很慢。当我测试(x*x-2)时很快,但是当我添加模运算符时却很慢。有没有一种方法可以更快地计算(x**2-2)%n

5 个答案:

答案 0 :(得分:8)

您在解释器中运行此命令吗?我做了一些测试,发现主要的减速似乎来自解释器试图显示结果。

如果将表达式分配给变量,则解释器将不会尝试显示结果,并且很快:

x = 2**1000000
n = 2**100000000
result = (x**2-2)%n

附录:

我本来也想与MikeW的回答相同,如果您想让代码的每个部分更快,您可以利用Python内部的整数基数2表示法并使用按位左移:

x = 1 << 1000000
n = 1 << 100000000

要注意的是,这仅适用于xn为2的幂的情况,因此您必须格外小心,以免出现一次错误。 This answer很好地说明了移位的基本原理,但是Python与其他语言(例如C,C ++或Java)有点不同,因为Python整数是unlimited precision,所以您永远都不能完全离开移位就像其他语言一样。

答案 1 :(得分:1)

如果x始终是2的幂,并且n始终是2的幂,那么您可以使用字节数组上的位运算来轻松快速地对其进行计算,然后可以将其重新构造为“数字”。

如果2 ^ N为(二进制)1后跟N个零,那么(2 ^ N)^ 2为(二进制)1后跟2N个零。

2^3 squared is b'1000000'

如果您有数字2 ^ K(二进制1,后跟K个零),那么2 ^ K-2将是K-1 1(一个),后跟零。

eg 2^4 is 16 =  b'10000', 2^4 - 2 is b'1110'

如果您需要“%2 ^ M”,然后以二进制形式,则只需选择最后(低)个M位,而忽略其余位。

9999 is       b'10011100001111'
9999 % 2^8 is       b'00001111'

'

因此将各个部分组合起来,如果x = 2 ^ A和n = 2 ^ B,则

(x ^ 2-2)%n

将是:(二进制数的)(最后B位)(2 * A-1个“ 1”后跟一个“ 0”)

答案 2 :(得分:1)

一些模块规则:

1)(a + b)mod(n)= amod(n)+ bmod(N)

2)(a.b)mod(n)= amod(n).bmod(n)

因此您可以将方程式转换为:

(x ** 2-2)%n ==>(x.x-2)%n ==>(x%n)。(x%n)-(2%n)

如果n始终大于2,则(2%n)本身就是2。

求解(x%n):

如果x和n始终为2 **值;如果x> n,那么(x%n)= 0就是答案,如果x

所以答案是0-(2%n)或x ** 2-(2%n)

答案 3 :(得分:0)

如果要计算(x ** y-z)%n
相当于(((x ** y)%n-z)%n

Python pow函数包含一个模数作为可选参数,因为它非常常用,并且可以优化方式进行计算。所以你应该使用:

(pow(x, y, n) - z) % n

答案 4 :(得分:0)

OP在评论中说:这很慢,因为我将x分配给答案,然后重复该过程

我尝试这个:

x = 2**(1000*1000)
n = 2**(100*1000*1000)

import time
t0=time.time()
for i in range(6):
    x = (x*x-2)%n   
    t1=time.time()
    print(i,t1-t0)
    t0=t1

print(x<n)

"""    
0 0.0
1 0.4962291717529297
2 0.5937404632568359
3 1.9043104648590088
4 5.708504915237427
5 16.74528479576111  
True   
"""

这表明在此问题中它变慢了,因为x增长了,每个循环的位数翻了一番:

In [5]: %timeit  u=x%n
149 ns ± 6.42 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

如果%nx<n绝对不会花费时间。