Miller-Rabin 소수성 테스트 를 구현하려고했는데 왜 중형 숫자 (~ 7 자리)에 너무 오래 (> 20 초) 걸리는지 의아해했습니다. 결국 문제의 원인이되는 다음 코드 줄을 발견했습니다.
x = a**d % n
(단 a
, d
및 n
모든 유사하지만 동일하지 않은, 중간 번호는, **
누승 연산자이며, %
모듈러 연산자이다)
그런 다음 다음으로 바꾸려고 시도했습니다.
x = pow(a, d, n)
비교하면 거의 즉각적입니다.
컨텍스트를 위해 원래 기능은 다음과 같습니다.
from random import randint
def primalityTest(n, k):
if n < 2:
return False
if n % 2 == 0:
return False
s = 0
d = n - 1
while d % 2 == 0:
s += 1
d >>= 1
for i in range(k):
rand = randint(2, n - 2)
x = rand**d % n # offending line
if x == 1 or x == n - 1:
continue
for r in range(s):
toReturn = True
x = pow(x, 2, n)
if x == 1:
return False
if x == n - 1:
toReturn = False
break
if toReturn:
return False
return True
print(primalityTest(2700643,1))
시간 제한 계산의 예 :
from timeit import timeit
a = 2505626
d = 1520321
n = 2700643
def testA():
print(a**d % n)
def testB():
print(pow(a, d, n))
print("time: %(time)fs" % {"time":timeit("testA()", setup="from __main__ import testA", number=1)})
print("time: %(time)fs" % {"time":timeit("testB()", setup="from __main__ import testB", number=1)})
출력 (PyPy 1.9.0으로 실행) :
2642565
time: 23.785543s
2642565
time: 0.000030s
출력 (Python 3.3.0, 2.7.2로 실행하면 매우 유사한 시간이 반환 됨) :
2642565
time: 14.426975s
2642565
time: 0.000021s
그리고 관련 질문, 일반적으로 PyPy가 훨씬 빠를 때 PyPy보다 Python 2 또는 3으로 실행할 때이 계산이 거의 두 배 빠른 이유는 무엇입니까?
답변
모듈 식 지수 에 대한 Wikipedia 기사를 참조하십시오 . 기본적으로를 할 때 a**d % n
실제로를 계산해야하는데 a**d
이는 상당히 클 수 있습니다. 하지만 스스로 a**d % n
계산하지 않고도 계산할 a**d
수있는 방법 pow
이 있습니다. **
당신이 바로 계수를 취할려고하고 있다는 것을 알고 “미래를 볼”수 없기 때문에 작업자는이 작업을 수행 할 수 없습니다.
답변
BrenBarn이 주요 질문에 대답했습니다. 제쳐두고 :
일반적으로 PyPy가 훨씬 빠르지 만 Python 2 또는 3으로 실행할 때 PyPy보다 거의 두 배 빠른 이유는 무엇입니까?
PyPy의 성능 페이지 를 읽으면 이것이 바로 PyPy가 잘하지 못하는 종류입니다. 실제로 그들이 제공하는 첫 번째 예입니다.
나쁜 예에는 최적화 할 수없는 지원 코드에 의해 수행되는 긴 long으로 계산하는 것이 포함됩니다.
이론적으로는 거대한 지수화에 이어 mod를 모듈 식 지수화 (적어도 첫 번째 패스 이후)로 바꾸는 것은 JIT가 만들 수있는 변환이지만 PyPy의 JIT는 아닙니다.
참고로, 거대한 정수를 사용하여 계산을 수행해야하는 gmpy
경우, 주류 사용 외부의 경우에 CPython의 기본 구현보다 훨씬 빠를 수있는, 같은 타사 모듈을 살펴보고 싶을 수 있습니다. 편리하지 않은 대신 직접 작성해야하는 추가 기능을 제공합니다.
답변
모듈 식 지수화를 수행하는 지름길이 있습니다. 예를 들어 a**(2i) mod n
모든 i
from 1
to 에서 필요한 중간 결과를 log(d)
함께 곱 (mod n
) 할 수 있습니다. 3-argument와 같은 전용 모듈 식 지수 함수 pow()
는 모듈 식 산술을 수행하고 있음을 알고 있기 때문에 이러한 트릭을 활용할 수 있습니다. 파이썬 파서는 베어 표현식이 주어지면이를 인식 할 수 없으므로 a**d % n
전체 계산을 수행합니다 (훨씬 더 오래 걸립니다).
답변
방법 x = a**d % n
계산 올리이다 a
받는 d
전력, 다음으로 모듈로한다는 n
. 첫째, a
이 크면 큰 수를 생성 한 다음 잘립니다. 그러나 x = pow(a, d, n)
는 마지막 n
숫자 만 추적 되도록 최적화 될 가능성이 가장 높으며 , 이는 곱셈 모듈로 숫자를 계산하는 데 필요한 모든 것입니다.