您的位置:首页 > 职场人生

黑马程序员——致java初学者:代码编写完整流程(兔子问题案例)

2014-08-09 22:08 531 查看
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------

声明:此篇文章为本人原创,当前版本为修改版,转载请注明出处或添加“转载”字样(这么说是因为被人悄悄的转载过)。

一、话题引入

1.学习中总是有人这样说

在我学习Java基础时,身边总有一些同学在说“听课看视频总是没问题,但是代码就是不会敲”。每到这时候,我就会随便找一个比较经典的案例,然后问问他“来,说说思路”。一般的,我得到的回答都是不知道、不会、说不出来……;也有人说了几条,然后就乱了;还有人,能说个大概,但是漏洞百出。联想到我刚刚学习编程语言时的情况,我感觉这是一门语言的初学者都会遇到的问题:我新学习了一种工具,但是我不能用它解决哪怕很简单的实际问题。而以我个人的观点,这个现象的症结在于编程语言的初学者还没有掌握分析进而掌握实际问题的思维,这就更别提从代码的角度,用编程的思想去解决问题了。而这个问题可以说是编程学习者面临的第一个坎,于是我结合自己的体会编写这篇播客,希望能对广大刚刚接触编程的学习者起到一定的辅助作用。我准备采用经典的小兔子问题为案例(此案例难度适中,最主要的方便进行分析过程的解读),下面是小兔子问题的描述。

2.小兔子问题的描述

有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子对数为多少?

二、总述

1.分析概述

在使用程序解决实际问题的过程中,至少包含两步:1、将实际问题转变为可用代码解决的问题 2、使用代码解决将实际问题转变为可用代码解决的问题的过程实质上是分析问题的过程,这个过程的结果是-->得出解决实际问题的思想使用代码解决的过程实质上是用编程语言说出解决实际问题思路的过程,这个过程的结果是-->将问题解决其实,在思想清晰的情况下,使用代码解决问题这个过程极其简单(即使与分析得出思想的过程相比也是如此),它几乎等同于“一个人想吃东西,他会说:‘有吃的吗?’;但是现在他身边只有外国人,所以他说:‘Would you has some food?’” 这样的过程。这二者所表达的都是询问对方有没有吃的,要做的只不过是使用一种可以解决问题的语言把这个意思表达出来。与之相对应的,是思路模糊的情况。此时如果想直接用代码解决实际问题,这就相当于自己都不知道自己要说什么,但是却硬要说,这样的后果是:依旧使用刚刚举得例子,不对老外表达出“我想吃屎”的意思就不错了。之所以说“分析问题”比“表达思想”复杂是因为:这个世界上有很多中渠道去教我们使用英文,但是没有任何一种直接的渠道告诉我们“我们想说什么”

2.小小的概括一下

使用程序解决实际问题,粗略且不负责任的总结一下:一共有两种风格第一种风格:泛泛的分析-->明确问题是什么-->编写代码用计算机去完整的模拟问题中的每一步(实际上分析时,我们只分析出了这个问题包含哪些步)。这种风格分析简单、代码表达复杂。比如1到100的求和,我们分析的过程十分少,用代码控制程(1+2+3...100)就可以了。当一门编程语言的语法粗通后,多采用这种分析风格,但如果想在技术上有建树的话,个人无责任猜想:早晚要转型至另一种风格,因为归根到底问题还是要靠人来解决的。第二种风格:深入的分析-->得到一个更优良的思思路-->用相对简单的代码实现这种风格分析复杂、代码表达简单。依旧用上面的例子,用代码控制程序return100*(1+100)/2;搞定。打一个比方:风格1所说出来的是“TMD饿死老子了,老子要吃饭”;风格2所说出了的是“朕饥,膳”。

三、案例

1.体验第一种风格的分析方式

首先,我们尝试用第一种风格去分析兔子问题
为了分析方便我们就把一对兔子称为一只了啊(其实是我后面都敲错了,懒得改了,太多= =||)对于每一只兔子而言其数量变化都符合{11 2 3 4 5 6 7 8 9...}这一数列。则设兔子出生了mon个月,兔子的数量为vol。它们满足关系 mon==1 时 vol==1; mon==n(n!=1) 时 vol==(n-1)。分析完毕,没错,分析完毕了(泛泛的分析嘛),我们可以尝试用代码实现了。需要注意的是,我们仅仅对于每一只兔子进行了分析,那我们的代码应该反映每一只兔子的状态,于是我们应该对于每一只兔子都建立一个实例。每一只兔子都是同时存活的,所以应该把每一只兔子都建立一个线程(至少要把同时出生的一窝兔子放入一个线程),这是为了防止出现“有的兔子成长了两个月而有的兔子仅成长了一个月”的线程安全问题,并且,所有线程都应该同步且建立线程间通信保证每只兔子都在一个月中得到一个月的成长后再进行下一个月然后套用我们刚刚得出的关系,然后我们只有统计有多少兔子实例就可以了好了,我们可以编代码了,额…………现在,我们遇到了“不会说外语”的情况,我们表达不出我们的思想风格1不适合我们,我们只能尝试深入分析问题然后简单表达的风格了

2.体验第二种风格的分析方式

下面我们对每个月的兔子的数量展开分析:
(注意,刚刚我们只是简单的分析了每一只兔子有什么特性,案例描述的其实就是每一只兔子的特性;现在我们是分析所有兔子的数量,这个案例中没有直接的描述,两种分析区别很明显)我们用字母'f'表示兔子的数量(别问我为毛不用r)用上标的形式表示这个月的兔子数量f(^0),上个月的兔子数量f(^-1),和上上个月的兔子数量f(^-2)兔子有三种分别是刚出生(出生一个月的兔子)的兔子,出生两个月的兔子,出生三个月及三个月以上的兔子这里我们用前缀的方法表示为1_f,2_f,3_f于是有这个表:        总兔子数  出生一月  出生二月  三月及以上   当前月  f(^0)   1_f(^0)  2_f(^0)   3_f(^0)   上个月  f(^-1)  1_f(^-1)  2_f(^-1)  3_f(^-1)   上上月  f(^-2)  1_f(^-2)  2_f(^-2)  3_f(^-2)分析开始:当前月兔子数为(当前月出生一月的兔子数量)+(当前月出生二月的兔子数量)+(当前月出生三月及以上的兔子数量)即f(^0)= 1_f(^0) + 2_f(^0) + 3_f(^0)同理f(^-1) = 1_f(^-1)+ 2_f(^-1) + 3_f(^-1)f(^-2) = 1_f(^-2)+ 2_f(^-2) + 3_f(^-2)当前月一月兔子是由上个月的三月及以上兔子生出来的,另外上个月的二月兔子到这个月会变为三月兔子并生下小兔子,也就是说当前月一月兔子数=上月三月兔子数+上月二月兔子数。即1_f(^0) = 2_f(^-1)+ 3_f(^-1)当前月的二月兔子都是由上个月的一月兔子存活而来即2_f(^0) = 1_f(^-1)当前月的三月兔子是由上个月的二月兔子生长和上个月的三月兔子存活而来即3_f(^0) = 2_f(^-1)+ 3_f(^-1)此时不难发现f(^0) = 1_f(^0) +2_f(^0) + 3_f(^0) = 2_f(^-1) + 3_f(^-1) + 1_f(^-1) + 2_f(^-1) + 3_f(^-1)因为f(^-1) = 1_f(^-1)+ 2_f(^-1) + 3_f(^-1)所以f(^0) = 2_f(^-1)+ 3_f(^-1) + 1_f(^-1) + 2_f(^-1) + 3_f(^-1) = f(^-1) + 2_f(^-1) + 3_f(^-1)用同样的方法分析2_f(^-1) 3_f(^-1)有上个月的二月兔子为上上月一月兔子数2_f(^-1) = 1_f(^-2)上个月三月为上上月二月兔子数+上上月三月兔子数3_f(^-1) = 2_f(^-2) + 3_f(^-2)又因为f(^-2) = 1_f(^-2)+ 2_f(^-2) + 3_f(^-2)于是:f(^0) = f(^-1)+ 2_f(^-1) + 3_f(^-1) = f(^-1) + 1_f(^-2) + 2_f(^-2) + 3_f(^-2= f(^-1) + f(^-2)现在我们得到了兔子总数和月份的关系:当前月兔子总数=上两月兔子总数的和

四、代码部分

从开始到刚刚,我们一直处于用代码解决实际问题的第一步:将实际问题转变为可用代码解决的问题
现在,我们通过上面的分析已经得到了兔子总数与月份之间的关系,我们可以进行下面的步骤了:使用代码解决
而从刚刚的分析我们不难得出下面的两句话:
我们需要三个变量分别保存当前月、上个月、上上月兔子数量:r,ri,r2我们需要一个变量记录月份:m(后续内容见程序注释)*************************************/
import java.util.Scanner;class  Rabbit{public static void main(String[] args){int m=1;//从第一个月开始嘛int max;//声明一个变量来存储显示几个月的兔子数量System.out.println("您希望显示几个月的兔子数量?请输入: ");max = new Scanner(System.in).nextInt();//从键盘获取需要显示几个月的兔子数int r=1;//第一个月只有一只兔子嘛int r1=0,r2=0;//第一个月时哪里有上上个月嘛/*每个月我们都打印一下月份和兔子数因为我们每个月都打印数量,且兔子数量是有规律的所以我们需要一个循环如果是第一个月 数量就是1,因为第一个月没有上个月第二个月数量还是1,因为没有上上个月没个月打印完后(进入下一轮循环之前)这个月的兔子变成上个月的,上个月的变成上上月的于是r2=r1;r1=r;等到了下个月时,(下个月变成了当前月)兔子等于上个月的兔子+上上月的r=r1+r2;差不多了,代码开敲*/for(;m<=max;m++){//从一月开始到最大月份结束if (m==1||m==2)r = 1;//一月二月就一只elser=r1+r2;//三月之后为上两月的和System.out.println("第"+m+"个月,兔子的数量为"+r+"只");//显示一下r2=r1;r1=r;//上个月变上上月,当前月变上个月}}}<span style="font-size:18px;"></span>

五、写在后面

以上,是对于一个需求(当然,我们的案例是一个很简单例子),从获知到敲出代码的完整分析过程,我试图囊括分析和实现代码的点点滴滴,希望对广大刚刚接触编程的初学者有些许益处。
------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------
                                            
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐