3.4.1 求解“鸡兔同笼”问题
有了条件分支和循环遍历这两类基本的流程控制,我们就能通过Java编程来解决复杂一点的问题了。现在通过一个阶段性的实战练习帮助大家加深对流程控制语句的理解。
在南北朝时期成书的数学著作《孙子算经》中,有一道趣味的“鸡兔同笼”问题,题干是“今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何?”这段文言文翻译成白话文,意思是:笼子里关着一群鸡和兔子,上面有35个头,下面有94条腿,请问鸡和兔子各有几只?显然这是一个二元一次方程组,列出代数方程式求解即可。按照中学课本里的常规解法,假设鸡的数量为x,兔子的数量为y,则有等式x+y=35(35个头)。又因为鸡有两条腿,兔子有4条腿,所以存在等式2x + 4y=94(94条腿)。结合x+y=35与2x+4y=94两个等式构成的方程组,很容易采取代数手段求得x=23且y=12,即笼子里有23只鸡加12只兔子。
不过代数方程式是西方的舶来品,中国的古人并不认识,我们的祖先使用另一种巧妙的办法,同样成功解开了鸡兔同笼问题。我国数学家命令笼子里的鸡和兔子都抬起一半的腿,于是两条腿的鸡抬起一条腿,4条腿的兔子抬起两条腿,瞬间笼子里站着的腿只剩下94的一半,也就是47条腿。这时每只兔子比每只鸡还多一条站立的腿,同时兔子跟鸡都只有一个头,那么笼子里腿的数量减去头的数量,必然等于兔子的数量。据此求得,兔子的个数=还站着的腿数量-头的数量=47-35=12,鸡的个数=头的数量-兔子个数=35-12=23。
无论是西方的方程式解法,还是东方的命令式解法,都依赖于解题者的算术素养,通过某种精巧的思路求得题解。然而程序代码非常笨,只会基本的加减乘除,即使引入牛顿迭代法,也只能求解一元N次方程的方根,你让它去计算二元一次方程组,没见过世面的程序还真要束手无策了。但计算机程序有一个优点,它的运算速度非常快,一秒钟能开展亿次运算,俗话说“笨鸟先飞”,计算机程序正是如此。虽然程序没有人那么聪明,可是笨办法总是会的,比如使用简单的穷举法把二元方程式可能的解一个一个试过去,只要方程式存在合理的解,穷举法就一定能够试出方程解。
就鸡兔同笼问题而言,鸡加上兔子的总数为35,意味着鸡的数量范围是0~35,真实的鸡个数必定是其中一个数字。那么我们可以设计一个循环语句,让鸡的个数从0开始算起,则兔子的个数=35-鸡的个数,于是全部腿的数量=鸡的个数×2+兔子的个数×4=94。这里的等式依然包含两个变量,分别是鸡的个数和兔子的个数,看起来仍是二元方程式,但运用了穷举法之后,在每次循环里面鸡的个数都是确定的(从0开始递增),兔子的个数也是确定的,代码只需判断表达式“鸡的个数×2+兔子的个数×4”的结果是否等于94。一旦发现腿的数量的计算结果符合条件,就表示本次循环预设的鸡的个数与兔子的个数正好是鸡兔同笼问题的答案。
根据以上的穷举法思路,编写对应的循环处理代码示例如下(完整代码见本章源码的src\com\control\Jitutonglong.java):
// 今有雉兔同笼,上有三十五头,下有九十四足,问雉兔各几何? int chick, rabbit; // chick表示鸡的数量,rabbit表示兔子的数量 int sum=35; // 鸡和兔子加起来一共35只 for (chick=0, rabbit=0; chick <= sum; chick++){ //利用穷举法逐个尝试可能的鸡兔组合 rabbit=sum - chick; // 计算兔子的数量 if (chick * 2 + rabbit * 4 == 94) { // 若满足鸡兔同笼的问题条件,则结束循环 break; } } System.out.println("鸡的数量为" + chick + ",兔子的数量为" + rabbit);
别看穷举法很傻,上面的几行代码分别运用了条件判断与循环操作两种控制语句,足以逐个筛查所有可能的鸡兔组合,并找到正确的问题答案。运行以上的穷举法代码,可以观察到以下的输出日志:
鸡的数量为23,兔子的数量为12
从日志可见,利用穷举法成功求得了鸡兔各自的数量,而且上述代码的解法适用于任何二元一次方程组,如果将代码里的35与94换成其他数字,那么这段代码仍然能够求出正确的方程解(只要存在的话)。