如何模块化素数分解 Python 脚本以将列表作为输入?

问题描述

我有可用的 Python 代码(如下),可以破解 RSA 密钥并乘以质因数分解。如何将此代码放入一个模块中,该模块采用代表 n 的整数列表来创建范围 0...2^n 的 n 位素数?我想传入一个整数列表并返回一个包含 n 值和运行时间的表,如下所示:

n   run time
15  0.2
16  1.1
17  1.4
18  10.6
19  30.2
20  46.6

代码:

import random
import math
import timeit


while True:
    try:
        n = input("Please enter a number n for creating an n-bit prime number for range 0...2^n: ")
        n = int(n)
        break

    except ValueError:
        print('\nPlease enter an integer value.')
        
def isPrime(m):
    for i in range(2,m//2,1):
        if m % i == 0:
            return False
    return True

def nBitPrime(n):
    r = random.random()
    m = int(r * (2**n))
    if m >= 2 and isPrime(m) == True:
        return m
    return nBitPrime(n)

def getPQ(n):
    pq = nBitPrime(n) * nBitPrime(n)
    return pq

pq = getPQ(n)

def factor(pq):
    
    p = math.floor(math.sqrt(getPQ(n)))
    if p % 2 == 0: 
        p += 1
    while p < pq:
        if pq % p == 0:
            return p,int(pq/p)
        p += 2
        
def wrapper(func,*args):
    def wrapped():
        return func(*args)
    return wrapped

wrapped = wrapper(factor,n)
speed = (timeit.timeit(wrapped,number=1))*1000
print(round(speed,4))

提前致谢。

解决方法

这里有几个问题。首先,您的因子函数会重新生成一个新的 pq 值,忽略您之前生成的值。这计算了两次获得 pq 的时间。此外,测量使用随机数生成器的进程的单次执行时间并不能让您对性能有一个很好的了解。您需要进行多次运行才能获得平均值。

我稍微清理了您的代码并更改了测量时间的方式以获得平均超过 100 次运行:

您的代码:

import random
import math
import timeit
        
def isPrime(m):
    for i in range(2,m//2,1):
        if m % i == 0:
            return False
    return True

def nBitPrime(n):
    r = random.random()
    m = int(r * (2**n))
    if m >= 2 and isPrime(m) == True: 
        return m
    return nBitPrime(n)

def getPQ(n):
    pq = nBitPrime(n) * nBitPrime(n)
    return pq


def factor(pq):   
    p = math.floor(math.sqrt(pq))
    if p % 2 == 0: 
        p += 1
    while p < pq:
        if pq % p == 0:
            return p,int(pq/p)
        p += 2
        
    
while True:
    try:
        n    = input("Please enter a number n for creating an n-bit prime number for range 0...2^n: ")
        n    = int(n)
        time = timeit.timeit(lambda:factor(getPQ(n)),number=100)
        print(f"{time*10:.4f}")

    except ValueError:
        print('\nPlease enter an integer value.')

除了简单的代码优化之外,使用质数生成器和质数列表将大大加快速度。

以下是相同函数的示例实现,以更高效和 Pythonic 的方式帮助您制作更好的自己的函数:

素数生成(根据需要)

primes     = [2,3]
primeSkip  = {9:3}

# fill primes list up to the square root of N
def morePrimes(N):
    lastPrime = primes[-1]
    while lastPrime*lastPrime<N:
        lastPrime += 2
        if lastPrime not in primeSkip:
            primeSkip[lastPrime*lastPrime] = lastPrime
            primes.append(lastPrime)
            continue
        prime = primeSkip.pop(lastPrime)
        multiple = lastPrime + 2*prime
        while multiple in primeSkip: multiple += 2*prime
        primeSkip[multiple] = prime

主要测试

# prime test uses the known prime list as a search mechanism
# and only checks divisions for primes when a larger number is given

from bisect import bisect_left        
def isPrime(N):
    if N<=primes[-1]:
        return primes[bisect_left(primes,N)] == N
    morePrimes(N)
    for p in primes:
        if N%p == 0: return False
        if p*p>N: return True
    return True

随机 n 位素数

# non-recursive implementation (faster than recursive)
# also uses randrange() to get the appropriate values without multiplication
# and floating point arithmetics

def nBitPrime(n):
    p = random.randrange(2,2**n)
    while not isPrime(p):
        p = random.randrange(2,2**n)
    return p

因式分解

# factorisation uses primes going backward from the square root of pq
# given that p and q are supposed to be large primes,this should converge
# faster to the two factors.

def factor(pq):
    morePrimes(pq)
    i = bisect_left(primes,int(pq**0.5)+1)
    i = min(i,len(primes)-1)
    while i>0:
        p = primes[i]
        if pq%p == 0: return p,pq//p
        i -= 1            

测试运行

Your original code (cleaned up):

Please enter a number n for creating an n-bit prime number for range 0...2^n: 10
0.0531
Please enter a number n for creating an n-bit prime number for range 0...2^n: 15
1.2645
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
38.0535

...

# With the improved functions

Please enter a number n for creating an n-bit prime number for range 0...2^n: 10
0.0359
Please enter a number n for creating an n-bit prime number for range 0...2^n: 15
0.2044
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
3.76593

基于素数的函数有一些开销,抵消了较小位大小的好处,但它们在高端提供了显着的改进

其他注意事项

您为 p 和 q 生成的值不符合通常用于选择它们的标准。它们必须是彼此相距足够远的大素数。纯随机生成将产生易于因式分解的弱 pq 值(尽管在 32 位以下,基于素数的因式分解会很快找到它们)

这可能会好一点:

def getPQ(n):
    p = random.randrange(2**(n-4),2**(n-2))
    while not isPrime(p):
        p = random.randrange(2**(n-4),2**(n-2))
    q = random.randrange(p*2,2**n)
    while not isPrime(q):
        q = random.randrange(p*2,2**n)
    return p*q

将分解时间与随机 pq 生成分开测量也是一个好主意。您的破解功能通常用于现有密钥,因此已预先生成 pq 以生成密钥。这将突出显示 factor() 函数的实现与基于素数的函数之间的巨大性能差异。

while True:
    try:
        n    = input("Please enter a number n for creating an n-bit prime number for range 0...2^n: ")
        n    = int(n)
        pq   = getPQ(n)
        print("pq",pq) # exclude this from time measurement
        time = timeit.timeit(lambda:factor(pq),number=100)
        print(f"{time*10:.4f}")

    except ValueError:
        print('\nPlease enter an integer value.')

仅测试 factor() 函数的性能:

# Your code:

Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 78161507029
4.5724
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 414308573933
17.8163
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 192833207923
14.7995

...

# Prime based:

Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 36909677657
1.9210
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 62575471813
1.1313
Please enter a number n for creating an n-bit prime number for range 0...2^n: 20
pq 761609512621
1.5924

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...