将循环转换为数学方程式

时间:2015-10-08 08:01:56

标签: c++ performance loops time-complexity equation

我的某些程序必须具有严格的时间限制才能完成给定的任务。

如果我是正确的,将某些循环转换为数学方程应该会减少我的程序的时间复杂度,是吗?我可以在一次操作中得到循环找到的相同结果吗?

我已经检查了很多关于这个问题的其他解决方案,遗憾的是他们都专注于解决循环本身,而不是应该采取的一般步骤将循环转换为数学方程式,我无法理解得多。

我需要能够自己转换循环,而且我无法在互联网上的任何地方找到帮助解决这个问题。参考文献将不胜感激。

例如,在某些情况下,此循环需要7秒以上:

for (int j = 0; j < N * M; j++){
            S += V;
            V = (A * V + B) % (10007);
        }

这个也需要一秒多的时间:

for (int i = 1; i <= product; i++){
    if (product % i == 0)
        sum += i;
}

请注意我的问题不在于这两个循环,我需要知道如何处理任何可转换循环。答案也不仅限于方程式,任何性能提示都表示赞赏。

编辑:Here就是我的意思。

2 个答案:

答案 0 :(得分:2)

您确实可以使用mathematical induction来推断循环等效于简单公式。您链接的问题中的循环是系列的简单总和,易于推理。但是,并非所有循环都可以简化为公式。

使用归纳法推理任何一个循环都很困难。第一个具有余数运算,使输出周期性。由于它是周期性的,因此单个归纳步骤不适用于j的每个增量。证明循环存在一个简单的公式,更不用说找到那个公式,这超出了我的数学技能。

我认识到你的第二个循环中的情况。这是对i是否为product因子的测试。所以,基本上,你将所有因素加在一起。我可以直观地猜测没有简单的方法来找到一个简单的公式,因为我知道没有已知的快速算法进行整数分解(即找到整数的所有因子)。

比测试每个小于product的数字更快的算法,但对于数字,如int,它还不错。

作为优化,您可以将迭代次数减少一半,因为我们知道没有比这更大的数字(除了product本身)是一个因素。 1始终是一个因素,因此我们可以在初始化期间添加它。更进一步,我们可以添加对因子,并且只迭代直到产品的平方根:

int sum = 1 + product;
int root = sqrt(product);
for (int i = 2; i < root; i++){
    if (product % i == 0)
        sum += (i + product / i);
}
// add square root separately, because it doesn't have a pair
if (root*root == product)
    sum += root;

答案 1 :(得分:2)

我没有时间将解决方案完全扩展到代码中,但您会发现有用的想法。

First Loop

首先我将N*M更改为仅N,因为它会简化方程式的编写(然后您可以在最终的方程中替换回来找到正确的公式)。进入循环时,我还假设S等于0。我还将在Z / 10007Z领域工作(10007是素数)

   for (int j = 0; j < N; j++){
        S += V;
        V = (A * V + B) % (10007);
    }

基本上,您有一系列数字v_i和一个和S_i定义如下:

v_0     = ? // not important in the following equations
S_0     = 0
v_{i+1} = (A*v_i+B)
S_{i+1} = S_i + v_{i}

您可以将v_i的重复公式重写为矩阵运算:

|v_{i+1}|   | A  B | |v_i|
|       | = |      | |   |
|      1|   | 0  1 | |  1|

让我们调用M矩阵。您现在可以通过以下公式轻松计算任何值v_i

|v_i|    i |v_0|
|   | = M  |   |
|  1|      |  1|

然后通过将i从0加到N得到:

|S|   / N    i \ |v_0|
| | = |SUM  M  | |   |
|1|   \i=0     / |  1|

让我们调用矩阵M的幂的总和:Q

您可以轻松证明M的第i个力量是:

 i  | A^i  B(A^i+A^(i-1)+...+1) |
M = |                           |
    | 0                       1 |

原来是:

 i  | A^i  B(1-A^(i+1))/(1-A) |
M = |                         |
    | 0                     1 |

(见:https://en.wikipedia.org/wiki/Geometric_series#Sum

因此我们可以将Q重写为:

    | (1-A^(N+1))/(1-A)  B*SUM(i=1,N, B(1-A^(i+1))/(1-A) ) |
Q = |                                                      |
    | 0                                                  1 |

最终我们得到:

     1   | 1-A^(N+1)  B*( N - (1-A^(N+1))/(1-A) ) |
Q = ---  |                                        |
    1-A  | 0                                    1 |

您可以在A^(N+1)中轻松计算O(log(N))

计算1/(1-A)是根据费马的小定理计算(1-A)^(10007-1-1)完成的。

如果事先知道A,您甚至可以预先计算它。

显然,一切都是在数字模数10007的领域完成的,如前所述。

第二次循环

基本上你计算一个数的除数。我不知道有什么更好的方法。 但是如果您必须为许多连续的数字执行此操作,则可能会进行优化。