《算法导论》chapter1/2

来源:互联网 发布:ai矢量图软件免费下载 编辑:程序博客网 时间:2024/05/17 05:07

preface:学习很久,将自己的一些体会与学习记录心得写出来容易以后翻阅。

①.算法前言
这种类似玄学的描述看上去是垃圾,可是蕴含的思想其实是最朴素最深刻的,摘抄几句。

算法algorithm词源来自于波斯数学家的名字,音吹思婷。
算法需要 确定性 有穷性 有效性。但是其实看来正确性也是充分的,却不是必要的,因为只要错误率可控的话也是能用的。
我们应当把算法当作一种技术来看待,就和硬件一样实在,追求一个效率与效益的均衡。
难题np问题诱人在于所有的np/npc都是同构的。(将在其他专题里研究)

②.算法基础
涉及基础排序算法,其实写代码的时候就应该如此,学着如何写伪代码,这样可以自己去理解代码本身的意思与思想就能够举一反三。

1.插入排序
这是一个原始非递归的版本。

void insertion_Sort(int *A, int n){     int i=0, j=0;       int key=0;      for(i=1; i<n; ++i){        key=A[i];        j=i-1;        while(A[j]>key && j>=0){            A[j+1]=A[j];            --j;            }            A[j+1]=key;        }}

思想很朴素就和玩牌一样,假定前面的牌都是有序的,新抽上来的与前面有序的对比以后插到合适的位置。比较难理解的其实是这个A[j+1]=key;因为循环变量是在退出while循环之前更新的,而且这里使用了哑值,所以说初始化变量的时候最好给值是好习惯,至少c++是这样。

思考形式发现这也是一个典型的递归,要排好A[i]之前的数组,必须排好A[i-1]的数组。所以可以递归,算法有三个条件,初始,保持,终止。递归也有三个,分解,回升,合并或者说完结。将一个极小子问题向上合并得到。其实最关键的是——什么时候算是极小子问题(具体可以到第四章再研究),就这个例子来讲,长度比是1的时候就是最短了,于是:

void Insertion_sort(int* A,int n)  {      int i=0;    int key=0;      if(n>1)        Insertion_sort(A,n-1);      i=n-2;    key=A[n-1];      while(A[i]>key && i>=0){           A[i+1] = A[i];          --i;          }         A[i+1]=key;  } 

这个递归版本可谓是非常糟糕,之前写的,只是用递归取代了一层for迭代。至于先排序还是先递归见仁见智。也求教大神写出漂亮的代码来帮助我解惑。

思想总归是朴素的,递归就是将问题分解为小的但是相似的问题,每次递归的作用就是减小一点(至于这一点是多少很难说),所以自然而然的想到当递归还没有触底的时候,就用一个简单的而且形式上完全相似的语句减小规模,当触底以后开始回升,回升的代码就是假设在一个已知的i上操作,写思维的导图是写算法理解算法一个非常好的工具。
虽然对算法很着迷,但是这种东西很严谨,稍微一点错误就会导致完全错误,逻辑是第一要义。

2.关于循环不变式
只要是体会过数学归纳法思想的人都很容易了解循环不变式的强大,因为不必麻烦与细节当中我们就可以了解到一个算法很远很远之处是个什么样子,三点,初始化,保持,终止。永远不要低估终止的威力,平凡的看来,是一个循环控制变量在整数范围内或一个自然数子集内的迭代,其实在实数范围也不是不可以,广义上的在此不做讨论,后更。分析算法是一项重要的技能,一定不是鸡肋,而这本书主采取的是面向过程的方法。
注意短路求值,这在逻辑式当中很常见,多种语言也支持。

3.算法分析
意味着预测资源,目前简单算法都认为存储是无限的也就是说空间复杂度不是要思考的东西,反而时间复杂度上有着比较有价值的考量。
提供一种RAM机的思想模型,每条“合理的”基本指令消耗同样的资源,计算机组成中的应该介绍的很清楚,算数,移动,控制,之类的基本。
精度是一个严肃的话题,不过应该具备一种思想,一切以现实应用为主导,要什么给什么,如果8bit足以描述,16bit绝对是浪费,空间不是主要矛盾,却也是固有矛盾。
由于机器二进制的特性2的幂可以通过左移右移实现,很巧。

4.例子——插入算法分析
打游戏的时候很强调一个自由度,自由度很高很有诱惑力,但是算法的自由度可谓是太高了,只能描述其冰山一角,不同的初始条件产生不同的过程,甚至结果。要考虑最早的有序程度,但是对于一个平均的情况我们关心的运行时间与输入项数密切相关。这其实引入了后续要说明的渐进分析的内容。
最普遍有效的也是用输入规模去描述时间的长短。
算法运行的总时间是所有语句运行时间之和,所以比较头痛的是循环的分析,特别是循环嵌套的内容。书上说的很好,代价与次数,求一个积和式。
以非递归的为例,while之前for之后的语句都是执行n级别的(准确为n与n-1,其中注意for测试的是n次,for迭代的内容则是n-1次,满满的细节)而while内语句的内容书中用一个带下标的未知量求和代替,要明确的是未知量是每次while要执行的次数,而求和则是for中的要求。如果是完全排好序的,while测试条件不会满足,这也就是说未知量取1够了,也就是测试的那句,这样一来全部都是至多n级别的,时间也是一个线性函数的样子。
而当是完全倒序的时候就是最坏情况了,计算机是很笨的思维,与人不同全局观很差,人一眼可以扫射,但计算机只能看到下面的一步,于是在每次while测试中的语句就会以n-1开始到1的递减求和,这也是初中等差数列知识,很容易知道会产生二次项。物理上会忽略高阶小量,高等数学则是无穷远处的大量支配小量而忽略,书中提到了主方法,于是就是个二次项复杂度的时间。
至于其他的夹在二者之间,这是在数学中最愿意看到的情况,夹逼。省略了很多复杂的细节。
递归版本的分析要有一个递归方程,求解这个方程其实和递归的数列类似,后续版本会有。

5.最坏情况与最好情况及平均情况
最坏情况的意义在于一个上界。
最好情况的意义在于一个下界。
平均情况的意义在于一个合理性。
平均的时候虽然说很诱人因为合理,但是如何去量化这个平均就显得很难,公公婆婆各说有理咯。总不能一条算数平均走到黑吧。
最好情况则是鸡肋,食之无味,弃之可惜。如果稍微修改算法很容易在完全排序的情况下达到常数级别的,但这并没有什么卵用。

6.设计一个算法
书中主要介绍了归并排序,这其实也是分治的一个思想体现,第四章统一描述。

今天就到这里吧。

1 0
原创粉丝点击