检查数字的可分性

时间:2014-03-18 13:34:01

标签: python optimization python-3.x modulus

这来自math.se post。我正在检查2^(n-1)+3是否可以被n整除。这是我写的代码,

def ck(n):
    c=pow(2,n-1,n)+3
    return not c%n

for i in range(10**7,2*10**7):
    if ck(i):
        print(i)
        break

print('Search Complete')

函数ck首先使用buit-in 2^(n-1)%n计算pow,添加3并最终得到余数。在数学上,这与(2^(n-1)+3)%n相同,但速度要快得多,因为计算pow(a,b,c)的速度比pow(a,b)%c

我想知道我是否可以进行其他优化(在函数或for循环中)?

range(10**7,2*10**7)中的值只是虚拟值,我逐步增加,以便搜索不会失控。

[在某人得到错误的想法之前,我完全破解哈希]

2 个答案:

答案 0 :(得分:1)

这个版本稍微快一些,因为我们在这里放弃了函数调用开销:

print(next(i for i in range(lowerbound,upperbound) if not (pow(2,i-1,i)+3)%i), 'Search Complete')

这将通过我的快速和肮脏测量提供~10%的加速:

python /tmp/so1.py  46.54s user 0.00s system 99% cpu 46.558 total

VS

python /tmp/so2.py  52.50s user 0.01s system 99% cpu 52.530 total

我也试过,如果没有将%的结果转换为bool,但使用0测试对象标识会更快 - 但事实并非如此。

答案 1 :(得分:1)

我对@ ch3ka的答案进行了几次不同的修改,这是我找到的最快的版本。

我维护gmpy2所以我用它来进行数值计算。 gmpy2使用GMP多精度库,并且使用Python的本机整数类型通常更快。使用gmpy2.powmod(...)要比pow(...)快得多。

从原始问题的链接,需要gcd(i,30) == 1。接下来我尝试使用gmpy2.gcd(...)来消除不可能的i值。这大大减少了一半的运行时间。

然后我通过七次通过范围取消了对gmpy2.gcd(...)的调用。这再次将运行时间缩短了一半。最后,我使用concurrent.futures将测试分布在4个核心上。

以下是最终版本:

import sys
import time
from gmpy2 import powmod
from concurrent import futures

BLOCKSIZE = 10**8

def blocktest(block):
    start = max(10, block * BLOCKSIZE)
    end = (block + 1) * BLOCKSIZE

    now = time.time()
    result = []
    result.extend(i for i in range(30*(start//30) + 1, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 7, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 11, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 13, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 17, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 19, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 23, end, 30) if powmod(2, i-1, i) == i-3)
    result.extend(i for i in range(30*(start//30) + 29, end, 30) if powmod(2, i-1, i) == i-3)

    return (start, end - 1, time.time() - now, result)

if __name__ == "__main__":
    print("starting time: ", time.strftime("%H:%M:%S"))
    with futures.ProcessPoolExecutor(max_workers=4) as executor:
        for s, e, t, r in executor.map(blocktest, range(10)):
            print("range({:,}, {:,}) time: {} et: {:6.2f} {!r}".format(s, e, time.strftime("%H:%M:%S"), t, r))

测试到10 ** 9大约需要1分15秒。只花了16分多钟才找到第一个成功的价值:13957196317。