我正在研究一种算法,并希望使我的代码更有效。我的代码使用简单的算术和比较语句。但是,我想替换if语句,因为它们可能很耗时。这段代码将运行在百万次,所以即使是最轻微的改进也是值得赞赏的。请回答!这里是代码 -
int_1024 sqcalc(int_1024 s,int_1024 f){
f=f*20;
s=s-81;
s=s-(f*9);
if(s>=0){
return 9;
}
s=s+f;
s=s+17;
if(s>=0){
return 8;
}
s=s+f;
s=s+15;
if(s>=0){
return 7;
}
s=s+f;
s=s+13;
if(s>=0){
return 6;
}
s=s+f;
s=s+11;
if(s>=0){
return 5;
}
s=s+f;
s=s+9;
if(s>=0){
return 4;
}
s=s+f;
s=s+7;
if(s>=0){
return 3;
}
s=s+f;
s=s+5;
if(s>=0){
return 2;
}
s=s+f;
s=s+3;
if(s>=0){
return 1;
}
s=s+f;
s=s+1;
if(s>=0){
return 0;
}
}
我希望更换支票,因为我认为'他们使算法变慢。有什么建议吗?int_1024是一个带有1000位的ttmath变量,所以保存它可能是个不错的选择?这么大的数字的除法或乘法可能会很慢,所以我尝试使用加法,但无济于事。请帮忙。
答案 0 :(得分:7)
我不知道它是否更快,但它更短(并且更容易分析)。
int k[] = { 17, 15, 13, 11, 9, 7, 5, 3, 1 };
int r = 0;
f *= 20;
s -= 81;
s -= f * 9;
while (s < 0) {
s += f;
s += k[r];
if (++r == 9) break;
}
if (s >= 0) return 9-r;
修改强>
实际上,最初的海报提出了一种聪明的方法来优化这个循环,方法是预先计算k
数组中的常量之和,并将s
与总和进行比较,而不是逐步将它们添加到s
。
修改强> 我遵循了moonshadow的分析技术,但得出了一个不同的等式。原始TeX格式替换为ASCII艺术(我试图让MathJax为我渲染TeX,但它不起作用):
S[0] = s >= 0 => 9 - 0
S[1] = S[0] + f + 19 - 2*1 >= 0 => 9 - 1
S[2] = S[1] + f + 19 - 2*2 >= 0 => 9 - 2
...
S[i] = S[i-1] + f + 19 - 2*i >= 0 => 9 - i
所以计算S[n]
:
S[n] = S[n-1] + f + 19 - 2n
.-- n
=> S[n] = s + > (f + 19 - 2*i)
`-- i=1 .-- n
=> S[n] = s + n(f + 19) - 2 > i
`-- i=1
=> S[n] = s + n(f + 19) - n(n+1)
2
=> S[n] = s + n(f + 18) - n
因此,不等式S[n] >= 0
是n
中的二次方程。假设s < 0
,我们希望n
成为二次方解的上限。
+-- --+
| _____________ |
| / 2 |
| f + 18 - . / (f + 18) + 4s |
| ` |
n = | --------------------------- |
| 2 |
所以例程看起来像:
f *= 180;
s -= 81;
s -= f;
if (s >= 0) return 9;
f /= 9;
f += 18;
s *= 4;
int1024_t ff = f;
ff *= f;
ff += s;
ff = ff.Sqrt();
f -= ff;
f += f.Mod2();
return 9 - f/2;
但是,我不确定在大整数对象上执行这些操作的代价是否值得实现以替换上面显示的简单循环。 (除非你期望扩展函数并且需要更长的循环。)
为了比循环更快,大整数平方根实现必须总是在4次迭代内收敛,以超过现有while循环的平均预期4.5次迭代。但是ttmath
实现似乎没有计算整数平方根。它似乎计算浮点平方根,然后舍入结果,我猜这将比循环慢得多。
答案 1 :(得分:6)
首先,我注意到如果最终if()
的条件为假,则返回值未定义。你可能想解决这个问题。
现在,该功能以
开头f=f*20;
s=s-81;
s=s-(f*9);
if(s>=0){
return 9;
}
其余的看起来非常重复。让我们看看我们是否可以使用那种重复。让我们建立一个不等式表 - s值与最终结果:
s + (f+17) >= 0: 8
s + (f+17) + (f+15) >= 0: 7
s + (f+17) + (f+15) + (f+13) >= 0: 6
.
.
s + (f+17) + (f+15) + (f+13) + ... + (f+1) >= 0: 0
因此,每一行测试以查看s + f +某个常数的某些倍数是否大于0.返回的值,常量和f的倍数看起来都相关。让我们尝试表达这种关系:
(s + ((9-n)*f) + (2*n)-1 >= 0)
让我们重新排列,以便n在一边。
(s + (9*f) - (n*f) + (2*n)-1 >= 0)
(s + (9*f) +1 >= (n*f) - (2*n))
(s + (9*f) +1 >= n*(f - 2))
n <= ((s + (9*f) +1) / (f - 2)
现在,该函数具有一系列不同输入的返回值。实际上,我们对0 {8范围内n
的值感兴趣:对于将产生n < 0
的输入,所提供的函数未定义(参见上文)。前言确保我们永远不会看到会产生n > 8
的输入。
所以我们可以说
int_1024 sqcalc(int_1024 s,int_1024 f){
f=f*20;
s=s-81;
s=s-(f*9);
if(s>=0){
return 9;
}
return (s + (9*f) +1) / (f - 2);
}
并且对于未定义结果的所有情况,行为应该与旧版本相同,而不需要大量的条件或循环。
准确性的证明是http://ideone.com/UzMZs。
答案 2 :(得分:0)
根据OP的评论,该函数试图找到满足不等式的所有值:
N * ((20 * F) + N) <= S
对于所有N,给定F和S.
使用代数,这出现在:
1) N^2 + 20Fn - S <= 0 (where N^2 is N*N or sqr(N))
OP应该使用F和N的一些常量并用代数求解(sp?)或在网上搜索“C ++ find root quadratic equation”。
选择一个功能,然后分析功能并在必要时进行优化。
答案 3 :(得分:0)
我尝试解决了二次方的问题,它使得函数的速度变得越来越慢。在@ user315052的回答之后,我制作了这段代码。
int_1024 sqcalc(int_1024 s,int_1024 f){
int k[] = { 0, 17, 32, 45, 56, 65, 72, 77, 80, 81 };
f=f*20;
s=((f*9)+81)-s;
int i=0;
while(s>k[i]){
s-=f;
i++;
}
return 9-i;
}
在这段代码中,不是减去一个数字然后与零进行比较,而是直接将它与数字进行比较。到目前为止,这会产生最快的结果。我可以做二分搜索....