以下代码:
s = 0 ;
for(i=m ; i<=(2*n-1) ; i+=m) {
if(i<=n+1){
s+=(i-1)/2 ;
}
else{
s+=(2*n-i+1)/2 ;
}
}
我想从O(n)
更改代码的复杂性
到O(1)
。所以我想消除for循环。但是作为
总和s
存储(i-1)/2
或(2*n-i+1)/2
等值,因此消除循环涉及繁琐计算每个(i-1)/2
或(2*n-i+1)/2
的最低值。我这样做变得非常困难,因为我可能在楼层总和中得出了错误的公式。你可以帮助我改变从O(n)
到O(1)
的复杂性。或者请帮助我这个楼层总结。有没有其他方法可以降低复杂性?如果是的话......那怎么样?
答案 0 :(得分:2)
我的方法是首先编写表征测试,断言为m
和n
的不同值生成的值,然后开始重构。
你的主循环基于中途(if(i<=n+1)
选择)的逻辑变化,所以我首先将它分成两个循环。
然后,在每个结果循环中,计算主要取决于i
是偶数还是奇数。将每个分成两个分开的循环,并且可以更容易理解地板计算。或者,您可能会看到重复值的模式,使您可以以不同的方式简化这些循环。
每个结果循环可能类似于算术级数的总和,因此您可能会发现它们可以被封闭形式的计算所取代,而根本不需要循环。
当您沿着这条路走时,您也可能会重构以将部分计算提取到函数中。在提取它们时为它们写出特征化测试。
继续运行所有测试,你可能会将其减少到一个简单计算的总和,然后可以通过普通的算术进一步减少。
答案 1 :(得分:2)
Don Roby说,对你的问题有一个简单的算术解决方案。让我告诉你如何为i的第一个值做这件事。
*编辑2:较低部分的代码*
for(int i=m ; i<= n+1 ; i+=m)//old computation
s+=(i-1)/2 ;
int a = (n+1)/m; // maximum value of i
int b = (a*(a+1))/2; //
int v = 0;
int p;
if(m % 2 == 0){
p = m/2;
v = b*p-a; // this term is always here
}
else{
p = (m - 1)/2;
int sum1 = ((a/2)*(a/2 +1))/2;
int sum2 = (((a-1)/2)*((a-1)/2 +1))/2;
v = b*p -a ;// this term is always here
v+= sum1 + a/2; //sum( 1 <= j <= a )(j-1), j pair
v+= sum2; //sum( 1 <= j <= a )(j-1), j impair
}
System.out.println( " Are both result equals ? "+ (s == v));
我如何想出来?
for(i=m ; i<= n+1 ; i+=m)
s+=(i-1)/2 ;
我做了一个改变
for(j=1 ; j*m <= n-1 ; j++)
s+=(j*m-1)/2 ;
我摆出a=Math.floor(n+1/m)
。有3例:
m是对,然后循环的内部是s+= p*j
。结果是
b(a*(a+1))/2 -a
m有损,迭代器j是对
m受损并且迭代器j受损
当m受损时,你可以写m = 2p + 1
并且循环内部变为
s+= p*j + (j-1)/2
p*j
与之前相同,现在你需要通过假设j总是对或j总是损害并对两个值求和来打破除法。
您需要计算的下一个循环是
for(int i=a+1 ; i<= (2*n-1) ; i+=m)// a is (n+1)/m
s+=(2*n-i+1)/2;
与
相同 for(int i=1 ; i<= (2*n-1)-a ; i+=m)
s+= (2n-a)/2 - (i-1)/2;
这个循环类似于第一个循环,因此没有太多工作要做...... 确实这很乏味......