[python] Python의 모듈 식 곱셈 역함수

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

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

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



답변

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

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


답변

모듈러스가 소수 (당신은 그것을라고 부른다 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


답변

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


답변

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

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


답변

다음은 CodeFights에 대한 한 줄짜리입니다 . 가장 짧은 솔루션 중 하나입니다.

MMI = lambda A, n,s=1,t=0,N=0: (n < 2 and t%N or MMI(n, A%n, t, s-A//n*t, N or n),-1)[n<1]

에 곱셈 역이 없으면 반환 -1됩니다 .An

용법:

MMI(23, 99) # returns 56
MMI(18, 24) # return -1

이 솔루션은 확장 유클리드 알고리즘을 사용합니다 .


답변

심볼릭 수학을위한 파이썬 모듈 인 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


답변

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

# 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