Python中的模块化乘法逆函数

时间:2011-01-25 20:50:20

标签: python algorithm

某些标准Python模块是否包含计算数字modular multiplicative inverse的函数,即y = invmod(x, p)的数字x*y == 1 (mod p)?谷歌似乎没有给出任何好的暗示。

当然,人们可以想出extended Euclidean algorithm的自酿10线,但为什么要重新发明轮子。

例如,Java的BigInteger具有modInverse方法。 Python没有类似的东西吗?

14 个答案:

答案 0 :(得分:103)

也许有人会觉得这很有用(来自wikibooks):

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 :(得分:49)

如果你的模数是素数(你称之为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

答案 2 :(得分:19)

您可能还想查看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 :(得分:5)

这是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 A中没有乘法逆,则会返回n

用法:

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

解决方案使用Extended Euclidean Algorithm

答案 4 :(得分:4)

Sympy,一个用于符号数学的python模块,如果你不想实现自己的(或者如果你已经使用Sympy),它有一个内置的模块化反函数:< / p>

<xsl:stylesheet
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    version="1.0">

    <xsl:param name="start-id">7</xsl:param>

    <xsl:key name="id" match="item" use="@id"/>
    <xsl:key name="child" match="item" use="parent/@idref"/>

    <xsl:template name="count-descendants">
        <xsl:param name="descendants" select="/.."/>
        <xsl:param name="level"/>
        <xsl:variable name="children" select="key('child', $level/@id)"/>
        <xsl:choose>
            <xsl:when test="not($children)">
                <xsl:value-of select="count($descendants)"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:call-template name="count-descendants">
                    <xsl:with-param name="descendants" select="$descendants | $children"/>
                    <xsl:with-param name="level" select="$children"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

    <xsl:template match="/">
        <xsl:variable name="start-item" select="key('id', $start-id)"/>
        <xsl:call-template name="count-descendants">
            <xsl:with-param name="level" select="$start-item"/>
        </xsl:call-template>
    </xsl:template>

</xsl:stylesheet>

这似乎没有在Sympy网站上记录,但这里是文档字符串:Sympy mod_inverse docstring on Github

答案 5 :(得分: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

答案 6 :(得分:2)

上面的代码不会在python3中运行,并且与GCD变体相比效率较低。但是,此代码非常透明。它引发了我创建一个更紧凑的版本:

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

答案 7 :(得分:2)

从3.8开始,python的pow()函数可以采用模数和负整数。参见here

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

答案 8 :(得分: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

答案 9 :(得分:0)

好吧,我在python中没有函数,但我在C中有一个函数可以很容易地转换为python,在下面的c函数中,扩展的欧几里得算法用于计算逆mod。

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 program to find Modular Multiplicative Inverse of two Relatively Prime Numbers

答案 10 :(得分:0)

我尝试使用此线程提供不同的解决方案,最后我使用以下解决方案:

def egcd(self, 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(self, a, m):
    g, x, y = self.egcd(a, m)
    if g != 1:
        raise ValueError('modinv for {} does not exist'.format(a))
    return x % m

Modular_inverse in Python

答案 11 :(得分:0)

这是一个简洁的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,经过精简后仅返回单个感兴趣的系数。

答案 12 :(得分:0)

来自cpython实现source code

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之前将其添加为负。

答案 13 :(得分:-1)

上述许多链接在2017年1月23日都被破坏了。 我找到了这个实现: https://courses.csail.mit.edu/6.857/2016/files/ffield.py