Python의 모듈 식 곱셈 역함수


110

일부 표준 파이썬은 계산하는 함수를 포함하는 모듈 않는 모듈 역수 수의, 즉 다수의 y = invmod(x, p)그러한를 x*y == 1 (mod p)? Google은 이에 대해 좋은 힌트를주지 않는 것 같습니다.

물론, 확장 된 유클리드 알고리즘 의 10 줄짜리 집에서 만든 10 줄짜리 알고리즘을 생각해 낼 수 있지만 왜 바퀴를 다시 발명해야할까요?

예를 들어 Java의 BigIntegerhas modInverse메소드. 파이썬에도 비슷한 것이 없습니까?


18
Python 3.8 (올해 말 출시 예정)에서는이를 위해 내장 pow함수 를 사용할 수 있습니다 y = pow(x, -1, p). bugs.python.org/issue36027을 참조 하세요 . 표준 라이브러리에 나타나는 솔루션에 대한 질문으로부터 8.5 년이 걸렸습니다!
Mark Dickinson

4
@MarkDickinson은 ey가이 매우 유용한 기능 향상의 저자임을 언급하는 것을 겸손하게 무시한 것을 보았습니다. 이 작업에 감사드립니다, Mark, 멋지네요!
Don Hatch

답변:


128

아마도 누군가가 유용하다고 생각할 것입니다 ( 위키 북에서 ) :

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

1
이 알고리즘을 사용하여 음수에 문제가있었습니다. modinv (-3, 11)가 작동하지 않았습니다. 나는 egcd를이 pdf의 2 페이지에있는 구현으로 대체하여 고쳤습니다 : anh.cs.luc.edu/331/notes/xgcd.pdf 도움이 되길 바랍니다 !
Qaz

@Qaz 또한 -3 모듈로 11을 줄여 양수로 만들 수도 있습니다.이 경우 modinv (-3, 11) == modinv (-3 + 11, 11) == modinv (8, 11). 그것은 아마도 PDF의 알고리즘이 어떤 시점에서 일어나는 일일 것입니다.
Thomas

1
을 사용하는 경우 트릭 sympyx, _, g = sympy.numbers.igcdex(a, m)수행하십시오.
Lynn

59

모듈러스가 소수 (당신은 그것을라고 부른다 p)라면 간단히 계산할 수 있습니다 :

y = x**(p-2) mod p  # Pseudocode

또는 적절한 Python에서 :

y = pow(x, p-2, p)

다음은 Python에서 수 이론 기능을 구현 한 사람입니다. http://www.math.umbc.edu/~campbell/Computers/Python/numbthy.html

다음은 프롬프트에서 수행되는 예입니다.

m = 1000000007
x = 1234567
y = pow(x,m-2,m)
y
989145189L
x*y
1221166008548163L
x*y % m
1L

1
순진한 지수는 1000000007과 같이 p의 합리적으로 큰 값에 대한 시간 (및 메모리) 제한 때문에 옵션이 아닙니다.
dorserg

16
모듈 식 지수화는 최대 N * 2 곱셈으로 수행됩니다. 여기서 N은 지수의 비트 수입니다. 2 ** 63-1의 모듈러스를 사용하면 역수를 프롬프트에서 계산할 수 있으며 즉시 결과를 반환합니다.
phkahler 2011 년

3
대단해. 저는 빠른 지수화를 알고 있습니다. pow () 함수가 모듈 식 지수화로 바꾸는 세 번째 인수를 취할 수 있다는 사실을 알지 못했습니다.
dorserg

5
그래서 파이썬을 사용하고 있습니까? 굉장하기 때문에 :-)
phkahler 2011 년

2
그런데 이것은 Fermat에서 작은 정리 pow (x, m-1, m)가 1이어야하기 때문에 작동합니다. 따라서 (pow (x, m-2, m) * x) % m == 1. 그래서 pow (x, m-2, m)은 x (mod m)의 역입니다.
Piotr Dabkowski

21

gmpy 모듈 을 살펴볼 수도 있습니다 . Python과 GMP 다중 정밀도 라이브러리 간의 인터페이스입니다. gmpy는 필요한 작업을 정확히 수행하는 반전 함수를 제공합니다.

>>> import gmpy
>>> gmpy.invert(1234567, 1000000007)
mpz(989145189)

업데이트 된 답변

@hyh에서 언급했듯이 gmpy.invert()는 역이 존재하지 않으면 0을 반환합니다. 그것은 GMP의 mpz_invert()기능 동작과 일치합니다 . gmpy.divm(a, b, m)에 대한 일반적인 솔루션을 제공합니다 a=bx (mod m).

>>> gmpy.divm(1, 1234567, 1000000007)
mpz(989145189)
>>> gmpy.divm(1, 0, 5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: not invertible
>>> gmpy.divm(1, 4, 8)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: not invertible
>>> gmpy.divm(1, 4, 9)
mpz(7)

divm()gcd(b,m) == 1곱셈 역이 존재하지 않을 때 솔루션을 반환하고 예외를 발생시킵니다.

면책 조항 : 저는 gmpy 라이브러리의 현재 관리자입니다.

업데이트 된 답변 2

gmpy2는 이제 역이 존재하지 않을 때 예외를 올바르게 발생시킵니다.

>>> import gmpy2

>>> gmpy2.invert(0,5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: invert() no inverse exists

gmpy.invert(0,5) = mpz(0)오류를 발생시키는 대신 발견 할 때까지 멋지다 ...
h__

@hyh gmpy의 홈페이지에서이 문제를보고 할 수 있습니까? 문제가보고되면 항상 감사합니다.
casevh apr

BTW,이 gmpy패키지에 모듈 식 곱셈이 있습니까? (즉, 값은 같지만 (a * b)% p? 보다 빠른 일부 함수 )
h__

이전에 제안 된 적이 있으며 다른 방법으로 실험하고 있습니다. (a * b) % p함수에서 계산하는 가장 간단한 방법은 (a * b) % pPython에서 평가하는 것보다 빠르지 않습니다 . 함수 호출에 대한 오버 헤드는 식을 평가하는 비용보다 큽니다. 자세한 내용은 code.google.com/p/gmpy/issues/detail?id=61 을 참조하세요.
casevh

2
좋은 점은 이것이 non-prime modulii에서도 작동한다는 것입니다.
synecdoche 2013 년

13

3.8부터 pythons pow () 함수는 모듈러스와 음의 정수를 취할 수 있습니다. 를 참조하십시오 여기 . 그것을 사용하는 방법에 대한 그들의 경우는

>>> pow(38, -1, 97)
23
>>> 23 * 38 % 97 == 1
True


6

심볼릭 수학을위한 파이썬 모듈 인 Sympy 는 자체적으로 구현하고 싶지 않은 경우 (또는 이미 Sympy를 사용중인 경우) 내장 모듈 식 역함수를 가지고 있습니다.

from sympy import mod_inverse

mod_inverse(11, 35) # returns 16
mod_inverse(15, 35) # raises ValueError: 'inverse of 15 (mod 35) does not exist'

이것은 Sympy 웹 사이트에 문서화되지 않은 것 같지만 여기에 독 스트링이 있습니다 : Github의 Sympy mod_inverse docstring


2

여기 내 코드가 있습니다. 엉성 할 수 있지만 어쨌든 저에게는 작동하는 것 같습니다.

# a is the number you want the inverse for
# b is the modulus

def mod_inverse(a, b):
    r = -1
    B = b
    A = a
    eq_set = []
    full_set = []
    mod_set = []

    #euclid's algorithm
    while r!=1 and r!=0:
        r = b%a
        q = b//a
        eq_set = [r, b, a, q*-1]
        b = a
        a = r
        full_set.append(eq_set)

    for i in range(0, 4):
        mod_set.append(full_set[-1][i])

    mod_set.insert(2, 1)
    counter = 0

    #extended euclid's algorithm
    for i in range(1, len(full_set)):
        if counter%2 == 0:
            mod_set[2] = full_set[-1*(i+1)][3]*mod_set[4]+mod_set[2]
            mod_set[3] = full_set[-1*(i+1)][1]

        elif counter%2 != 0:
            mod_set[4] = full_set[-1*(i+1)][3]*mod_set[2]+mod_set[4]
            mod_set[1] = full_set[-1*(i+1)][1]

        counter += 1

    if mod_set[3] == B:
        return mod_set[2]%B
    return mod_set[4]%B

2

위의 코드는 python3에서 실행되지 않으며 GCD 변형에 비해 효율성이 떨어집니다. 그러나이 코드는 매우 투명합니다. 더 컴팩트 한 버전을 만들게되었습니다.

def imod(a, n):
 c = 1
 while (c % a > 0):
     c += n
 return c // a

1
아이들에게 설명해도 괜찮습니다 n == 7. 그러나 그렇지 않으면이 "알고리즘"의 해당 관하여 :for i in range(2, n): if i * a % n == 1: return i
토마스 Gandor

2

다음은 외부 라이브러리를 사용하지 않고이를 수행하는 간결한 1 줄입니다.

# Given 0<a<b, returns the unique c such that 0<c<b and a*c == gcd(a,b) (mod b).
# In particular, if a,b are relatively prime, returns the inverse of a modulo b.
def invmod(a,b): return 0 if a==0 else 1 if b%a==0 else b - invmod(b%a,a)*b//a

이것은 단일 관심 계수 만 반환하도록 간소화 된 egcd 일뿐입니다.


1

모듈 식 곱셈 역수를 알아 내려면 다음과 같이 확장 유클리드 알고리즘을 사용하는 것이 좋습니다.

def multiplicative_inverse(a, b):
    origA = a
    X = 0
    prevX = 1
    Y = 1
    prevY = 0
    while b != 0:
        temp = b
        quotient = a/b
        b = a%b
        a = temp
        temp = X
        a = prevX - quotient * X
        prevX = temp
        temp = Y
        Y = prevY - quotient * Y
        prevY = temp

    return origA + prevY

이 코드에 버그가있는 것 같습니다 : a = prevX - quotient * X이어야 X = prevX - quotient * X하고을 반환해야합니다 prevX. FWIW,이 구현은 Märt Bakhoff의 답변에 대한 주석의 Qaz 링크 와 유사합니다 .
PM 2Ring

1

이 스레드에서 다른 솔루션을 시도하고 결국 다음을 사용합니다.

def egcd(a, b):
    lastremainder, remainder = abs(a), abs(b)
    x, lastx, y, lasty = 0, 1, 1, 0
    while remainder:
        lastremainder, (quotient, remainder) = remainder, divmod(lastremainder, remainder)
        x, lastx = lastx - quotient*x, x
        y, lasty = lasty - quotient*y, y
    return lastremainder, lastx * (-1 if a < 0 else 1), lasty * (-1 if b < 0 else 1)


def modinv(a, m):
    g, x, y = self.egcd(a, m)
    if g != 1:
        raise ValueError('modinv for {} does not exist'.format(a))
    return x % m

Python의 Modular_inverse


1
이 코드는 유효하지 않습니다. returnegcd의 의도가 잘못되었습니다
ph4r05

0

글쎄, 나는 파이썬에 함수가 없지만 파이썬으로 쉽게 변환 할 수있는 C 함수가 있습니다. 아래 c 함수 확장 유클리드 알고리즘은 역 모드를 계산하는 데 사용됩니다.

int imod(int a,int n){
int c,i=1;
while(1){
    c = n * i + 1;
    if(c%a==0){
        c = c/a;
        break;
    }
    i++;
}
return c;}

Python 함수

def imod(a,n):
  i=1
  while True:
    c = n * i + 1;
    if(c%a==0):
      c = c/a
      break;
    i = i+1

  return c

위의 C 함수에 대한 참조 는 두 개의 상대적 소수의 모듈 식 곱셈 역을 찾기 위해 다음 링크 C 프로그램 에서 가져옵니다.


0

cpython 구현 소스 코드에서 :

def invmod(a, n):
    b, c = 1, 0
    while n:
        q, r = divmod(a, n)
        a, b, c, n = n, c, b - q*c, r
    # at this point a is the gcd of the original inputs
    if a == 1:
        return b
    raise ValueError("Not invertible")

이 코드 위의 주석에 따르면 작은 음수 값을 반환 할 수 있으므로 음수인지 확인하고 b를 반환하기 전에 음수 일 때 n을 추가 할 수 있습니다.


"그러므로 잠재적으로 음수인지 확인하고 b를 반환하기 전에 음수 일 때 n을 추가 할 수 있습니다." 불행히도 그 시점에서 n은 0입니다. (n의 원래 값을 저장하고 사용해야합니다.)
Don Hatch

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.