对于大型输入而言,使该程序运行得更快会遇到问题

时间:2018-02-12 07:37:56

标签: python performance math time-complexity

def calculate(i,j,m,k,n):
    for v in range(1,n+1):
        ans = (i*k + j) % m
        k = ans
    return ans

该程序代表一个通用公式,其中x = (i * k + j) % m其中k是前一个答案的值。从某种意义上说,它基本上是x1 = (i * x0 + j) % mx2 = (i * x1 + j) % m,依此类推。我遇到的问题是计算大输入需要很长时间。

考虑到这一点,我正在考虑使用算术系列公式,例如:a + (n - 1) * d),但我不确定如何在诸如此类的程序中实现它。

1 个答案:

答案 0 :(得分:2)

x1 = (i * x0 + j)
x2 = (i * x1 + j) = i * i * x0 + i * j + j
x3 = i * i * i * x0 + i * i * j + i * j + j
xn = i^n * x0 + sum(i^t for t from 0 to n - 1) * j
   = i^n * x0 + (i^n - 1) / (i - 1) * j

找到最后一行with Wolfram Alpha

如果m是素数,则公式很好。 在这种情况下,您可以执行以prime为模的所有计算, 包括分裂,以保持数字小。 你只需要快速获得取幂i^n。 我建议你看一下https://en.wikipedia.org/wiki/Modular_exponentiation#Right-to-left_binary_method及其中的参考资料。 与循环的O( n )相比,这应该给你O(log( n ))时间复杂度。

如果m不是素数,则上述公式中的除法很烦人。但是你也可以通过平方计算总和来做类似于求幂的事情。观察

1 + i + i^2 + i^3 + i^4 + i^5 + i^6 + … + i^(2n+1) =
(1 + i) * (1 + i^2 + i^4 + i^6 + … + i^n)
1 + i + i^2 + i^3 + i^4 + i^5 + i^6 + … + i^(2n+2) =
1 + (i + i^2) * (1 + i^2 + i^4 + i^6 + … + i^n)

所以你可以在每一步的右括号中加一半的加数。现在没有除法,因此您可以在每次操作后执行模运算。 因此,您可以定义类似

的内容
def modpowsum(a, n, m):
  """(1 + a + a^2 + a^3 + ... + a^n) mod m"""
  if n == 0:
    return 1
  if n == 1:
    return (1 + a) % m
  if n % 2 == 1:
    return ((1 + a) * modpowsum((a * a) % m, (n - 1) // 2, m)) % m
  return (1 + ((a + a * a) % m) * modpowsum((a * a) % m, n // 2 - 1, m)) % m

整个计算可以在https://ideone.com/Xh0Fuf运行一些随机和一些不那么随机的测试用例来查看。