存档

2007年3月 的存档

想去当网管

2007年3月30日 1 条评论

最近在学Linux下的服务器配置,朋友于墨的实验室需要一个网管,于墨让我去和他竞争。我准备去试试。毕竟,实践出真知。只是我对网络的了解不是很深入,怕做不好。但无论如何,试试再说吧。

今天成功解决了我的Linux虚拟机无法上网的问题,算是一个好的开始吧。继续努力中。。。

分类: 一点心情 标签: , ,

二百五定律,我们都是二百五…

2007年3月29日 没有评论
  • 250定律

    拉德认为:每一位顾客身后,大体有250名亲朋好友。如果您赢得了一位顾客的好感,就意味着赢得了250个人的好感;反之,如果你得罪了一名顾客,也就意味着得罪了250 名顾客。 在你的网站访客中,一个访客可能可以带来一群访客,任何网站都有起步和发展的过程,这个过程中此定律尤其重要。

  • 达维多定律

    达维多认为,一个企业要想在市场上总是占据主导地位,那么就要做到第一个开发出新产品,又第一个淘汰自己的老产品。 国内网站跟风太严重,比如前段时间的格子网,乞讨网,博客网,一个成功了,大家一拥而上。但实际效果是,第一个出名的往往最成功,所以在网站的定位上,要动自己的脑筋,不是去捡人家剩下的客户。同理,买人家出售的数据来建站效果是很糟糕的。

  • 木桶定律

    水桶定律是指,一只水桶能装多少水,完全取决于它最短的那块木板。这就是说任何一个组织都可能面临的一个共同问题,即构成组织的各个部分往往决定了整个组织的水平。 注意审视自己的网站,是速度最糟糕?美工最糟糕?宣传最糟糕?你首先要做的,不是改进你最强的,而应该是你最薄弱的。

  • 马太效应

    《新约》中有这样一个故事,一个国王远行前,交给三个仆人每人一锭银子,吩咐他们:“你们去做生意,等我回来时,再来见我。”国王回来时,第一个仆人说: “主人,你交给我们的一锭银子,我已赚了10锭。”于是国王奖励他10座城邑。第二个仆人报告说:“主人,你给我的一锭银子,我已赚了5锭。” 于是国王例奖励了他5座城邑。第三个仆人报告说:“主人,你给我的一锭银子,我一直包在手巾里存着,我怕丢失,一直没有拿出来。”于是国王命令将第三个仆人的一锭银子也赏给第一个仆人,并且说:“凡是少的,就连他所有的也要夺过来。凡是多的,还要给他,叫他多多益善。”这就是马太效应。 在同类网站中,马太效应是很明显的。一个出名的社区,比一个新建的社区,更容易吸引到新客户。启示是,如果你无法把网站做大,那么你要做专。作专之后再做大就更容易。

  • 手表定理

    手表定理是指一个人有一只表时,可以知道现在是几点钟,而当他同时拥有两只表时却无法确定。
    一个网站,你只需要关注你特定的用户群需求。不要在意不相干人的看法。

  • 不值得定律

    不值得定律:不值得做的事情,就不值得做好 不要过度seo,如果你不是想只做垃圾站。不要把时间浪费在美化再美化页面,优化再优化程序,在你网站能盈利后,这些事情可以交给技术人员完成。

  • 彼得原理

    劳伦斯.彼得认为:在各种组织中,由于习惯于对在某个等级上称职的人员进行晋升提拔,因而雇员总是趋向于晋升到其不称职的地位。
    不要轻易改变自己网站的定位。如博客网想变门户,盛大想做娱乐,大家拭目以待吧。

  • 零和游戏原理

    当你看到两位对弈者时,你就可以说他们正在玩“零和游戏”。因为在大多数情况下, 总会有一个赢,一个输,如果我们把获胜计算为得1分,而输棋为-1分,那么,这两人得分之和就是:1+(-1)=0 不要把目光一直盯在你的竞争网站上,不要花太多时间抢它的访客。我们把这些时间用来寻找互补的合作网站,挖掘新访客。

  • 华盛顿合作规律

    华盛顿合作规律说的是: 一个人敷衍了事,两个人互相推诿, 三个人则永无成事之日。 如果你看准一个方向,你自己干,缺人手就招。不要轻易找同伴一起搞网站,否则你会发现,日子似乎越过越快了,事情越做越慢了。

  • 邦尼人力定律

    一个人一分钟可以挖一个洞,六十个人一秒种却挖不了一个洞。合作是一个问题,如何合作也是一个问题。你需要有计划。

  • 牛蛙效应

    把一只牛蛙放在开水锅里,牛蛙会很快跳出来;但当你把它放在冷水里,它不会跳出来,然后慢慢加热,起初牛蛙出于懒惰,不会有什么动作,当水温高到它无法忍受的时候,想出来,但已经没有了力气。 如果你是soho,注意关注你的财务。不要等到没钱了再想怎么挣,你会发现那时候挣钱更难。

  • 蘑菇管理

    蘑菇管理是许多组织对待初出茅庐者的一种管理方法,初学者被置于阴暗的角落(不受重视的部门,或打杂跑腿的工作),浇上一头大粪(无端的批评、指责、代人受过),任其自生自灭(得不到必要的指导和提携)。
    做网站毕竟要遭遇这样的阶段,搜索引擎不理你,友情链接找不到,访客不上门。这是磨练。

  • 奥卡姆剃刀定律

    如无必要,勿增实体。
    把网站做得简单,再简单,简单到非常实用,而不是花俏。google的首页为什么比雅虎好?

  • 巴莱多定律(Paredo 也叫二八定律)

    你所完成的工作里80%的成果,来自于你20%的付出;而80%的付出,只换来20%的成果。
    随时衡量你所做的工作,哪些是最有效果的。

  • 马蝇效应
    林肯少年时和他的兄弟在肯塔基老家的一个农场里犁玉米地,林肯吆马,他兄弟扶犁,而那匹马很懒,慢慢腾腾,走走停停。可是有一段时间马走得飞快。 林肯感到奇怪,到了地头,他发现有一只很大的马蝇叮在马身上,他就把马蝇打落了。看到马蝇被打落了,他兄弟就抱怨说:”哎呀,你为什么要打掉它,正是那家伙使马跑起来的嘛!” 在你心满意足的时候,去寻找你的马蝇。没有firefox,不会有ie7,firefox就是微软的马蝇之一。马蝇不可怕,怕的是会一口吃掉你的东西,像 ie当初对网景干的那样。
  • 最高气温效应
    每天最热总是下午2 时左右,我们总认为这个时候太阳最厉害,其实这时的太阳早已偏西,不再是供给最大热量的时候了。此时气温之所以最高,不过是源于此前的热量积累。
    你今天的网站流量,是你一个星期或更长时间前所做的事带来的。
  • 超限效应(溢出效应)
    刺激过多、过强和作用时间过久而引起心理极不耐烦或反抗的心理现象,称之为“超限效应”。 别到别人论坛里发太多广告。别在自己网站上放太多广告。别在自己的论坛里太多地太明显地诱导话题。
  • 懒蚂蚁效应
    生物学家研究发现,成群的蚂蚁中,大部分蚂蚁很勤劳,寻找、搬运食物争先恐后,少数蚂蚁却东张西望不干活。当食物来源断绝或蚁窝被破坏时,那些勤快的蚂蚁一筹莫展。“懒蚂蚁”则“挺身而出”,带领众伙伴向它早已侦察到的新的食物源转移。 不要把注意力仅仅放在一个网站上,即使这个网站现在为你带来一切。你要给自己一些时间寻找新的可行的方向,以备万一。
  • 长尾理论
    ChrisAnderson认为,只要存储和流通的渠道足够大,需求不旺或销量不佳的产品共同占据的市场份额就可以和那些数量不多的热卖品所占据的市场份额相匹敌甚至更大。 对于搜索引擎,未必你需要一个热门词排在第一位,如果有一千个冷门词排在第一位,效果不但一样,还会更稳定更长远。
  • 破窗理论
    栋建筑上的一块玻璃,又没有及时修好,别人就可能受到某些暗示性的纵容,去打碎更多的玻璃。 管理论坛时,如果你发现第一个垃圾贴,赶紧删掉他吧。想想:落伍现在为什么那么多××贴?现在控制比最初控制难多了。
  • “羊群效应”,又称复制原则(Copy Strategy)
    一个羊群(集体)是一个很散乱的组织,平时大家在一起盲目地左冲右撞。如果一头羊发现了一片肥沃的绿草地,并在那里吃到了新鲜的青草,后来的羊群就会一哄而上,争抢那里的青草,全然不顾旁边虎视眈眈的狼,或者看不到其它地方还有更好的青草。
    不要轻易跟风,保持自己思考的能力。
  • 墨菲定律
    如果坏事情有可能发生,不管这种可能性多么小,它总会发生,并引起最大可能的损失。
    除非垃圾站,否则不要作弊,对搜索引擎不要,对广告也不要。
  • 光环效应
    人们对人的某种品质或特点有清晰的知觉,印象比较深刻、突出, 这种强烈的知觉, 就像月晕形式的光环一样,向周围弥漫、扩散,掩盖了对这个人的其他品质或特点的认识。
    不要轻易崇拜一个人或者公司、一个概念、一种做法。
  • 蝴蝶效应
    一只亚马逊河流域热带雨林中的蝴蝶,偶尔扇动几下翅膀,两周后,可能在美国德克萨斯州引起一场龙卷风。
    不管你做什么,网站或者其他,你都应该关注新闻。机遇或者灾难可能就在那。
  • 阿尔巴德定理
    一个企业经营成功与否,全靠对顾客的要求了解到什么程度。 我赞同别人的点评:看到了别人的需要,你就成功了一半;满足了别人的需求,你就成功了全部。尤其是做网站。
  • 史密斯原则
    如果你不能战胜他们,你就加入到他们之中去。
    不要试图做孤胆英雄。如果潮流挡不住,至少,你要去思考为什么。

文章来自: Blueidea.com

递归的力量(四):递归效率

2007年3月28日 没有评论

前几天我们在讨论递归的过程中发现,递归在解决某些问题的时候使得我们思考的方式得以简化,代码也更加精炼,容易阅读。那么既然递归有这么多的优点,我们是不是什么问题都要用递归来解决呢?难道递归就没有缺点吗?今天我们就来讨论一下递归的不足之处。

我们知道,递归调用实际上是函数自己在调用自己,而函数的调用开销是很大的,系统要为每次函数调用分配存储空间,并将调用点压栈予以记录。而在函数调用结束后,还要释放空间,弹栈恢复断点。所以说,函数调用不仅浪费空间,还浪费时间。

这样,我们发现,同一个问题,如果递归解决方案的复杂度不明显优于其它解决方案的话,那么使用递归是不划算的。因为它的很多时间浪费在对函数调用的处理上。在C++中引入了内联函数的概念,其实就是为了避免简单函数内部语句的执行时间小于函数调用的时间而造成效率降低的情况出现。在这里也是一个道理,如果过多的时间用于了函数调用的处理,那么效率显然高不起来。

举例来说,对于求阶乘的函数来说,其迭代算法的时间复杂度为O(n):

  1. int fact(n){
  2.      int i;
  3.      int r = 1;
  4.      for(i = 1; i <= n; i++){
  5.           r *= i;
  6.      }
  7.      return r;
  8. }

而其递归函数的时间复杂度也是O(n):

  1. int fact_r(n){
  2.     if(n == 0)
  3.          return 1;
  4.     else
  5.          return n * f(n);
  6. }

但是递归算法要进行n次函数调用,而迭代算法则只需要进行n次迭代而已。其效率上的差异是很显著的。

我们再来看看之前我们讨论的费波纳契数列问题。

我们当时使用的是简单的用定义来求 fib(n),也就是使用公式 fib(n) = fib(n-1) + fib(n-2)。这样的想法是很容易想到的,可是仔细分析一下我们发现,当调用fib(n-1)的时候,还要调用fib(n-2),也就是说fib(n-2)调用了两次,同样的道理,调用f(n-2)时f(n-3)也调用了两次,而这些冗余的调用是完全没有必要的。可以计算这个算法的复杂度是指数级的。

那么计算费波纳契数列是否有更好的递归算法呢? 当然有。让我们来观察一下费波纳契数列的前几项:

1, 1, 2, 3, 5, 8, 13, 21, 34, 55 …

注意到没有,如果我们去掉前面一项,得到的数列依然满足f(n) = f(n-1) – f(n-2), (n>2),而我们得到的数列是以1,2开头的。很容易发现这个数列的第n-1项就是原数列的第n项。怎么样,知道我们该怎么设计算法了吧?我们可以写这样的一个函数,它接受三个参数,前两个是数列的开头两项,第三个是我们想求的以前两个参数开头的数列的第几项。

  1. int fib_i(int a, int b, int n);

在函数内部我们先检查n的值,如果n为3则我们只需返回a+b即可,这是简单情境。如果n>3,那么我们就调用f(b, a+b, n-1),这样我们就缩小了问题的规模(从求第n项变成求第n-1项)。好了,最终代码如下:

  1. int fib_i(int a, int b , int n){
  2.     if(n == 3)
  3.          return a+b;
  4.     else
  5.          return fib_i(b, a+b, n-1);
  6. }

这样得到的算法复杂度是O(n)的。已经是线性的了。它的效率已经可以与迭代算法的效率相比了,但由于还是要反复的进行函数调用,还是不够经济。

由以上分析我们可以看到,递归在处理问题时要反复调用函数,这增大了它的空间和时间开销,所以在使用迭代可以很容易解决的问题中,使用递归虽然可以简化思维过程,但效率上并不合算。效率和开销问题是递归最大的缺点。

虽然有这样的缺点,但是递归的力量仍然是巨大而不可忽视的,因为有些问题使用迭代算法是很难甚至无法解决的(比如汉诺塔问题)。这时递归的作用就显示出来了。

递归的效率问题暂时讨论到这里。

备案成功了

2007年3月27日 4 条评论

今天收到了ICP备案申请审核通过的邮件。

我的备案申请是过年之前提交的,现在大概还不到两个月吧。还行,速度可以,不过对于期待中的我来说还是嫌慢啊。

好了,现在这个小站也算是有了身份了(当然,也要受人管制了)。不再是混子黑户了。那些发垃圾评论的注意了,共产党罩着我呢!小心着点!

金庸小说人物卜

2007年3月23日 3 条评论

一个挺有意思的游戏。能测出你在金庸小说中的角色。
http://ceshi.nilaicha.com/test/jinyongqunxia.htm

下面是我的测试结果(用阴历算的):

你在金庸小说中是:令狐冲

令狐冲的一生,前半生风平浪静,当他的华山派大师兄,但是后半生却惊涛骇浪,几乎没在江湖风浪之中淹死,好几次险死还生,居然得以不死,当其尼姑头,做其三山五岳人物的盟主,任性而为,一副什么都不在乎的气概,又是金庸创造出来的另一种活龙活现的豪杰人物,而和杨过、乔峰等又截然不同。令狐冲的际遇,是“置之死地而后生”,几次皆是如此,以为万无生理,率性豁出去,结果却又峰回路转,柳暗花明,又是一番境界。他自认必死,义助向问天,是死而复生;被囚西湖底,忽然又学会了吸星大法,是死而复生;苦恋岳灵珊不遂,忽然又有任盈盈,也是死而复生。令狐冲苦恋岳灵珊,而结果岳灵珊爱上了林平之,这是由于岳灵珊和令狐冲一起长大之故,大凡青梅竹马的男女,恋爱很少会有结果,因为大家一起长大,相处太久,双方之间就失去了神秘之感,一有第三者介入,吸引就会转移到第三者上面去。在武侠小说之中,男主角几乎全部在恋爱上无往而不利,象令狐冲那样,居然失恋,可说绝无仅有。令狐冲性格最可爱之处是在于不羁,名门正派的戒律,能使他的思想上有一定的约束,但是在行为上,他却处处在突破这种约束。在自然而然之中,流露他的真性情。他不执著,不在乎,潇洒浪漫之处,在金庸笔下所有男主角之上,允称第一。这一点,连任我行这样聪明绝顶的人都未曾看出来。任我行第一次邀令狐冲入朝阳神教,令狐冲拒绝,任我行就不知道令狐冲是真的不想加入,心口如一。任我行还以为令狐冲是嫌他地位不稳。所以才有后来夺了教主之位,再邀他入教之争发生。而令狐冲仍然不肯,终任我行一世,无法了解令狐冲何以不肯,这两人性格截然不同之故,任我行热衷,令狐冲淡薄,近乎神仙中人。令狐冲的这种什么都不放在心上的性情,和他的遭遇也有一点关系,他先是失恋,继而身中奇毒,朝不保夕,自然容易使他看得开。但主要还是天性使然,别的人有这样的遭遇,一定痛不欲生,整日里愁眉苦脸,哪里还会有这样的洒脱!令狐冲接近神仙境界,是绝顶人物。

用阳历算的:

你在金庸小说中是:康熙

中国历史上有数的好皇帝之一,玄烨大帝,在《鹿鼎记》中,是一个极其出色有人物。金庸自他初接拉时写起,一直写到他的中年。大约二十年的时间,是康熙当政以来,日子惊涛骇浪的二十年。虽然是小说家言,但这个皇帝的英明、决断、处理政务的能力,用人之明,态度之速,对统治理论的精娴,实在令人拍案叫绝。从来也没有一本武侠小说、历史小说、文艺小说之中,将一个皇帝写得如此生动、成功过,也从来没有一部小说,将这样英明的一个皇帝,和一个百分之百的无赖人物纠缠在一起,对比如此之强烈,而又安排得如此之融洽过。康熙是上上人物。

博客写作近期计划

2007年3月23日 4 条评论

WordPress主题教程的翻译工作仍在继续,不过不是我自己了, 我将和我的同学丁梦为一起进行翻译。另外我准备暂时放放面向对象的学习,先好好学习一下数学、数据结构与算法和Linux操作系统的知识。所以面向对象的写作在最近不会太多。

最近接了个做网站的活。时间有限,所以不得不让同学来帮我翻译。另外欢迎对我最近的写作主题感兴趣的朋友常来,我们可以一起讨论。

PS:昨天不好的心情今天好了起来。信心坚定了,所以前途看起来也光明了。

分类: 一点心情 标签: ,

无病呻吟一下

2007年3月22日 没有评论

今天心情不好,很莫名其妙,说不上是什么感觉。所以先不更新了。请大家原谅。

人活着挺累的,尤其像我这样虚伪的活着就更累了。都说要活就活个洒脱,可是我就是洒脱不起来。很久没有心事了。现在有了。但这时的心事已经与以往的年少的轻狂憧憬变成了现实的退让妥协。味道变了。

我花了很长时间去学着做个像样的男人,我学的很卖力,可是今天我才发现,原来我一无所成。说到头,我还是不够成熟,这才是问题的所在。

不愿去想,却逃不开。

很酸是吗?我也讨厌无病呻吟,可是我好像真的病了。请原谅。

递归的力量(三):更多例子

2007年3月21日 4 条评论

昨天说了一些递归的简单应用,今天再说一点其他的应用吧。

不得不提的一个经典的问题是汉诺塔问题。问题是这样的:

有三个柱子,其中的一个上面套着八个圆盘,从下到上,圆盘按从大到小的顺序排列。要求把所有圆盘移动到另一个柱子上,并遵循以下规则:

  • 一次只能移动一个圆盘
  • 圆盘只能套在柱子上
  • 小圆盘只能放在比它大的圆盘上

要解决这个问题,用普通的方法来作实在是很困难。而用递归来解决呢? 会不会有一个容易的解决方案呢?

还是考虑递归的两个条件,是否可把问题规模缩小,是否存在简单情境。为了便于讨论,我们把圆盘现在所在的柱子称为源,把圆盘要移动到的柱子成为目的,另外一个柱子称为辅助。我们容易发现,要把最大的圆盘移动到目的,首先要把上面较小的七个圆盘移动到辅助上(注意,这是一个与原问题有着相同的形式且规模更小的问题),然后把大圆盘移动到目的上,在把七个较小圆盘从辅助移动到目的。可以看到,移动七个小圆盘其实与原问题形式相同而规模更小。好了,我们已经成功地缩小了问题规模。下面来找找简单情境。

很容易看到,当只有一个圆盘时,只要把它从源移动到目的就行了。这就是简单情境。

好,两个条件都具备了,我们可以开始写程序了。

  1. void move_single_disk(char source, char target){
  2.     printf("%c --> %cn", source, target);
  3. }
  4. void move_hanoi_tower(char source, char target, char tmp, int n){
  5.     if(n == 1)
  6.           move_single_disk(source, target);
  7.     else{
  8.           move_hanoi_tower(source, tmp , target, n - 1);
  9.           move_single_disk(source, target);
  10.           move_hanoi_tower(tmp, target, source, n - 1)
  11.     }
  12. }

以上就是实现输出汉诺塔问题解的主要函数了。你可以编写一个主程序来调用它们。像这样:

  1. int main(){
  2.     move_hanoi_tower('A', 'B', 'C', 8);
  3.     return 0;
  4. }

不错吧,很简洁的,几行代码,就解决了这个问题。是不是很爽呢?不过这个递归调用的算法的复杂性如何呢?让我们来看看。

记规模为n的输入耗时T(n),则:
[tex]T(1) = 1[/tex]
[tex]T(n) = 2*T(n-1) + 1[/tex]
[tex]T(n) + 1= 2*(T(n-1) + 1)[/tex]
[tex]T(n) + 1 = (T(1) + 1) * ( 1 – (T(1)+1)^n) / ( 1 – (T(1)+1))[/tex]
[tex]T(n) = 2^n – 1[/tex]

啊,指数级,好像效率不太高啊。但确实解决了问题。

这是一个很经典的例子,我们来给它改个版,留为今天的作业吧:

所有要求不变,添加如下要求:
圆盘不能从源直接移动到目的。
如何解决此问题?复杂度是多少?

递归的力量(二)

2007年3月20日 没有评论

昨天大体上说了一下递归的根本思想和使用递归解决问题的两个条件。今天来看看在解决具体问题的时候如何使用递归。

探测回文:

回文是一种字符串,它正着读和反着读都是一样的。比如level,eye都是回文。用迭代的方法可以很快地判断一个字符串是否为回文。用递归的方法如何来实现呢?

首先我们要考虑使用递归的两个条件:
第一:这个问题是否可以分解为形式相同但规模更小的问题?
第二:如果存在这样一种分解,那么这种分解是否存在一种简单情境?

先来看第一点,是否存在一种符合条件的分解。容易发现,如果一个字符串是回文,那么在它的内部一定存在着更小的回文。 比如level里面的eve也是回文。 而且,我们注意到,一个回文的第一个字符和最后一个字符一定是相同的。所以我们很自然的有这样的方法:先判断给定字符串的首尾字符是否相等,若相等,则判断去掉首尾字符后的字符串是否为回文,若不相等,则该字符串不是回文。 注意,我们已经成功地把问题的规模缩小了,去掉首尾字符的字符串当然比原字符串小。

接着再来看第二点, 这种分解是否存在一种简单情境呢?简单情境在使用递归的时候是必须的,否则你的递归程序可能会进入无止境的调用。对于回文问题,我们容易发现,一个只有一个字符的字符串一定是回文,所以,只有一个字符是一个简单情境,但它不是唯一的简单情境,因为空字符串也是回文。这样,我们就得到了回文问题的两个简单情境:字符数为1和字符数为0。

好了,两个条件都满足了,基于以上分析,我们可以很容易的编写出解决回文问题的递归实现方式:

  1. int is_palindereme(char * str, int n){
  2.     if(n == 0 || n == 1)
  3.         return 1;
  4.     else{
  5.         return ((str[0] == str[n-1]) ? is_palindereme(str+1, n-1) : 0);
  6. }
  7. }

看起来很不错嘛。只有简单的几行代码,非常的简短,就解决了问题。

还有一个典型的例子是对已排序数组的二分查找算法

现在有一个已经排序好的数组,要在这个数组中查找一个元素,以确定它是否在这个数组中,很一般的想法是顺序检查每个元素,看它是否与待查找元素相同。这个方法很容易想到,但它的效率不能让人满意,它的复杂度是O(n)的。现在我们来看看递归在这里能不能更有效。

还是考虑上面的两个条件:
第一:这个问题是否可以分解为形式相同但规模更小的问题?
第二:如果存在这样一种分解,那么这种分解是否存在一种简单情境?

考虑条件一:我们可以这样想,如果想把问题的规模缩小,我们应该做什么?可以的做法是:我们先确定数组中的某些元素与待查元素不同,然后再在剩下的元素中查找,这样就缩小了问题的规模。那么如何确定数组中的某些元素与待查元素不同呢? 考虑到我们的数组是已经排序的,我们可以通过比较数组的中值元素和待查元素来确定待查元素是在数组的前半段还是后半段。这样我们就得到了一种把问题规模缩小的方法。

接着考虑条件二:简单情境是什么呢? 容易发现,如果中值元素和待查元素相等,就可以确定待查元素是否在数组中了,这是一种简单情境,那么它是不是唯一的简单情境呢? 考虑元素始终不与中值元素相等,那么我们最终可能得到了一个无法再分的小规模的数组,它只有一个元素,那么我们就可以通过比较这个元素和待查元素来确定最后的结果。这也是一种简单情境。

好了,基于以上的分析,我们发现这个问题可以用递归来解决,二分法的代码如下:

  1. int binary_search(int * a, int n, int key){
  2.     int mid;
  3.     if(n == 1){
  4.         return (a[0] == key);
  5.     }else{
  6.         mid = n/2;
  7.     if(a[mid-1] == key)
  8.         return 1;
  9.     else if(a[mid-1] > key)
  10.         return binary_search(a, mid, key);
  11.     else
  12.         return binary_search(&a[mid], n - mid, key);
  13. }
  14. }

这个算法的复杂度是O(logn)的,显然要优于先前提到的朴素的顺序查找法。

好了,今天就说到这里,光看不管用,不如留个练习题您回去做做:

写一个函数dig_sum(int n),输入一个非负整数,返回组成它的数字之和。如:dig_sum(2007) = 2+0+0+7 = 9。

递归的力量(一)

2007年3月19日 没有评论

学C语言的时候老师说递归挺难的,但也是很好用的,同时还是一个合格的程序员必须要掌握的。

从学编程以来对递归的理解就不太深刻。一直就停留在“自己调用自己”的程度上。这其实这只是递归的表象(严格来说连表象都概括得不全面,因为除了“自己调用自己”的递归外,还有交互调用的递归)。而递归的思想远不止这么简单。前些日子看了一本叫《程序设计抽象思想》的书,里面很详细的论述了递归的思想。看后令我颇受启发。所以,我开始写这个关于递归的系列,希望能与同道们一同探讨一下递归的力量

递归,并不是简单的“自己调用自己”,也不是简单的“交互调用”。它是一种分析和解决问题的方法和思想。简单来说,递归的思想就是:把问题分解成为规模更小的、具有与原问题有着相同解法的问题。比如二分查找算法,就是不断地把问题的规模变小(变成原问题的一半),而新问题与原问题有着相同的解法。

有些问题使用传统的迭代算法是很难求解甚至无解的,而使用递归却可以很容易的解决。比如汉诺塔问题。但递归的使用也是有它的劣势的,因为它要进行多层函数调用,所以会消耗很多堆栈空间和函数调用时间。

既然递归的思想是把问题分解成为规模更小且与原问题有着相同解法的问题,那么是不是这样的问题都能用递归来解决呢?答案是否定的。并不是所有问题都能用递归来解决。那么什么样的问题可以用递归来解决呢?一般来讲,能用递归来解决的问题必须满足两个条件:

  • 可以通过递归调用来缩小问题规模,且新问题与原问题有着相同的形式
  • 存在一种简单情境,可以使递归在简单情境下退出

如果一个问题不满足以上两个条件,那么它就不能用递归来解决。

举个例子:

费波纳契数列的第N项的值。

这是一个经典的问题,说到递归一定要提到这个问题。费波纳契数列这样定义:
f(0) = 0, f(1) = 1, 对n > 1, f(n) = f(n-1) + f(n-2)
这是一个明显的可以用递归解决的问题。让我们来看看它是如何满足递归的两个条件的:
1、对于一个n>2, 求f(n)只需求出f(n-1)和f(n-2),也就是说规模为n的问题,转化成了规模更小的问题;
2、对于n=0和n=1,存在着简单情境:f(0) = 0, f(1) = 1。

因此,我们可以很容易的写出计算费波纳契数列的第n项的递归程序:

[c]int fib(n){
if(n == 0)
return 0;
else if(n == 1)
return 1;
else
return f(n-1) + f(n-2);
}[/c]

注意:在编写递归调用的函数的时候,一定要把对简单情境的判断写在最前面,以保证函数调用在检查到简单情境的时候能够及时地中止递归,否则,你的函数可能会永不停息的在那里递归调用,你就成了造出第一个永动机的人了。

(未完待续

Page optimized by WP Minify WordPress Plugin