有没有什么方法可以优化乘法循环?

时间:2017-05-02 20:03:32

标签: algorithm math algebra

让我们说我必须重复将变量乘以常数的过程,并将结果模数为另一个常数 n 次以获得我想要的结果。

显而易见的解决方案是迭代 n 次,但耗时更长 n

代码示例:

const N = 1000000;

const A = 123;
const B = 456;

var c = 789;

for (var i = 0; i < n; i++)
{
    c = (c * a) % b;
}

log("Total: " + c);

是否有任何代数解决方案来优化此循环?

2 个答案:

答案 0 :(得分:4)

%有两个有用的属性:

1)(x % b) % b = x % b
2)(c*a) % b = ((c%b) * (a%b))%b

这意味着例如

(((c*a)%b)*a) % b = ((((c*a)%b)%b) * (a%b)) % b
                   = (((c*a) % b) * (a%b)) % b
                   = (c*a*a) % b
                   = (c*a^2) % b

因此,在您的情况下,您计算的最终c等同于

(c*a^n)%b

这可以使用exponentiation by squaring有效计算。

说明这种等同性:

def f(a,b,c,n):
    for i in range(n):
        c  = (c*a)%b
    return c

def g(a,b,c,n):
    return (c*pow(a,n,b)) % b

a = 123
b = 456
c = 789
n = 10**6

print(f(a,b,c,n),g(a,b,c,n)) #prints 261, 261

答案 1 :(得分:0)

首先,请注意c * A^n永远不是B = 456的精确倍数,因为前者总是奇数而后者总是偶数。您可以通过考虑所涉及数字的主要因素来概括这一点,并且看到cA因素的重复不会给出包含B的所有因子的内容。这意味着c永远不会因迭代乘法而变为0

c * a mod B = 456只有456个可能的值;因此,如果循环迭代456次,您将至少看到重复c的值。假设c时重复的c'的第一个值为i= i'。假设c'时首先看到i=i''。通过继续迭代乘法,我们希望再次看到c'

  • 我们在i''
  • 看到了它
  • 我们在i'
  • 看到了它
  • 我们应该在i' + (i' - i'')
  • 看到它
  • 我们也应该在i' + k(i' - i'')看到它

一旦你发现重复,你就会知道这种模式会永远重复。因此,您可以计算到达N所需的模式数量,以及您在i = N - 1处的重复模式中的偏移量,然后在不实际执行乘法的情况下知道答案。< / p>

一个更简单的例子:

A = 2
B = 3
C = 5

c[0] = 5
c[1] = 5 * 2 % 3 = 1
c[2] = 1 * 2 % 3 = 2
c[3] = 2 * 2 % 3 = 1 <= duplicate

i' = 3
i'' = 1
repeating pattern: 1, 2, 1

c[1+3k] = 1
c[2+3k] = 2
c[3+3k] = 1

10,000 = 1 + 3k for k = 3,333
c[10,000] = 1
c[10,001] = 2
c[10,002] = 1