Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/12.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python中的模乘反函数_Python_Algorithm - Fatal编程技术网

Python中的模乘反函数

Python中的模乘反函数,python,algorithm,Python,Algorithm,一些标准Python模块是否包含一个函数来计算一个数字,即一个数字y=invmod(x,p),从而x*y==1(mod p)?谷歌似乎没有就此给出任何好的暗示 当然,你可以自制10班轮的啤酒,但为什么要重新发明轮子呢 例如,Java的biginger具有modInverse方法。Python难道没有类似的东西吗?如果模是素数(称之为p),则可以简单地计算: y = x**(p-2) mod p # Pseudocode 或者在Python中: y = pow(x, p-2, p) 下面是一

一些标准Python模块是否包含一个函数来计算一个数字,即一个数字
y=invmod(x,p)
,从而
x*y==1(mod p)
?谷歌似乎没有就此给出任何好的暗示

当然,你可以自制10班轮的啤酒,但为什么要重新发明轮子呢

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

如果模是素数(称之为
p
),则可以简单地计算:

y = x**(p-2) mod p  # Pseudocode
或者在Python中:

y = pow(x, p-2, p)
下面是一位在Python中实现了一些数论功能的人:

下面是一个在提示下完成的示例:

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

您可能还想看看这个模块。它是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)
的一般解决方案


要计算模乘逆,我建议使用扩展的欧几里德算法,如下所示:

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
Python 3.8+ Python3.7及更早版本 也许有人会发现这很有用(来自):


这是我的代码,它可能是草率的,但它似乎为我无论如何工作

# 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
这是一张单程票;这是最短的解决方案之一:

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]

这个解决方案使用了。

好吧,我没有python中的函数,但是我有一个C中的函数,您可以轻松地将其转换为python,在下面的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函数的引用来自以下链接

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

def imod(a, n):
 c = 1
 while (c % a > 0):
     c += n
 return c // a
,一个用于符号数学的python模块,如果您不想实现自己的(或者如果您已经在使用Symphy),它有一个内置的模块化逆函数:


Symphy网站上似乎没有记录这一点,但这里有一个文档字符串:

我尝试了来自此线程的不同解决方案,最后我使用了此解决方案:

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
def egcd(a,b):
最后余数,余数=abs(a),abs(b)
x、 lastx,y,lasty=0,1,1,0
其余的:
最后余数,(商,余数)=余数,divmod(最后余数,余数)
x、 lastx=lastx-商*x,x
y、 lasty=lasty-商*y,y
返回last余数、lastx*(-1,如果a<0,则返回1),lasty*(-1,如果b<0,则返回1)
def modinv(a,m):
g、 x,y=self.egcd(a,m)
如果g!=1:
raise VALUERROR({}的modinv不存在。格式(a))
返回x%m

这里有一个简洁的单行程序,它不使用任何外部库

# 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

cpython实现中给定的0:


根据这段代码上面的注释,它可以返回小的负值,因此您可以在返回b之前检查是否为负值,并在为负值时添加n。

从3.8版开始,pythons pow()函数可以采用模数和负整数。看见他们如何使用它的理由是

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

由于时间(和内存)限制,对于任何合理的大p值(比如1000000007),朴素求幂不是一个选项。模幂运算最多使用N*2次乘法,其中N是指数中的位数。使用2**63-1的模,可以在提示下计算逆,并立即返回结果。哇,太棒了。我知道快速求幂,我只是不知道pow()函数可以接受第三个参数,将其转换为模块求幂。这就是为什么要使用Python,对吗?因为它很棒:-)顺便说一句,这是因为根据费马小定理,pow(x,m-1,m)必须是1。因此(功率(x,m-2,m)*x)%m==1。所以pow(x,m-2,m)是x(mod m)的倒数。这很酷,直到我发现
gmpy.invert(0,5)=mpz(0)
而不是引起错误…@hyh你能在gmpy的主页上报告这个问题吗?如果报告了问题,我们将不胜感激。顺便问一下,这个
gmpy
包中有模乘吗?(即,某些函数具有相同的值,但比
(a*b)%p
?)更快)它以前已经提出过,我正在试验不同的方法。在函数中只计算
(a*b)%p
的最简单方法并不比在Python中只计算
(a*b)%p
快。函数调用的开销大于计算表达式的开销。请参阅以了解更多详细信息。最棒的是,这也适用于非素数模II。我在使用此算法时遇到了负数问题。modinv(-3,11)不起作用。我用本pdf第二页的实现替换了egcd:希望对您有所帮助@Qaz您也可以将-3模11减小为正,在本例中,modinv(-3,11)==modinv(-3+11,11)==modinv(8,11)。这可能就是PDF中的算法在某个时刻所做的。如果您碰巧使用的是
sympy
,那么
x,u,g=sympy.numbers.igcdex(a,m)
成功了。我喜欢它被烘焙到3.8+语言中,这段代码中似乎有一个bug:
a=prevX-quotine*X
应该是
X=prevX-quotine*X
,它应该返回
prevX
。FWIW,此实现类似于对Märt Bakhoff答案的注释中的实现。此代码无效return
在Python3.8(将于今年晚些时候发布)中以错误的方式被索引,您将能够使用内置的
pow
函数来实现这一点:
y=pow(x,-1,p)
。看见从这个问题开始只花了8.5年
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
def imod(a, n):
 c = 1
 while (c % a > 0):
     c += n
 return c // a
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'
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
# 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
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")
>>> pow(38, -1, 97)
23
>>> 23 * 38 % 97 == 1
True