[python] pow (a, d, n)이 a ** d % n보다 훨씬 빠른 이유는 무엇입니까?

Miller-Rabin 소수성 테스트 를 구현하려고했는데 왜 중형 숫자 (~ 7 자리)에 너무 오래 (> 20 초) 걸리는지 의아해했습니다. 결국 문제의 원인이되는 다음 코드 줄을 발견했습니다.

x = a**d % n

(단 a, dn모든 유사하지만 동일하지 않은, 중간 번호는, **누승 연산자이며, %모듈러 연산자이다)

그런 다음 다음으로 바꾸려고 시도했습니다.

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모든 ifrom 1to 에서 필요한 중간 결과를 log(d)함께 곱 (mod n) 할 수 있습니다. 3-argument와 같은 전용 모듈 식 지수 함수 pow()는 모듈 식 산술을 수행하고 있음을 알고 있기 때문에 이러한 트릭을 활용할 수 있습니다. 파이썬 파서는 베어 표현식이 주어지면이를 인식 할 수 없으므로 a**d % n전체 계산을 수행합니다 (훨씬 더 오래 걸립니다).


답변

방법 x = a**d % n계산 올리이다 a받는 d전력, 다음으로 모듈로한다는 n. 첫째, a이 크면 큰 수를 생성 한 다음 잘립니다. 그러나 x = pow(a, d, n)는 마지막 n숫자 만 추적 되도록 최적화 될 가능성이 가장 높으며 , 이는 곱셈 모듈로 숫자를 계산하는 데 필요한 모든 것입니다.


답변