让我们说我必须重复将变量乘以常数的过程,并将结果模数为另一个常数 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);
是否有任何代数解决方案来优化此循环?
答案 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
的精确倍数,因为前者总是奇数而后者总是偶数。您可以通过考虑所涉及数字的主要因素来概括这一点,并且看到c
和A
因素的重复不会给出包含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