zoukankan      html  css  js  c++  java
  • C++ code:More Loop Designs

    1  逻辑判断

    对于逻辑判断问题,一般都要考虑全部的可能性,然后从这些可能性中按条件逐一排查,直到最后获得某个结论。

    【百钱买百鸡问题】

    问题描述:

    雄鸡(cock)7元一只,母鸡(hen)5元一只,小鸡(chick)1元3只。花费100元,买100只鸡,如果雄鸡、母鸡、小鸡都必须有,则雄鸡、母鸡、小鸡各应买几只?

    分析:

    考虑全部的可能性时,先考虑雄鸡的最高耗费金额为100-5-1=94元,取7的倍数,得91元,所以雄鸡数量范围为1~13;同理可得,母鸡数量范围为1~18;小鸡数量范围为3~96(注意小鸡虽可以花费100-7-5=88元钱买来264只,但由于总鸡数量的限制,三种鸡都要有,则小鸡数应小于等于98,取3 的倍数,则为96)。

    其程序表达式为:

    1 for(列举所有情况){ //可能为多重循环
    2     if(条件1不满足) continue;
    3     if(条件2不满足) continue;
    4     //...
    5     if(条件n不满足) continue;
    6     输出结果之一或者累计符合所有条件的方案  
    7 }

    本题的条件为:

    (1)雄鸡数量+母鸡数量+小鸡数量-100=0

    (2)雄鸡花费+母鸡花费+小鸡花费-100=0

    (3)小鸡数为3的倍数

    因此其反条件为:

    (1)cock+hen+chick-100!=0

    (2)7*cock+5*hen+chick/3-100!=0

    (3)chick%3!=0

    注:只要条件值非零,即为真。因此条件“x!=0”等价于“x”,最初分析的程序应为:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5     for (int cock = 1; cock <= 13;++cock)
     6     for (int hen = 1; hen <= 18;++hen)
     7     for (int chick = 3; chick <= 96; ++chick)
     8     {
     9         if (7 * cock + 5 * hen + chick / 3 - 100) continue;
    10         if (cock + hen + chick - 100) continue;
    11         if (chick % 3) continue;
    12         cout << "Cock: " << cock << ", Hen: " << hen << ", Chick: " << 100 - cock - hen << endl;
    13     }
    14     cin.get();
    15     return 0;
    16 }

    运行结果:

    还可以考虑程序的优化:由于chick=100-cock-hen,因此,确定了cock和hen也就确定了chick,可以省略chick这重循环。将所有有chick的地方用100-cock-hen代替。得到:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5     for (int cock = 1; cock <= 13;++cock)
     6     for (int hen = 1; hen <= 18;++hen)
     7     //for (int chick = 3; chick <= 96; ++chick)
     8     {
     9         if (7 * cock + 5 * hen + (100-cock-hen) / 3 - 100) continue;
    10         //if (cock + hen + chick - 100) continue;
    11         if ((100 - cock - hen) % 3) continue;
    12         cout << "Cock: " << cock << ", Hen: " << hen << ", Chick: " << 100 - cock - hen << endl;
    13     }
    14     cin.get();
    15     return 0;
    16 }

    也可按照下面的模式:

    1 for(列举所有的可能情况
    2 {
    3     if(条件1满足&&条件2满足&&...&&条件n满足)
    4         输出结果之一或者累计符合所有条件的方案
    5 }

    则得程序如下:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5     for (int cock = 1; cock <= 13;++cock)
     6     for (int hen = 1; hen <= 18;++hen)
     7     //for (int chick = 3; chick <= 96; ++chick)
     8     {
     9         //if (7 * cock + 5 * hen + (100-cock-hen) / 3 - 100) continue;
    10         //if (cock + hen + chick - 100) continue;
    11         //if ((100 - cock - hen) % 3) continue;
    12         if ((100 - cock - hen) % 3 == 0 && 7 * cock + 5 * hen + (100 - cock - hen) / 3 == 100)
    13         cout << "Cock: " << cock << ", Hen: " << hen << ", Chick: " << 100 - cock - hen << endl;
    14     }
    15     cin.get();
    16     return 0;
    17 }

    或者为:

     1 #include<iostream>
     2 using namespace std;
     3 int main()
     4 {
     5     for (int cock = 1; cock <= 13;++cock)
     6     for (int hen = 1,chick=99-cock; hen <= 18;++hen,chick--)
     7     //for (int chick = 3; chick <= 96; ++chick)
     8     {
     9         //if (7 * cock + 5 * hen + (100-cock-hen) / 3 - 100) continue;
    10         //if (cock + hen + chick - 100) continue;
    11         //if ((100 - cock - hen) % 3) continue;
    12         if ((100 - cock - hen) % 3 == 0 && 7 * cock + 5 * hen + (100 - cock - hen) / 3 == 100)
    13         cout << "Cock: " << cock << ", Hen: " << hen << ", Chick: " << 100 - cock - hen << endl;
    14     }
    15     cin.get();
    16     return 0;
    17 }

    运行结果很显然和初始代码一样

    2 级数逼近

    循环的设计,对于无穷数列求和,逼近到某个近似值的情况。例如,数学公式:

    π/4=1-1/3+1/5-1/7+...

    求π的近似值,精确到小数点后6位。

    分析:

    (1)整数不能表示小数,所以π的值用浮点double表示。

    (2)按公式,先求π/4。

    (3)数列的第n项是(-1)n-1/(2n-1)。

    (4)一种方法是根据循环变量n,求得第n项的值,累计,然后条件判断,若满足结束条件,则退出。循环条件为:前一个累计和与后一个累计和的差小于106。由于前后累计的结果之差的绝对值等于后一次加上去的值,所以,也可以通过判断后一项的绝对值小于106而得到循环退出条件。

     1 #include<iostream>
     2 #include<cmath>
     3 #include<iomanip>
     4 using namespace std;
     5 int main()
     6 {
     7     double sum = 0, item = 1;
     8     for (int n = 1; abs(item) > 1e-6; ++n)
     9     {
    10         item *= (-1.0)*(2 * n - 3) / (2 * n - 1);
    11         sum += item;
    12     }
    13     cout << "Pi= " << setiosflags(ios::fixed) << setprecision(6) << sum * 4 << endl;
    14     cin.get();
    15     return 0;
    16 }

    运行结果:

    补充知识点:

    【C++中setiosflags( )的用法】

    setiosflags 是包含在命名空间iomanip 中的C++ 操作符,该操作符的作用是:执行由有参数指定区域内的动作;
    iso::fixed 是操作符setiosflags 的参数之一,该参数指定的动作是以带小数点的形式表示浮点数,并且在允许的精度范围内尽可能的把数字移向小数点右侧;
    iso::right 也是setiosflags 的参数,该参数的指定作用是在指定区域内右对齐输出;
    setprecision 也是包含在命名空间iomanip 中的C++ 操作符,该操作符的作用是设定浮点数;

    setprecision(2) 的意思就是小数点输出的精度,即:小数点右面的数字个数为2。

    1 cout<<setiosflags(ios::fixed)<<setiosflags(ios::right)<<setprecision(2);

    合在一起的意思就是,输出一个右对齐的小数点后两位的浮点数。

    使用setprecision(n)可控制输出流显示浮点数的数字个数。C++默认的流输出数值有效位是6。

    如果setprecision(n)与setiosflags(ios::fixed)合用,可以控制小数点右边的数字个数。

    (5)另一种方法是先设定一个初项,然后一边累计,一边根据前一项求下一项,一直累计,直到满足条件为止。

     1 #include<iostream>
     2 #include<cmath>
     3 using namespace std;
     4 int main()
     5 {
     6     double sum = 0, item = 1;
     7     for (int denom = 1,sign=1; abs(item) > 1e-6; denom+=2,sign*=-1)
     8     {
     9         item *= sign / double(denom);
    10         sum += item;
    11     }
    12     cout << "Pi= " << fixed<< sum * 4 << endl;
    //此处的“fixed”表示
    用一般的方式输出浮点数,而不是科学计数法。
    13     cin.get();
    14     return 0;
    15 }

    流输出的默认精度是6位,与这里的输出要求一致,所以可以省略精度设置。由于直接用了fixed流状态操作,便可以省略流的辅助头文件iomanip。

    denom存放每一项的分母值,它与sign都仅在for循环中使用,所以放在for循环初始处定义是清晰的。

    abs是求浮点数的绝对值。调用abs函数,对于整数,其返回值也为整数;对于浮点数,其返回值也为浮点数。C++采用了一种函数重载的技术来实现这一编程的方便性。

    当然,也可以用while循环来实现:

     1 #include<iostream>
     2 #include<cmath>
     3 using namespace std;
     4 int main()
     5 {
     6     double sum = 1, item = 1;
     7     int denom = 1, sign = 1;
     8     while (abs(item) > 1e-6)
     9     {
    10         denom += 2;
    11         sign *= -1;
    12         item = sign*1.0 / denom;
    13         sum += item;
    14     }
    15     cout << "Pi= " << fixed << sum * 4 << endl;
    16     cin.get();
    17     return 0;
    18 }

    运行结果:

    for和while循环没有性能上的差异,两者在使用上完全反映了编程风格。作者更喜欢用for循环,一是因为希望履行局部变量尽量不要公开化的模块原则:denom、sign是局限于循环内的,能够让它们在循环描述中定义和初始化是最合适的;二是循环变量的修正,或者一切调整循环状态的语句,放在循环描述中也更显得直观,而且也压缩了循环体的规模,减轻了阅读强度。因此for循环体现了更好的结构性和简洁性。

    在后面的章节中,作者刻意地在使用for而很少使用while,目的是在告诉读者,很多过程优化是从for循环下手的,调配for循环描述的三个部分构成了编程的初级艺术,它的根本性体现在对循环结构的深刻认识上。

    那么能否用do-while语句来实现呢?能。只不过初值要改一下:

     1 #include<iostream>
     2 #include<cmath>
     3 using namespace std;
     4 int main()
     5 {
     6     double sum = 0, item = 1;
     7     long denom = -1;
     8     int sign = -1;
     9     do
    10     {
    11         denom += 2;
    12         sign *= -1;
    13         item = sign*1.0 / denom;
    14         sum += item;
    15     } while (abs(item)>1e-6);
    16     cout << "Pi= " << fixed << sum * 4 << endl;
    17     cin.get();
    18     return 0;
    19 }

    运行结果:

    看上去与while循环结构差不多,但其循环控制思路上差了一个节拍。作者并不主张这类问题用do-while解决,只有在迫不得已,即用while写出的循环很难看懂时,才偶尔用一下do-while。

  • 相关阅读:
    个人对回调函数的理解(personal understanding of callback function)
    蓄水池抽样及实现
    一些我做的软件
    poj1063 解题报告(poj 1063 analysis report)
    有关MAP、ML和EM的个人理解
    2012年总结
    asp.net中requiredfieldvalidator很纠结的问题(有关ClientScript属性)
    wcf在iis6上的部署
    GridView后台代码动态显示隐藏ItemTemplate
    aspx向silverlight传值
  • 原文地址:https://www.cnblogs.com/ariel-dreamland/p/8997631.html
Copyright © 2011-2022 走看看