通常可以将求和转换为封闭形式的解决方案。
例如
for (int i=0;i<n;i++)
result += i;
相当于result += max(0, n * ( n - 1) / 2)
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
result += i;
相当于result += max(0, m * n * ( n - 1) / 2)
for (int i=0;i<n;i++)
for (int j=0;j<m;j++)
if (i < j)
result += i;
相当于
for (int i=0;i<n;i++)
for (int j=i+1;j<m;j++)
result += i;
因此相当于
for (int i=0;i<n;i++)
if (i+1 < m)
result += i * (m-i);
因此相当于
for (int i=0;i<min(n,m-1);i++)
result += m * i - i * i;
因此最终等同于result += max(0, m * min(n,m-1) * (min(n,m-1) - 1) / 2 - (min(n,m-1) - 1) * ((min(n,m-1) - 1) + 1) * (2 * (min(n,m-1) - 1) + 1) / 6)
或者更容易写成result += max(0, m <= n ? m * (m-1) * ((m-1) - 1) / 2 - ((m-1) - 1) * (((m-1) - 1) + 1) * (2 * ((m-1) - 1) + 1) / 6) : m * n * (n - 1) / 2 - (n - 1) * ((n - 1) + 1) * (2 * (n - 1) + 1) / 6)
何时可以转换为封闭式解决方案?
似乎,如果只允许条件,加法和乘法(即多项式),则始终可以从最内圈开始,跟踪允许的最小值/最大值和添加的多项式的范围。
但是,手动执行此转换非常容易出错并且耗费时间,因为范围在每个循环中被分割并且呈指数增长。
是否有工具可以从迭代版本自动生成封闭形式的解决方案?
当允许分裂时,它变得多难?
for (int i=2;i<n;i++)
if (i % 2 == 0)
result += 1;
与for (int i=2;i<n;i+=2) result += 1;
相当容易,相当于max(0, (n/2) * (n/2 + 1) )
另一方面
for (int i=2;i<n;i++)
if (n % i == 0)
result += 1;
似乎很难改变。
答案 0 :(得分:1)
虽然有许多总和的封闭形式表达式,但也有许多其他的,没有人能够找到一个,如谐波数(注意这些总和的数量取决于你允许的功能)封闭形式)。
看来,如果只允许条件,加法和乘法(即多项式),总是可以从最里面的循环开始,跟踪允许的最小值/最大值和添加的多项式的范围。
你几乎是对的,如果条件只影响求和的参数(第一个或最后一个术语,或可能的步长,如if (i % 2 == 0)
),那么总会有一个封闭的形式,可以表示为有理函数。除其他表达式外,这些表达式可以使用例如finite calculus来计算。
对于更广泛的表达式,您可以使用generating functions(请参阅第17.2.2节以获得非常温和的介绍)。这些可以在很大程度上用于计算某些表达式的闭合形式(总和或重复关系)。
当允许分裂时,它变得多难?
正如我所说,if (i % 2 == 0)
相当容易,因为它只是增加总和的步长,例如
for (int i = 0; i <= n; i++)
if (i % 2 == 0)
result += i;
有效地成为
for (int i = 0; i <= n/2; i++)
result += 2*i;
非常适合n
的封闭形式。其他条件,例如您提供的if (n % i == 0)
,将会更加艰难。例如,考虑非常相似的表达式,它计算除数的总和
for (int i = 1; i < n; i++)
if (n % i == 0)
result += i;
如果它允许一个封闭的表格表达式,那么这很容易被用来用两个素数除数来计算数字,这通常被认为很难。