兄弟

顶你的

虽然我也是拿来主义 可是

不管了 顶你

前面leebai提到一道题:
--------------------
数字1、2、3、、、、10排成一圈,首尾相连。从1开始从中取数,每隔两个数取一个,如1、4、7、、、,直到取完为止。要求写个程序把取数序列打印出来,可以用任何熟悉的语言或伪代码。
--------------------

做了一下,用面向过程的思路大概是这样解决:



int allNumber = 100; // 表示从1到10的数字
int nextI = 3;
// 表示隔两个取数字
long timeStart=System.nanoTime();
LinkedList ll = new LinkedList();
for (int i = 1; i <= allNumber; i++) {
ll.add(new Integer(i));
}
Integer itgNow = null;
int i = 0;
while (true) {
if (i == ll.size()) {
break;
} else {
if (i > ll.size()) {
i = i - ll.size();
}
itgNow = (Integer) ll.get(i);
System.out.println(itgNow.intValue());
i = i + nextI;
}
}
System.out.println(System.nanoTime()-timeStart);

如果面向对象的方法,我的思路如下:


int allNumber = 100; // 表示从1到10的数字
int nextI = 3;
// 表示隔两个取数字
long timeStart=System.nanoTime();
// 定义这个数字环起始的一环,即“1”
RoundNumberUnit nStarter = new RoundNumberUnit(1);
// 初始化这个数字环,目前只有一个元素“1”。
RoundNumber roundNumber = new RoundNumber(nStarter);
// 从2开始添加到最大值。在添加方法中将各元素连接起来。
for (int j = 2; j <= allNumber; j++) {
roundNumber.addNew(new RoundNumberUnit(j));
}
// 开始从每一个取值
RoundNumberUnit nNext = roundNumber.getStarter();
do {
System.out.println(nNext.getNumberValue());
// 继续取向左的第nextI个元素。
nNext = roundNumber.getNextLeft(nextI);
} while (nNext != roundNumber.getStarter());
// 如果回到第一个元素则退出循环。
System.out.println(System.nanoTime()-timeStart);

附件:test.rar
(里面包含了面向过程和面向对象的所有代码)


1、可以看出面向对象多写了一些类,多了些代码,不过IDE可以帮助生成Bean的那些代码。
2、面向对象的好处是思路比较自然,扩展性也好得多,面向过程的算法式的解法短平快,但只适应这一道题,而且需要了解链表LinkedList的结构特点。
3、面向对象的方法实际上实现了链表性的结构,同时后期可以在此基础上扩展其他更多的方法,其中的元素也不局限于是数字,可以考虑成是别的业务对象。
4、这里提一下我的测试,当最大数加到100000时,面向对象的方法效率明显更高,可能是因为对象有了缓存的原因吧。

应该说我更支持将面向对象作为初学者的基础,让他们一入门就有这样好的思维逻辑,顺理成章地继续接口、模式、框架等的学习,在这过程中,在解决具体业务问题时,再顺便学习一些好的算法、数据结构等。这样的发展应该是非常好的,并不偏废哪一方面,以后可以根据自己的特长走向某些专业领域。

[该贴被wind13于2007年04月11日 17:05修改过]
attachment:


test.rar

看完了,真的不知道说什么了??
第一,我承认我有点跑题,后面得跟帖跟前面真的有些离题了,我是想说明一个程序员的内涵,而不是说明了java语言的基础。这个我承认。

第二,有点无奈,现在程序员可以说自己不懂算法。而且说得理直气壮,我不知道对错,我知道你能做工程,也许还做的不错,不过也仅此而已。而且算法已经归类于专向人才了:(,不知道你想过么,你的select就是算法的结晶,为什么你要建立索引,这是一种数据结构啊,为什么用B-tree而不用其他的??觉得我吹毛求疵了??
可是看看这个论坛的帖子,为什么怎么怎么做就提示内存溢出,那么你考虑过可能是你的存储或者算法有问题么??

第三,oo是否重要??完全重要,因为项目没有不修改的,所以不用oo改起来会很麻烦。framework重要么??也重要,有了它原来半年的工程我现在2个月搞定,老板高兴,我也高兴。但是学习这个并不是大家想想中那么难,你说什么是soa,什么是DDD,我们其实过去做项目的时候都用过,只是没有上升到理论的层次罢了,现在提出来指导开发,其实这些概念我觉得更加属于软件销售人员的,因为客户需要这样的指导概念。

第四,我无疑挑谁的语病,过去说文无第一,武无第二,我就是说咱程序员就像学武的一样,怎么辨别出谁厉害,写两段代码,解决两个实际问题,一下子就看出来了。谁对谁错我们见见真章。不过有点失望,鼓吹DDD的到现在没有一个告诉我怎么分析怎么做怎么实现。觉得项目小了不值得做??如果小的都做不来真的怀疑大的了。

第五,没有第五,希望大家在j2ee,spring,hib中不要迷失了就好。饭碗重要,但是未来的饭碗就不重要了??40岁的时候还要为了迎合老板去学习新的架构么??国外的程序员为什么可以到40岁依然做,因为他们的基础在那里。

最后说一句,如果大家觉得我言辞过分,请大家原谅,我对事不对人。

出来2年 独立设计并编写了N个系统
可是 我连二叉树是什么都不知道

谁能告诉我下 是干什么的?

另外,也说一下前面Coolyu0916 所说的那个公交车的问题。

确实计算如何转车不是一件容易的事,而且我可以肯定的是你目前已经实现的方法也不是非常完美的,因为没有基于对象的思想。

首先,对于一个城市的站点可能不能那样简单地以字符串来表示,很多十字路口会有多个比较近的站点,可能站点名称也不相同。所以一个站点这样一个对象,应该抽象出来,它可能需要有不断增加和修改的站点名称,以便更好地匹配和未来的扩展。

其次,公交线路可以作为一种对象,将这些站点对象串起来,类似我上面做的那个数字环的思路,其实是人们理解一个问题的最简单的方式,比如“始发站”、“终点站”、“当前站”、“上一站”、“下一站”等概念,可以很好地用面向对象的思想整理出来。

第三,针对站点类,可以扩展附近特征建筑这样的类,将更好地表现一个城市的公交情况,也更方便地通过一些具体的参照建筑来查找线路。

第四,转车的问题,实际上是个比较复杂的问题,如果我的算法水平达不到时,可以只考虑转一次车的查询,或者就算是不提供转车的查询,我想这个系统也是基本可以接受的。

这就是我为什么支持banq的说法,认为程序员更应该先以面向对象思想、模式思想、框架思想等为基础学习,算法的问题,可以作为具体面对问题时的解决手段,这方面不影响大局,可以专项研究个别问题即可,况且有些问题已经有很多解决手法,比如这个公交车的转车算法,我完全可以参考Coolyu0916 的算法,将我这个系统完善起来,但是我的大体架构是不会大变的,以后是有更好的扩展性的。

Coolyu0916兄高人也!

看了你其他几个贴子,很多观点言论偶都非常认同,实乃真“道”中人也。感觉Coolyu0916实力与banq兄难分高下,一个内功深厚,一个得武林绝学,二位真应该认真切搓切搓,让天下菜鸟瞧瞧啥叫真功夫,啥叫真本事,哈哈。

leebai 说笑了,我只是喜欢写程序罢了。

wind13 ,你的设计我看了一下
首先是这样,地点相同,地名不同的不予考虑,
换车次数通过广度优先来考虑,一般可以设计在4次以内(多了没有意义,不过这个可以灵活设置),下一战,上一战没有什么意义,可以看一下我的数据文件,我的里面没有环路,所以基本上可以考虑继承一个List就可以。如果不做最短路径,或者少价格,用string就可以了。如果没有算法,单纯的靠分析与OO解决不了这个问题,而且该问题也是非常实际的问题。可以用于火车、轮船等物流公司的最佳运送路径,这就是为企业节省成本啊。

to wind13:

看出来老兄真是OO中人,连这个题都会想到去用LinkedList,真是离不开对象了。由此我也发现一个问题:思想被传统编程禁锢的人转到完全OO是很困难的,思想被OO编程禁锢的人解决简单传统问题也是很困难的:)。

按你对题的理解,最简单的程序差不多是这样的:

int n=10;
int number = 1;
for(int i=0; i<n; i++){
System.out.println(number);
number += 3;
if(number > n)
number -= n;
}

你再与对象OO实现对比一下最大值100000时的运行性能,呵呵。

其实此题的本意是取出来的数就不能参与间隔计算了,只有个别应聘者能理解到这一层。

[该贴被leebai于2007年04月11日 17:52修改过]

To leebai,Coolyu0916

其实武功中有一项很重要的,也容易被初学者忽视的东西——心法。

编程思想可以理解成这种心法,而当前最好的思想,莫过于面向对象的思想,所以初学者为什么要舍本逐末?DDD则更是解决企业应用架构方面很好的武功心法。而算法、数据结构等更象是江湖中常说的入门功夫或是基本功夫如扎马步等,但是心法则象是佛家的思想、道家的思想、或者独孤九剑的总决等,这些即使是不练武功的人也可以认真学习,即使本身功夫不高(算法不厉害),但江湖中实际上不仅仅是以功夫高低论英雄的。而且很多高级的功夫,是一定要以心法为基础来修习的。

一味追求功夫的高低,忽视心法的重要,这样通常有两种后果:
一是最后象“鸠摩智”那样走火入魔的;
一是象大多数的江湖侠客那样,仅仅有些面子上的功夫,无法提升到高级境界。

希望初学的程序员能够明白,什么才是真正的基础,应该走向哪条道路。

To leebai
嗯,是我用对象概念太多了,呵呵……你的算法快得多。

另外你说理解到那一层,是我没仔细看清题目,是要取出来再不能用了,这样的话就在我原来那个对象上加个remove的方法,应该就可以得出新的答案了,如下:


RoundNumberUnit n2Current = roundNumber.getStarter();
roundNumber.setCurrent(n2Current); // 设当前的为第一个
while (true){
System.out.println(n2Current.getNumberValue());
roundNumber.setCurrent(n2Current.getNumberLeft());
roundNumber.removeOne(n2Current);
if(roundNumber.getLength()>0){
n2Current = roundNumber.getNextLeft(nextI-1);
}
else{
break;
}
}

结果应该是:
1
4
7
10
5
9
6
3
8
2
这里体现出面向对象思路的可扩展性,修改后就可以解答了,呵呵……,但是面对leebai的那个算法就不知道怎么可以改出新的答案了,好象要用一种全新的思路才可以解那个新问题了。

其实我是用了很笨的办法,用面向对象的思路,解决这样一个可能用算法做起来很简单的问题,而且第一种思路还用了LinkedList对象,因为感觉这个有点象这种结构,呵呵……
这些也都说明我还是个初学者,还需要更多地学习。

如果用算法来解这个题,是不是用两个数组来搞?一个是原始的1到10,一个是要打印的数组,我知道在javascript中Array可以pop出去,另一边push到要打印的数组里,其他循环类似之前的办法。
[该贴被wind13于2007年04月11日 21:31修改过]

用LinkedList说明你很了解这个数据结构
同样是remove方法,效率会比ArrayList高很多
这就是程序常出现的问题
为什么一样的程序你的快,我的慢
就在于你明白什么时候用什么
也许100个对象的时候我们程序差不多速度
但是10000个的时候就有所察觉
1000000的时候估计就非常明显了。

我也同意你的内功心法的说法,不过这个都是这样,如果你连马步都不会,你要心法有什么用,你根本都不知道任督二脉在何处怎么能打通它那,我们现在谈基础,像你在算法以及数据结构上有了修养,继续往上走就会轻松很多。

可以这样感觉:
一个是宏观上的基础路线;
一个是微观上的基础路线;

两手抓,两手都要硬,哈哈……
不过象我这样的初学者,可能理解宏观上的东西比较容易吧,微观上的东西可能需要数学的思维能力较强些,有很多算法之类的方法也是经验性的或是带点运气的成份。


public class TestRoundNumber {

public static void switchNumber(int[] iNumber,int iLeftTrue,int iJumped){
for(int l=0; l<iNumber.length; l++){
if(iJumped==2){
if(iNumber[l]==0){
iNumber[l]=1;
//System.out.println(l+1);
iJumped=0;
iLeftTrue--;
}
}
else if(iJumped<2){
if(iNumber[l]==0){
iJumped++;
}
}
}
if(iLeftTrue>0){
switchNumber(iNumber,iLeftTrue,iJumped);
}
}
public static void main(String[] args) {
int allNumber = 100000;
// 表示从1到10的数字
int nextI = 3;
// 表示隔两个取数字
long timeStart=System.nanoTime();
timeStart=System.nanoTime();
int iLeft=allNumber;
int iJumped=2;
int arrayO[]=new int[allNumber];
TestRoundNumber.switchNumber(arrayO, iLeft, iJumped);
System.out.println(System.nanoTime()-timeStart);
}

}

参考了一个网上的什么圆桌数数的算法,写了上面这个,数字设到100000,运行时间:45401860。
用我原来写的那个类的,运行时间:91500736。仔细测试了一下,主要是类的初始化(构造数字环时)比较慢些,运算时倒挺快。

public static void main(String[] args) {
int allNumber = 100000; // 表示从1到10的数字
int nextI = 3;
// 表示隔两个取数字
long timeStart=System.nanoTime();
timeStart=System.nanoTime();
RoundNumberUnit nStarter = new RoundNumberUnit(1);
RoundNumber roundNumber = new RoundNumber(nStarter);
for (int j = 2; j <= allNumber; j++) {
roundNumber.addNew(new RoundNumberUnit(j));
}
RoundNumberUnit n2Current = roundNumber.getStarter();
roundNumber.setCurrent(n2Current);
// 设当前的为第一个
while(true){
// System.out.println(n2Current.getNumberValue());
roundNumber.setCurrent(n2Current.getNumberLeft());
roundNumber.removeOne(n2Current);
if(roundNumber.getLength()>0){
n2Current = roundNumber.getNextLeft(nextI-1);
}
else{
break;
}
}

System.out.println(System.nanoTime()-timeStart);
}
}

总体感觉面向对象的思路容易实现些,就用笨办法去构造,同时也较好扩展,算法如果想不到的话就卡在那儿没办法了。如果想到妙的算法,当然效率会非常好。有些问题如果找到计算公式之类,可能一步直接得到答案,那效率就是别的方法没办法比的了。然而算法的局限性也比较明显,会只能针对某个问题,稍加变动,可能就要想别的办法解决新的问题了。这方面面向对象的构造方式通常可以解决某一类的问题,较有优势。

综上所述,还是建议初学者先学会笨办法面向对象的思维方式,以后再提高算法方面的能力,这样二者结合后威力会比较大。
[该贴被wind13于2007年04月11日 23:25修改过]

在smalltalk中,连1+1=2也是用对象与消息实现,没人觉得这就是笨办法。

smalltalk是纯粹的面向对象的,可惜java不是,呵呵,是不是又说我贬低java了那??

如果你喜欢历史那就研究一下java的历史,我原来的帖子谈到过java是由于applet出名的,那么现在你又研究过java因为什么而推广起来的那??答案是java JIT编译器的竞赛,一开始的java编译器速度很慢,根本不能满足人们的需求,只能运行一些小的程序,borland公司为了进入java这个领域,首先进攻的就是JIT领域,当时borland研究的编译器从编译速度到编译出来的bytecode比java本身的要快数倍,一举奠定了borland在java届的地位,然后引发了,borland、symantec、ibm的编译器大赛,从而提高了java的运行速度,java才开始大规模的使用,当然我说的不够精彩,你可以看看李维的《borland传奇》。那么这个改进是什么改进那??当然是算法的改进。从根上而言,这种语言的竞争也是速度的竞争,特性之争。难以想象如果java的速度依然是十年前的水平,java是否还能象今天一样火热。c++的战争是如此,java的战争是如此,以后每一种新的语言的竞争也是如此,更加简单,执行更加高效是发展的必然。

说了这些就是想说明算法的重要,可能你觉得算法用处不大,可惜你永远摆脱不了它的影响,只要你还是程序员,你早晚会遇见它。你可以不重视它,因为现在别人替你做好了它。但是你永远只能吃别人做的饭。