具有多个语句和条件的单个循环比几个简单循环更好吗?

时间:2016-08-11 19:35:10

标签: c algorithm performance readability

我正在创建一个非常简单的程序,使用贪心算法确定将更改返回给客户端所需的硬币数量。 该算法非常明显,您只需确定哪个是您可以使用的较大硬币,从更改中减去其值并更新硬币计数器。

我想到了两个非常相似的实现。

注意:changeInt是更改,乘以100并转换为整数。

1)单个“复杂”循环

while(changeInt != 0) {
       if(changeInt - 25 >= 0){
           changeInt -= 25;
           coins++;
       }
       else if(changeInt - 10 >= 0){
           changeInt -= 10;
           coins++;
       }
       else if(changeInt - 5 >= 0){
           changeInt -= 5;
           coins++;
       }
       else if(changeInt - 1 >= 0){
           changeInt -= 1;
           coins++;
       }

   }

2)多个简单循环

    while(changeInt - 25 >= 0) 
   {
       changeInt -= 25;
       coins++;
   }
    while(changeInt - 10 >= 0)
   {
       changeInt -= 10;
       coins++;
   }

    while(changeInt - 5 >= 0)
   {
       changeInt -= 5;
       coins++;
   }

    while(changeInt - 1 >= 0)
   {
       changeInt -= 1;
       coins++;
   }

现在,我知道在两种情况下性能可能都是类似的,因为算法是相同的,但我想知道哪种方法更好。

单循环是我提出的第一个想法,然后我想到了第二种方法,直觉上看起来对我来说更好。

我并不关心我的确切情况,我对一般情况更感兴趣(几个简单的循环与几个更复杂的循环)

1)哪种方法在性能方面更好?

2)差异是否明显,至少在处理大量数据时是什么?

3)一种方法比另一种方法更具可读性吗? (不知道我能否在这里问一下)

谢谢!

4 个答案:

答案 0 :(得分:2)

正如其他人所提到的,第二种方法更可取,因为它使用较少的比较。

更简洁,更简洁的方法是使用除法和模数:

int current = changeInt;
coins += current / 25;
current %= 25;
coins += current / 10;
current %= 10;
coins += current / 5;
current %= 5;
coins += current;

虽然div和mod运算符比减去运算符更贵,但对于changeInt的较大值而言它可能会更快,并且没有分支。

答案 1 :(得分:1)

如果您必须在您描述的循环方法之间进行选择,则第二种方法更可取(略有变化)。它更干净,大多数都避免了不必要的测试。

这里有轻微的变化......

while(changeInt >= 25) {
   changeInt -= 25;
   coins++;
}

while(changeInt >= 10) {
   changeInt -= 10;
   coins++;
}

while(changeInt >= 5) {
   changeInt -= 5;
   coins++;
}

while(changeInt > 0) {
   changeInt -= 1;
   coins++;
}

这提供的主要优势在于它有助于确保改变 - X'永远不会缠绕。根据您在帖子中描述的内容,它不太可能是一个问题,但如果类型从有符号整数更改为无符号整数,那么您可能已经发现自己试图找出错误的位置。

或者,您可能希望使用除法运算符和模数运算符的组合来计算更改并避免循环。

希望这有帮助。

答案 2 :(得分:0)

如你所说,两者都很相似,如果我们谈论的是可读性,我更喜欢第一种(但这是一种主观意见)。

如果我们考虑性能,恕我直言,第二个比前一个快一点。我们可以尝试将其转换为ASM以进行详细比较:

1)单个“复杂”循环(aprox ASM x86-64)

jmp
mov
sub
test
js
sub
add
jmp
mov
sub
test
js 
sub
add
jmp 
mov 
sub 
test
js  
sub 
add 
jmp 
mov 
sub 
test
js 
sub
add
cmp
jne

2)多个简单循环(aprox ASM x86-64)

jmp
sub
add
mov
sub
test
jns
jmp
sub
add
mov
sub
test
jns
jmp
sub
add
mov
sub
test
jns
jmp
sub
add
mov
sub
test
jns

如果我们计算x86-64 ASM指令:

jmp: 4
mov: 4
sub: 8
test: 4
add: 4
js: 4
cmp: 1
jne: 1

vs

jmp: 4
mov: 4
sub: 8
test: 4
add: 4
jns: 4

然后总结一下:

js 4
cmp 1
jne 1

VS

jns 4

“js”类似于“jns”。

但是这可能会随着其他编译器或架构而改变,即便如此,我认为第二个比第一个更快一点。

答案 3 :(得分:0)

我认为没有理由为此需要一个循环。添加临时变量以防您需要保留原始值:

int tempChangeInt;

tempChangeInt = changeInt;
coins = changeInt / 25;
tempChangeInt = changeInt % 25;
if (tempChangeInt != 0)
{
    coins += tempChangeInt / 10;
    tempChangeInt = tempChangeInt % 10;
}
if (tempChangeInt != 0)
{
    if (tempChangeInt >= 5)
        coins += (tempChangeInt - 5) + 1;
    else
        coins += tempChangeInt;
}