【问题标题】:Is there any way of optimizing a multiplication loop?有什么方法可以优化乘法循环吗?
【发布时间】:2017-05-02 20:03:32
【问题描述】:

假设我必须重复将一个变量乘以一个常数并将结果乘以另一个常数的过程,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);

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

【问题讨论】:

  • 迭代乘法有数学抽象吗?那么AB之间存在特殊关系:如果A % B == 0
  • 您可以预先计算或缓存c = 0 .. b-1 的值并将它们存储在look-up table 中。在循环内,c 总是被模运算截断到这个范围内。
  • 是c是周期性的,最大周期长度是b。
  • @PaulHankin 我最初倾向于同意你的观点,即这是重复的,但是当我看到两个高级用户给出的答案似乎是基于假设这不仅仅是伪装的简单模幂运算我认为等效性不是很明显。

标签: algorithm math algebra


【解决方案1】:

% 有两个有用的属性:

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

【讨论】:

    【解决方案2】:

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

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

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

    一旦您检测到重复,您就知道该模式将永远重复。因此,您可以计算出需要多少模式才能达到 N,以及重复模式中 i = N - 1 的偏移量,然后您无需实际执行乘法即可知道答案。

    一个更简单的例子:

    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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 2016-06-12
      • 2020-03-06
      • 1970-01-01
      • 2021-03-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多