c++中if语句的替代
Posted
技术标签:
【中文标题】c++中if语句的替代【英文标题】:alternative to if statement in c++ 【发布时间】:2012-06-08 16:03:04 【问题描述】:我正在研究一种算法,并希望使我的代码更高效。我的代码使用简单的算术和比较语句。但是,我想替换 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;
我希望替换 if 检查,因为我“认为”它们会使算法变慢。有什么建议吗?int_1024 是一个具有 1000 位的 ttmath 变量,所以保存它可能是一个不错的选择?除法或乘法这样的大数字可能很慢,所以我尝试使用加法,但无济于事。请帮助。
【问题讨论】:
使用缩进让它更清晰,一切看起来都像骗子。 这个函数有什么作用?您是否对此进行了分析以确认 if 语句有问题? 我“认为”它们使算法变慢。 使用优化别想,分析并仅依赖关于你可以看到的结果。 遇到这样的问题,您通常最好重新考虑整个算法。if
与条件分支的速度一样快,因此您需要找到一种方法来尽可能减少检查或更便宜的检查。这意味着更有效的算法。甚至可能无法加快速度,但您需要先了解为什么速度很慢,然后才能做到这一点。
由于此方法仅通过添加f
或一些常量int
来操作s
,并比较s >= 0
,您应该能够在最开始时弄清楚您想要返回什么算法。它甚至不需要条件语句(在我看来,就像一些操作和 mod 操作一样)。
【参考方案1】:
我不知道它是否更快,但它要短得多(并且更容易分析)。
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
。
编辑: 我遵循了月影的分析技术,但得出了不同的方程式。原始 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
实现似乎并未计算整数平方根。它似乎计算一个浮点平方根,然后将结果四舍五入,我猜这会比循环慢得多。
【讨论】:
感谢您的回答!它确实有效!它比以前更快! k[r] == 17 - (r*2),所以你不需要数组。实际上,应该可以将整个循环分解为一个 O(1) 表达式:结果是输入的线性函数。 嗯...我一定要试试!! @utkarshsinghal:f
和 s
的值是在 while 循环之前计算的值,并且该解决方案仅在 s
小于 0 时才有效。
@utkarshsinghal:无论如何,我认为您优化我最初为您发布的循环的方式已经非常快了。【参考方案2】:
首先,我注意到如果最终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。
【讨论】:
感谢您的回答和出色的分析!但是,当我尝试它时,它不起作用。例如 s=100 的 sqcalc,f=1 应该是 4,但是,它返回 1 .这可能是因为 (2*n)-1 返回 17,15,13.... 系列中的下一个数字,而不是之前数字的总和。你认为总和可以如何计算?A.P 的也许?【参考方案3】:根据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++ 查找根二次方程”。
选择一个函数,然后分析该函数并在必要时进行优化。
【讨论】:
【参考方案4】:我尝试求解二次方程,但对于较大的数字,它会使函数变慢。按照@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;
在这段代码中,我不是减去一个数字然后与零进行比较,而是直接将它与数字进行比较。到目前为止,这会产生最快的结果。虽然我可以进行二进制搜索....
【讨论】:
以上是关于c++中if语句的替代的主要内容,如果未能解决你的问题,请参考以下文章
JavaScript 单行 'if' 语句 - 最佳语法,这个替代方案? [关闭]