Python 求包含指数和包含大量数的除法的余数模

Python 求包含指数和包含大量数的除法的余数模,python,algorithm,math,language-agnostic,Python,Algorithm,Math,Language Agnostic,我需要一个快速算法来评估以下内容 ((a^n-1)/(a-1)) % p a和n几乎相等,但小于10^6,p是一个固定素数(假设p=1000003)。我需要在1秒之内计算出来。我正在使用python。Wolfram Mathematica。使用以下代码需要35.2170000076秒 print (((10**6)**(10**6)-1)/((10**6)-1))%1000003 如果分母a-1不存在,我可以将幂分成更小的顺序,并使用关系a*b(mod c)=(a(mod c)*b(mod

我需要一个快速算法来评估以下内容

((a^n-1)/(a-1)) % p
a
n
几乎相等,但小于10^6,
p
是一个固定素数(假设
p=1000003
)。我需要在1秒之内计算出来。我正在使用python。Wolfram Mathematica。使用以下代码需要35.2170000076秒

print (((10**6)**(10**6)-1)/((10**6)-1))%1000003
如果分母
a-1
不存在,我可以将幂分成更小的顺序,并使用关系
a*b(mod c)=(a(mod c)*b(mod c))(mod c)
,但分母存在

如何使用快速算法对此进行评估?没有可用的numpy/scipy

更新::这是我最后的代码

def exp_div_mod(a, n, p):
    r = pow(a, n, p*(a-1)) - 1
    r = r - 1 if r == -1 else r
    return r/(a-1)
((a**n)-1)/(a-1))%p

可以重写为

((a**n)-1)%((a-1)*p))/(a-1)

本部分:

((a**n)-1%((a-1)*p))

可以通过计算以下各项来计算:

((a**n)%((a-1)*p))

然后再调整为-1

将a提高到N次方,将mod提高((a-1)*p)。这可以使用Python pow()函数完成。然后调整-1并除以a-1

使用pow()函数并传递一个模值比计算完整指数然后取模要快,因为模可以应用于计算的每个阶段的部分积,从而防止值变得过大(106到106的幂有600万个十进制数字,在每一步应用一个模,值的增长永远不必大于模的大小-在本例中约为13位)

代码:

输出:

444446
注:此方法仅在a、n和p为整数时有效。

可以乘以
p-1
。根据费马的小定理,可以得到xp-2·x≡ xp-1≡ 1(mod p)表示所有0pow函数:

(pow(a, n, p) - 1) * pow(a - 1, p - 2, p) % p
该算法具有时间复杂度(an−1) /(a)−1) 是从i=0到n的和−1.人工智能

计算后一个mod p很简单,基于以下内容:

设F(a,n)为∑(i=0..n-1){ai}如果n>0,否则为0

现在:

  • F(a,n)=a×F(a,n−1) +1

  • F(a,2n)=(a+1)×F(a2,n)

  • 第二个恒等式是分治递归

    由于这两个都只涉及加法和乘法,我们可以通过分配模运算来计算它们mod p,而不需要大于a×p的整数类型(见下面的代码)

    通过第一次递归,我们可以编写迭代解决方案:

    def sum_of_powers(a, n, p):
      sum = 0
      for i in range(n): sum = (a * sum + 1) % p
      return sum
    
    同样使用分治递归,我们得到了一些不太复杂的东西:

    def sum_of_powers(a, n, p):
      if n % 2 == 1:
        return (a * sum_of_powers(a, n-1, p) + 1) % p
      elif n > 0:
        return ((a + 1) * sum_of_powers(a * a % p, n // 2, p)) % p
      else:
        return 0
    


    第一个解在不到一秒钟的时间内返回,n==106。第二个解立即返回,即使n大到109。

    你可以用它来求逆
    1/(a-1)mod p
    ;然后应用您编写的最后一个标识。@hiroprotation我会尝试让您知道。该模块有一个可以计算模逆的函数:
    gmpy.divm
    “返回x,这样b*x==模m,或者如果不存在这样的值x,则引发ZeroDivisionError异常”使用
    a
    n
    p
    就像在OP中一样,
    (pow(a,n,p)-1)*gmpy.divm(1,a-1,p)%p
    返回444446。当然,如果你像samgak的回答那样做了一点代数,gmpy是不必要的。:
    r=r-1如果r=-1,那么更新中的r
    是错误的。很好!但是您不需要这个循环:内置Python
    pow
    函数将模数作为可选的第三个参数。所以你可以做
    m=p*(a-1);x=((pow(a,n,m)-1)%m)/(a-1)
    @NiklasB.:为什么它不起作用?请注意,
    ((a**n)-1)/(a-1)
    是一个整数(几何级数之和)。设
    q/b
    为整数,其中
    q/b=k*p+r
    。然后
    q=k*(p*b)+(r*b)
    @NiklasB。FWIW,我刚刚用
    (a**n-1)/(a-1))%p测试了samgak的算法,随机选择了10000个1返回(pow(a,n,m)-1)%m/(a-1)
    ,但这只是偏好的问题我想你是说
    a-1
    ,而不是
    p-1
    。嗨,我不明白你的第一种方法,你能为它写些数学题吗?我知道费马定理,但在它之后,你可以用模逆x^(-1)乘以一个数x除以模素数。通常你会使用扩展的欧几里德算法来计算逆,但在素数模的情况下,逆是x^(p-2),由于费马理论,我理解另一个答案,但我仍然不能从数学上得到它。由于费马,x是x^(p-2)的模逆,但这是怎么回事?遗憾的是,我们不能在这里使用乳胶,就像……你能不使用循环吗?在一个循环中求100万左右模幂的和不是很快。@PM2Ring有一个技巧,将奇数项和偶数项分开,然后重用偶数和来计算O(1)中奇数项的和,生成一个只使用O(logn)加法和multiplications@pm2ring:如果将^i%p起始时间与i=0相加,每个术语“仅”需要一个乘法和一个模(通过利用前一术语的值);你永远不需要计算幂。在现代硬件上,一百万次这样的计算可以在一秒钟内轻松完成。但NiklasB的建议是更公平的faster@NiklasB. 有趣!我想我看到了分而治之的方法在O(logn)中是如何工作的了。@rici相信我,在我把答案贴在这里之前,我试过这个方法。我考虑了Nicholas B提出的方法,但发现太多,无法编写代码。
    def sum_of_powers(a, n, p):
      if n % 2 == 1:
        return (a * sum_of_powers(a, n-1, p) + 1) % p
      elif n > 0:
        return ((a + 1) * sum_of_powers(a * a % p, n // 2, p)) % p
      else:
        return 0