颖's profilemilkwayhongBlog Tools Help
February 28

20世纪最好的10个算法 zz

人类在20世纪产生了10个著名的算法,是什么算法?
这里是一篇文章,介绍了美国科学家评出的10个算法,感兴趣可以看一看。

                  20世纪最好的10个算法

                     三镜先生

一、算法一词的来源

  Algos是希腊字,意思是“疼”,A1gor是拉丁字,意思是“冷却”。这两个字都不是A1gorithm(算法)一词的词根,a1gorithm一词却与9世纪的阿拉伯学者al-Khwarizmi有关,他写的书《al-jabr w’al muqabalah》(代数学)演变成为现在中学的代数教科书。Ad-Khwarizmi强调求解问题的有条理的步骤。如果他能活到今天的话,他一定会被以他的名字而得名的方法的进展所感动。

二、20世纪10最好的算法

  20世纪最好的算法,计算机时代的挑选标准是对科学和工程的研究和实践影响最大。
下面就是按年代次序排列的20世纪最好的10个算法。

1.  Monte Carlo方法

1946年,在洛斯阿拉莫斯科学实验室工作的John von Neumann,Stan Ulam和Nick Metropolis编制了Metropolis算法,也称为Monte Carlo方法。
    Metropolis算法旨在通过模仿随机过程,来得到具有难以控制的大量的自由度的数值问题和具有阶乘规模的组合问题的近似解法。数字计算机是确定性问题的计算的强有力工具,但是对于随机性(不确定性)问题如何当时并不知晓,Metropolis算法可以说是最早的用来生成随机数,解决不确定性问题的算法之一。

2.  线性规划的单纯形方法

    1947年,兰德公司的Grorge Dantzig创造了线性规划的单纯形方法。就其广泛的应用而言,antzig算法一直是最成功的算法之一。线性规划对于那些要想在经济上站住脚,同时又有赖于是否具有在预算和其他约束条件下达到最优化的能力的工业界,有着决定性的影响(当然,工业中的“实际”问题往往是非线性的;使用线性规划有时候是由于估计的预算,从而简化了模型而促成的)。单纯形法是一种能达到最优解的精细的方法。尽管理论上讲其效果是指数衰减的,但在实践中该算法是高度有效的——它本身说明了有关计算的本质的一些有趣的事情。

3.  Krylov子空间叠代法

    1950年,来自美国国家标准局的数值分析研究所的Magnus Hestenes, Eduard Stiefel和Cornelius Lanczos开创了Krylov子空间叠代法的研制。

这些算法处理看似简单的求解形为                   Ax=b
的方程的问题。当然隐藏的困难在于A是一个巨型的n*n 矩阵,致使代数解
                   x=b/A
是不容易计算的(确实,矩阵的“相除”不是一个实际上有用的概念)。叠代法——诸如求解形为
                 Kx(k+1)=Kx(k)+b-Ax(k)
的方程,其中K 是一个理想地“接近”A 的较为简单的矩阵——导致了Krylov子空间的研究。以俄罗斯数学家Nikolai Krylov命名的Krylov子空间由作用在初始“余量”向量

                              r(0)=b-Ax(0)
上的矩阵幂张成的。当 A是对称矩阵时,Lanczos找到了一种生成这种子空间的正交基的极好的方。对于对称正定的方程组,Hestenes 和Stiefel提出了称为共轭梯度法的甚至更妙的方法。过去的50年中,许多研究人员改进并扩展了这些算法。当前的一套方法包括非对称方程组的求解技巧,像字首缩拼词为GMRES和Bi-CGSTAB那样的算法。(GMRES和Bi-CGSTAB分别首次出现于1986和1992  SIAM journal on Scientific and Statistical computing(美国工业与应用数学学会的科学和统计计算杂志)。

4.  矩阵计算的分解方法

1951年,橡树岭国家实验室的A1ston Householder系统阐述了矩阵计算的分解方法。研究证明能把矩阵因子分解为三角、对角、正交和其他特殊形式的矩阵是极其有用的。这种分解方法使软件研究人员能生产出灵活有效的矩阵软件包。这也促进了数值线性代数中反复出现的大问题之一的舍入误差分析问题。 (1961年伦敦国家物理实验室的James Wilkinson基于把矩阵分解为下和上三角矩阵因子的积的LU分解,在美国计算机协会(ACM)的杂志上发表了一篇题为“矩阵逆的直接方法的误差分析”的重要文章。)

5.  Fortran最优编译程序

    1957年,John Backus在IBM领导一个小组研制Fortran最优编译程序。
Fortran的创造可能是计算机编程历史上独一无二的最重要的事件:科学家(和其他人)终于可以无需依靠像地狱那样可怕的机器代码,就可告诉计算机他们想要做什么。虽然现代编译程序的标准并不过分――Fortran I只包含23,500条汇编语言指令――早期的编译程序仍然能完成令人吃惊的复杂计算。就像Backus本人在1998年在IEEE annals of the History of computing 发表的有关Fortran I,II, III的近代历史的文章中回忆道:编译程序“所产生的如此有效的代码,使得其输出令研究它的编程人员都感到吓了一跳。”

6.  矩阵本征值计算的QR算法

    1959—61年,伦敦Ferranti Ltd.的J.G. F. Francis找到了一种称为QR算法的计算本征值的稳定的方法。本征值大概是和矩阵相连在—起的最重要的数了,而且计算它们可能是最需要技巧的。把—个方阵变换为一个“几乎是”上三角的矩阵――意即在紧挨着矩阵主对角线下面的一斜列上可能有非零元素――是相对容易的,但要想不产生大量的误差就把这些非零元素消去,就不是平凡的事了。QR 算法正好是能达到这一目的的方法,基于QR 分解, A可以写成正交矩阵Q 和一个三角矩阵R 的乘积,这种方法叠代地把 A=Q(k)R(k)变成A(k+1)==Q(k)R(k) 就加速收敛到上三角矩阵而言多少有点不能指望。20世纪60年代中期QR 算法把一度难以对付的本征值问题变成了例行程序的计算。

7.  快速分类法

1962:伦敦Elliott Brothers, Ltd.的Tony Hoare提出了快速(按大小)分类法.
把n个事物按数或字母的次序排列起来,在心智上是不会有什么触动的单调平凡的事。智力的挑战在于发明一种快速完成排序的方法。Hoare的算法利用了古老的分割开和控制的递归策略来解决问题:挑一个元素作为“主元”、把其余的元素分成“大的”和“小的”两堆(当和主元比较时)、再在每一堆中重复这一过程。尽管可能要做受到严厉责备的做完全部N(N-1)/2 次的比较(特别是,如果你把主元作为早已按大小分类好的表列的第一个元素的话!),快速分类法运行的平均次数具有O(Nlog(N)) 的有效性,其优美的简洁性使之成为计算复杂性的著名的例子。

8.  快速Fourier变换

    1965年,IBM的T. J. Watson研究中心的James Cooley以及普林斯顿大学和AT&T贝尔实验室的John Tukey向公众透露了快速Fourier变换(方法)(FFT)。应用数学中意义最深远的算法,无疑是使信号处理实现突破性进展的FFT。其基本思想要追溯到Gauss(他需要计算小行星的轨道),但是Cooley—Tukey的论文弄清楚了Fourier变换计算起来有多容易。就像快速分类法一样,FFT有赖于用分割开和控制的策略,把表面上令人讨厌的O(N*N) 降到令人欢乐的O(Nlog(N)) 。但是不像快速分类法,其执行(初一看)是非直观的而且不那么直接。其本身就给计算机科学一种推动力去研究计算问题和算法的固有复杂性。

9.  整数关系侦查算法

    1977年,BrighamYoung大学的Helaman Ferguson 和Rodney Forcade提出了整数关系侦查算法。这是一个古老的问题:给定—组实数,例如说x(1),x(2),...,x(n) ,是否存在整数a(1),a(2),..,a(n) (不全为零),使得

                     a(1)x(1)+a(2)x(2)+...+a(n)x(n)=0
对于n=2 ,历史悠久的欧几里得算法能做这项工作、计算x(1)/x(2) 的连分数展开中的各项。如果x(1)/x(2) 是有理数,展开会终止,在适当展开后就给出了“最小的”整数a(1) 和a(2) 。欧几里得算法不终止——或者如果你只是简单地由于厌倦计算——那么展开的过程至少提供了最小整数关系的大小的下界。Ferguson和Forcade的推广更有威力,尽管这种推广更难于执行(和理解)。例如,他们的侦查算法被用来求得逻辑斯谛(logistic)映射的第三和第四个分歧点,b(3)=3.544090 和 b(4)=3.564407所满足的多项式的精确系数。(后者是120 阶的多项式;它的最大的系数是257^30 。)已证明该算法在简化量子场论中的Feynman图的计算中是有用的。

10. 快速多极算法

    1987年,耶鲁大学的Leslie Greengard 和Vladimir Rokhlin发明了快速多极算法。


该算法克服了N体模拟中最令人头疼的困难之一:经由引力或静电力相互作用的N个粒子运动的精确计算(想象一下银河系中的星体,或者蛋白质中的原于)看来需要O(N*N) 的计算量——比较每一对质点需要一次计算。该算法利用多极展开(净电荷或质量、偶极矩、四矩,等等)来近似遥远的一组质点对当地一组质点的影响。空间的层次分解用来确定当距离增大时,比以往任何时候都更大的质点组。快速多极算法的一个明显优点是具有严格的误差估计,这是许多算法所缺少的性质。

三、结束语
   
  2l世纪将会带来什么样的新的洞察和算法?对于又一个一百年完整的回答显然是不知道的。然而,有一点似乎是肯定的。正如20世纪能够产生最好的l0个算法一样,新世纪对我们来说既不会是很宁静的,也不会是弱智的。

February 26

学习到的

     去东软报道的路上,一黄牛评价大学生就业说:“大学生要有黄牛精神,不愁找不到工作!”当时正是科学宫开招聘会的时候,凭票入内,看来该黄牛又赚了不少。
     不过看科学宫外,宣传单和用过的票满地都是,看来找不到工作的学生很多也不是没有原因的。
November 19

我认为的一个不错的小程序

    大学毕业时,同学到东软找工作的时候就是问的这个题,有些印象。后来在学程序设计方法学时再次遇到,不过每次遇到都要想想,不如干脆写道这里,mask一下。
    题目如下:
    一个数组中存有-1,0,1三类数若干,无序。排序成-1在前,0在中间,1在末尾。时间o(n),空间o(1)。
 
关键代码
    int pf,p0,p1;// 负,0,1
    p0 = p1 = n-1;//n为数组长度
    while(p0!=pf)
    {
        if(a[p0]==-1)
        {
            a[p0]<-->a[pf];
            pf++;
        }
        else
        {
            if (a[p0]==1)
            {
                 a[p0]<-->a[p1];
                 p0--;
                 p1--; 
            }
            else
            p0--;
        }
    }
   

指向函数的指针

    一直以为这个东西没什么用,才发现它的用处还是很大,在os底层,估计这种用法还是很多的。
    我们还是太年轻,不知道的东西还很多。这次论文收获很大,学到不少东西,弄清楚了很多概念,不是以前不知道,而是以前不知道自己不知道。现在知道自己不知道很多东西,同时也知道知道了一些。好像绕口令呀,大学毕业的绕口令又用了一回。要是读博士不知道会不会再用一回,呵呵。
 
C/C++语言中指向函数的指针
在C语言中,函数本身不是变量,但可以定义指向函数的指针,这种指针可以被赋值、存放于数组之中,传递给函数及作为函数的返回值等” --《The C Programming Language Second Edition》
下面给出几个简单的例子来说明指向函数的指针。
第一个例子说明指向函数的指针如何说明、赋值、调用。
#include
#define TESTDATE 100
int func(int a)  /* func用于打印一个整数 */
{
return printf("%d\n",a);
}

main()
{
int (*FunctionPionter)(int a);
FunctionPionter = func;
(*FunctionPionter)(TESTDATE);
return 0;
}
其中重点语句的含义如下:
int (*FunctionPionter)(int a);
FunctionPionter: 指向一个返回整数的函数的指针,这个指针有一个整数参数。
FunctionPionter = func;
将FunctionPionter指向函数func;其中函数必须已经定义,且函数和函数指针的说明的返回值必须一致。 
(*FunctionPionter)(TESTDATE);
通过函数指针调用函数;因为函数指针已经指向函数,所以用*取出函数指针的内容就为函数本身。
 
了解这些大概就够了。
 
不过在网上找的这个例子还是写全比较好。
 
下面这个例子显示如何将指向函数的指针传递给函数、作为函数的返回类型。在这个例子中,有三个函数:
hello:返回字符指针的函数,用来返回字符串“hello world!\n”
RetFunc:返回一个指向函数的指针的函数,且返回指针所指的那个函数为一个返回字符指针的函数。
call:返回一个void *型的指针,且call有一个指向函数的指针的参数,且这个函数指针返回一个字符指针
#include
#define MAX 100

main()
{
void *call(char *(*)());
char *(*RtnFunc())();  
/* 上面两个说明有些复杂 */
printf("%s",call(RtnFunc()));
return 0;
}
char *hello()
{
return "Hello World!\n";
}
char *(*RtnFunc())()
{
return hello;
}
void *call(char *(*func)())
{
return (*func)();
}
上面的例子中,main()无法直接调用hello函数,利用两个函数分别返回hello和调用hello,实现了在main()中调用hello。虽然,似乎这个程序显得多余但却很好的说明了如何把指向函数的指针传递给函数、作为函数的返回。其中call函数利用了void *型指针的灵活机制,使得call的适用性大为增加,这也正是指向函数的指针的优点之一。同样的例子是《The C Programming Language Second Edition》中下面这个函数调用:
qsort((void **) lineptr, 0, nlines-1, (int (*)(void *, void *))(numeric ? numcmp : strcmp));
其中,使用了两次强制类型转换,其中第二甚至是利用指向函数的指针,将函数的类型进行了转换。当然上面语句在某些编译器上无法通过,因为某些编译器要求条件表达:
表达式1 ? 表达式2 : 表达式3
中表达式2与表达式3的类型相同。当然这样的要求是不符合ANSI标准的。在ANSI标准中,如果表达式2与表达式3的类型不同,则结果的类型由类型转换规则决定。当然,我们可以变同一下,先将两个函数的类型进行强制转换来达到目的:
qsort((void **) lineptr, 0, nlines-1, numeric ? (int (*)(void *, void *))numcmp : (int (*)(void *, void *))strcmp));
对于如何直接说明一个像RtnFunc一样返回指向函数的指针的函数,我查阅了不少资料,都没有找到答案,最后是自己硬着头皮摸索出来的。由此,我也对C的复杂说明有了更深刻的体会,将在以后的技术日记中写出来。当然在我看来,过多的、不合适的使用这些复杂说明,并不是一种好的编程风格,因为它将使程序变得难以理解,同时也增加了出错的可能性。
一个比较好的折衷的方法是使用typedef来使程序的含义明朗。下面给出用typedef给写上面那个程序的例子,其中定义个一个类型PtoFun,用typedef说明PtoFun是指向函数的指针类型,指针所指的函数返回一个字符指针,且没有参数。
#include
#define MAX 100

typedef char *(*PtoFun)();
main()
{
void *call(PtoFun);
PtoFun RtnFunc();
printf("%s",call(RtnFunc()));
return 0;
}
char *hello()
{
return "Hello World!\n";
}
PtoFun RtnFunc()
{
return hello;
}
void *call(PtoFun func)
{
return (*func)();
}
改写后的程序的可读性大为增加,给人一目了然的感觉。
 
 
 
论文修改差不多了,不错。继续努力!
November 08

参加婚礼的感触

    又有一个同学结婚了!
    上周末去锦州参加婚礼,略有感触。上个月高中同学结婚,在盘锦,房子140m2,1400¥/m2大概20W吧,很好的一个小区。这次的是老婆大学同学,在锦州,房子87m2,13W-14W大概1500¥/m2。
想想自己,无奈。只好安慰幸好不是在北京上海。
    不同地方有不同生活呀!
    不同的家境需要不同的努力带来不同的结果。
    原来一直引以为傲的班级第一远不如父母当权来的实惠,大概最大的好处是让我找到理想的老婆。呵呵。
October 31

为了记录而记录

    上周五老婆同事过生日,一同前往。这个生日party真是好事多磨,连续推迟了两次,搞得大家颇有怨言。不过吃的玩的都很好,大家都很开心,还相约下次go on。
    吃,是在盆盆香,重庆风味的盆盆鸭唇、盆盆大虾真是不错,水煮鱼量足价格也不高。推荐爱吃这个口味的xdjm去品尝。辽宁日报社南一百米左右吧。
    酒足饭饱后去快乐迪唱歌。音像效果不错!犹豫几位老师都上了不少课,嗓子经受不了这种高强度,大部分时间都留给大菲对象和我表现。没想到就我这歌声也有这个时候,哈哈。刚开始还是有些迷惑人的,尤其是《有多少爱可以重来》,开头唱的不错,慢慢就露馅了,听到旁边的笑声。呵呵。
    玩的真是不错!
 
    今天研究一个趣味小题,abcde×f=gggggg(为>0,<10的不同整数),这个东东看似很简单,一动手竟然屡屡出错,真是汗颜呀,而且前两天刚刚做过abc+def=ghij,看来基本功不过关,思路转变慢,程序优化不熟练。问题真的很多,看来还是要慢慢练内功。
October 27

东软游记

今天去东软看了一看。

因为要把体检结果送到东软,本来可以让同学帮忙的,不过还是想看看这个未来工作的地点,就跑了一趟。

做东软的班车还是满快的,10点出发,10:18就到了,还以为要跑个半拉点呢。

由于是深秋,路边的树光秃秃的,小草也是稀疏的,不过整体感觉不错,大气。

A1楼的3楼是HR部门,教体检结果,ok

去我们部门看了一看,员工也是热火朝天,气氛不错。和部门领导谈了一会,了解了今后工作内容,决定提前看看。

不过,还是先玩玩游戏吧,呵呵。