要说的话好多,列个提纲先
TAOCP初读感受
《The Art of Computer Programming》的第一卷,大理石花纹的封皮,拿在手里沉甸甸的,这部书给我的第一印象就是这样--"厚重"--带有着神秘感和历史感。
其实这部书的中文版前言,我早就有幸拜读过,不过和英文原文相比较,在中文翻译的味道真的是差了很多,我觉得只有读原文才能感到Knuth略带诙谐的而又同是不是严谨的风格,他写文章的风格其实真的挺天马行空的,从写程序扯到做饭,从算法这个词聊起,追着这个词的来历,竟然还带出了莱布尼茨?真晕,开句玩笑,Knuth绝对是那种老顽童型的人物,他这本书达到如此厚度估计此类"废话"功不可没。
从Algorithm到Euclid's Algorithm也就是我们熟悉的辗转相除求最大公约数法,我这个算法小白开始进入了他打开的算法世界......
Knuth行文很喜欢比较、比喻、对比,这让读者看起来很轻松愉悦,不过当他真的玩起数学来,我就有点吃不消了,最后面对算法的一个形式化描述,消耗了我不少精力,不过目前看来还是大致明白了
总之,这本盛名之下的书,也的确有很多独到的地方,作为计算机科学领域的史诗,它给我的第一印象的确很棒。希望我能坚持着看下去,从中吸收营养。
今天的收获
虽然只看了一节,不过也消耗了我不少的时间和精力(看来别的一些事情也不能太耽误,也要抓紧了)
今天的收获很多,首先对算法这个名词有了更多一些的感性认识,Knuth提出的“有限、明确定义、有输入、有输出、有效率”这几个原则总结得真是不错,尤其最前面的两点和效率问题,往往构成了很多复杂的问题,著名的图灵机停机问题大概就是在说这个问题吧……
另外对于辗转相除法的一些数学上的推导也给了我不错的感觉,虽然书上没有明确的给一个严格的证明,但是根据他的叙述我马上就体会到了用比较严格的方法如何写这个证明,以及这个证明的关键点(我觉得证明中其实用到了通过双包含来争相等的手法,这个是关键)
算法的形式化描述应起了我大的兴趣,回来的路上想,貌似这个好像形成了某种数学结构,而其上的f映射,构成了某种代数结构,没有仔细想过,不过好像是这样子的哦,我觉得貌似算法的本质就是某种自动状态机,只不过不一定是有限状态的吧,至少从他的意思上看是这样的
开始没有理解第二个,加上了效率约束的的形式化表达方法的意思,后来花了点时间看了下Ex1.1.8,我觉得我似乎明白了点
我认为Ex1.1.8是这样的一个状态表
j |
Theta_j |
Phi_j |
a_j |
b_j |
0 |
a |
a |
5 |
1 |
1 |
ab |
c |
3 |
2 |
2 |
bc |
cb |
1 |
2 |
3 |
b |
a |
4 |
3 |
4
| c
| b
| 0
| 4 |
5
| c
| c
| 5 |
5 |
为了验证,我写了个简单的程序来试验我的状态表(真是不行了,好多东西要翻看手册,写程序的速度总是上不来)
1#include <iostream>
2#include <string>
3
4using namespace std;
5int main ( int argc, char *argv[] )
6{
7 // 0, 1, 2, 3, 4, 5
8 string theta[]={ "a", "ab", "cb", "b", "c", "c"};
9 string phi []={ "a", "c", "bc", "a", "b", "c"};
10 int a []={ 5, 3, 1, 4, 0, 5};
11 int b []={ 1, 2, 2, 3, 4, 5};
12
13 int j=0;
14 int i=0;
15 string stat;
16 getline (cin,stat);
17 while(true)
18 {
19 unsigned int loc=stat.find(theta[j],0);
20 if (loc==string::npos)
21 {
22 j=a[j];
23 }
24 else
25 {
26 string temp=stat.substr(0,loc)+phi[j]+stat.substr(loc+theta[j].length());
27 stat=temp;
28 j=b[j];
29 }
30 cout<<i++<<":\tj("<<j<<")\tloc("<<loc<<")\t"<<stat<<endl;
31 cin.get();
32 }
33 return EXIT_SUCCESS;
34} /**//* ---------- end of function main ---------- */
35 最后一定要提的是,我好像发现了书里的一处小Bug,而且好像官方网站里的Errata里面没有这个(中文版同样有这个问题),我已经写信给Knuth了,希望我是真的找到了一个没人发现的Bug啊(其实我知道这个不可能)
关于Galgo库的"瞎想"
念叨做一个泛型的算法库已经有好长时间了,我觉得这个事情与其一直这么YY,还不如高兴了就写一点,不高兴,就扔着,
其实,这个世界是不缺泛型算法库的,STL,Boost,Blitz++中的泛型算法很全面了,我的计划是把他们中间缺少的部分补起来,不能互操作的地方粘合起来,再有就是增加对MetaProgramming的支持
呵呵,应该还算是一个比较雄伟的计划吧
我希望这套库能尽可能的高效率、容易使用、同事保证安全,理想的境地是能够代替ACM集训队使用的模块
目前我的设想是整个库放在Galgo这个namespace里,这个namespace分为两个子namespace,分别是泛型算法Generic和元编程算法Meta
我觉得这样一个库的建立与维护,任重而道远不说,没准前人已经作过360遍了,不过没关系,权当娱乐了。
First Step——Euclid GCD的一个实现
不说什么废话了,先贴代码:
1//-------------------------------BEGIN:GAlgo_Euclid_GCD.hpp--------------------------//
2#ifndef _GAlgo_Euclid_GCD_H_
3#define _GAlgo_Euclid_GCD_H_
4namespace GAlgo
5{
6 namespace Generic
7 {
8 template <typename T>
9 T Euclid_GCD(const T& a,const T& b)
10 {
11 return ((a%b)==0)?b:Euclid_GCD(b,a%b);
12 }
13 }
14 namespace Meta
15 {
16 template <int A,int B>
17 struct Euclid_GCD
18 {
19 static const int value=Euclid_GCD<B,A%B>::value;
20 };
21
22 template <int A>
23 struct Euclid_GCD<A,0>
24 {
25 static const int value=A;
26 };
27 }
28}
29#endif
30
31//-------------------------------END:GAlgo_Euclid_GCD.hpp--------------------------// 应该没什么好说的,比较中规中矩,常规手法,不过根据TAOCP上的说法,可能在某些m,n的取值上需要很多重的递归这时候Meta的方法可能会遇到困难(其实第一种也有运行时堆栈溢出的危险),所以说……说什么好呢,就这样了
下面是个简单的测试
1#include "GAlgo_Euclid_GCD.hpp"
2#include <iostream>
3using namespace std;
4int main()
5{
6 cout<<GAlgo::Generic::Euclid_GCD(6,9)<<endl;
7 cout<<GAlgo::Meta::Euclid_GCD<6,9>::value<<endl;
8 return 0;
9}
10
个人觉得今后有研究价值的方向
我觉得对于算法描述和图灵机、有限状态机、以及隐隐约约我看到的马尔科夫的某些工作(马尔科夫链)之间的关系深入挖掘一下应该会有不少收获,那个我对这个问题可能会有一个数学结构的猜想估计也可能可以在这个方向上证实或证伪……
突然想去向偶像黄兆镇请教一下……还是等我把胆子先练大再去吧……