并发技术是的计算机程序能够在同一时间间隔或同一时限内做更多的工作,……。某些情况下,在同一时间间隔做更多的工作并不是目的所在,简化程序解决方案才是真正目的。有时把问题的解决方案看作一组并发执行的任务更合理。
并行(Parallel)和分布式(Distributed)编程是达到软件并发的两种基本途径,它们是两种不同的、有时又相互交叉的编程范例。并行编程技术将程序必须处理的作业分配给一个物理或虚拟计算机内的两个或多个处理器,分布式编程技术将程序必须处理的作业分配各两个或多个处理器,这些处理器可以也可以不在同一个计算机中,也就是说分布式程序的各部分通常在不同的由网络连接的计算机上运行,或者至少在不同的处理器上运行。包含并行性的程序在同一个物理或虚拟计算机上执行,程序内的并行性可分成进程或线程。分布式程序仅能分成线程,多线程仅限于并行性。
在技术上,并行程序有时候是分布式的,例如PVM(Parallel Virtual Machine) 编程;分布式编程有时用于实现并行性,例如MPI(Message Passing Interface)编程。但是并非所有的分布式程序都包括并行性,分布式程序的各部分可以在不同时间间隔内的不同时刻执行。
软件级的并发可以分为:指令级、例程(函数/程序)级、对象级、应用程序级。
——摘自《C++并行与分布式编程》 Parallel and Distributed Programming Using C++
并行计算和分布式计算是两个容易混淆的概念,希望上面的解释可以帮助同样迷惑的你理解他们的关系。
]]>先说Java. 目前广泛使用的是Java 1.5。Java分为Java Platform, Enterprise Edition(Java EE), Java Standard Edtion(Java SE), Java Micro Edition(Java ME)。分别运用于企业开发,标准开发以及移动设备(嵌入式)开发。在Java是1.2版本的时候,SUN认为Java改动很大,所以称之为Java2。后来大家习惯了这种称呼方法,就有了所谓的J2EE, J2SE, J2ME,他们跟Java EE/SE/ME是完全等价的。不过在2006年,SUN放弃了J2xE的称呼,统一为Java XX Edition。这里侧重说一些关于Java EE的名词。
JDK:Java Development Kit。语源是SDK。是Java得开发包,面向使用Java开发的人员。JRE是Java Runtime Enviroment,面向的是使用Java相关软件或者服务的用户。
Servlet是Java EE的核心。它是一个响应网络请求,并做出答复的组件。可以处理各种网络请求,并返回结果。绝大多数时候,servlet响应的是http请求,返回的是一个html页面。Servlet处理请求得功能很完善,不过对于返回的Html各式并没有天生的良好支持。所以就有了JSP(Java Server Page),它是将类似Java语法的代码嵌入html页面中,生成动态的html。JSP实质上,在最后都会编译成servlet。所以,JSP只是一种简化html开发的技术。
基于Java EE的框架。所谓框架, 可以理解成是对底层进行了封装,并提供专有功能的程序库。Struts是Java EE的老牌开源框架。struts, webwork都是侧重于表示层方面的框架,都是基于MVC模型。Struts 1.2比较成熟,多数使用struts的都是基于此版本。所谓的struts 2.0是struts的开发者发现struts本身的固有模型有问题,所以完全放弃,跟另一个表示层框架webwork合并,使用Webwork2作为底层的新框架,跟struts 1.2完全不同。
Java Server Faces。一个模型更加高级的表示层框架。类似asp.net的事件驱动模型。有很大潜力,不过最大的缺点是,它还不是Java标准。(这个我没有用过,所以信息就这么多了)。
目前最流行的Java EE框架。基本延伸到了Java EE的整个模型中(表示层,业务逻辑层,数据层)。其主要特点是AOP和IoC。它的模块很多,可以只使用其中几个,而且Spring跟其他框架是配合关系,而不是竞争关系。Spring可以很好的整合目前流行的很多框架。Spring还有自己的一套轻量级MVC模型,可以取代Struts/Webwork。
Aspect Oriented Programming.面向切片编程(以前叫做面向方面编程)。将程序的一些服务代码,例如日志纪录,数据检验等分离出来。使得原始程序更加注重业务逻辑。
Inversion of Control.控制反转,或称依赖注入。一般情况下,要使用一个框架,就将框架的一些代码嵌入到原始程序中,这样做会产生对框架的依赖性。控制反转,就是再不改变原始的对象的情况下,反转整个过程,由外部来注入。这样就可以很容易的使用或者放弃某个框架。这个能力就是Spring的核心。
Data Access Object。数据访问对象,属于O/R mapping的一种实现方法。通过DAO对象,可以将程序对象的属性等映射到数据库中。
数据访问层的组件/框架。JDBC(Java DataBase Connection)是Java访问数据库的底层组件。JDO(Java Data Object),Java的数据对象。Hibernate是目前最流行的O/R mapping框架。自动的将程序对象和数据表做映射,封装了数据库的操作。Hibernate的底层是JDBC。
开源的单元测试框架。是利用反射机制实现的。
Eclipse如日中天的开发环境IDE。MyEclipse是建立在Eclipse基础上的升级版,提供很多功能,使得开发JavaEE更加容易。其他IDE还有NetBeans, JBuilder等等。
Apache是一个组织。作了很多开源的东西。其中最著名的就是Apache/Tomcat了。Apache/Tomcat是类似IIS那样的网络服务平台。作为JavaEE, PHP, Asp.net(mono)等等网络服务器的底层运行平台。一般来说,JavaEE编译好的文件要部署到Apache/Tomcat才可以浏览。
版本控制工具。CVS是老牌的,Subversion是摒弃CVS的一些缺点的新型版本控制工具。TortoiseSVN是SVN的可视化版本。
程序构建工具。make/makefile等的替代品。集成了编译、部署等功能。这个基本没有怎么研究过,所以不多说了。
国人开发的报表。表示层控件。开源的。
to be continue…
JBoss/Weblogic
]]>计算过程是存在于计算机里的一类抽象事物,在其演化过程中,这些过程会去操作一些被称为数据的抽象事物。人们创建出一些称为程序的规则模式,以指导这类过程的进行。
一个强有力的程序设计语言,不仅是一种指挥计算机执行任务的方式,它还应该成为一种框架,使我们能够在其中组织自己有关计算过程的思想。
函数与过程之间的矛盾,不过是在描述一件事件的特征,与描述如何去做这件事情之间的普遍性差异的一个具体反映,人们有时也称它为说明性的知识与行动性的知识之间的差异。在数学里,人们通常关心的是说明性描述(是什么);而在计算机科学里,人们通常关心行动性的描述(怎么做)。
能够看清所考虑的动作的后果的能力,对成为程序设计专家至关重要。对结果的了解,才能反推出所需的过程。我们需要对计算过程中各种动作的进行情况作出规划,用一个程序去控制这一过程的进展。要学会去看清各种不同种类的过程会产生什么样的计算过程,只要在掌握了这种技能之后,我们才能学会如何去构造出可靠的程序,使之能够表现出所需要的行为。
]]>Solve this cryptic equation, realizing of
course that values for M and E could be
interchanged. No leading zeros are allowed.
WWWDOT – GOOGLE = DOTCOM
我的解法如下:
先把式子写成竖式方便推算:
| w | w | w | d | o | t |
| g | o | o | g | l | e |
| d | o | t | c | o | m |
观察发现w-o=o, w-o=t。同样的减数,被减数做差不同,只能是产生了借位。(-1都表示低位做运算的时候有借位情况,+10则表示被减数小于减数,导致向高位借位。)
情况一:
w – o = t; w + 10 – o = o;
不成立。因为被减的都是o,不可能出现一个借位了,一个没有借。
情况二:
w – 1 + 10 – o = t; (被d – g借了一位)
w – 1 + 10 – o = o;(上一式成立的前提就是w-1 两等式左边相同,而结果不同,矛盾。
情况三:
w + 10 – o = t;
w + 10 – 1 – o = o;(-1是因为上式借位)
可推出w = 20 – 9; t = o + 1;
其中 0 =当o = 5或o = 6时w = 1,或2。w – g = d中g,d无法取值。
当o = 7的时候w = 5 t = 8。
因为w + 10 – o = t可知其低位d-g=c没有发生借位,即d>g。
又因为w = 5;所以g = 1, d = 3。(之前有借位)
当t = 8时,m,e可取值对为(1,7; 2,6; 3,5)。
当g = 1, d = 3时。c = d – g = 2;m, e的三对取值都与前假设矛盾。
所以,o = 8。可算出w = 7; t = 9;
此时d + g = w – 1 = 6。所以d,g=5,1 或d,g = 4,2。
当d=4,g=2的时候,c=d-g=2。矛盾。所以d=5,g=1,推出c=4。
e,m则等于3, 6。o-l=o。所以l=0。
最后结果:0=l, 1=g, 2没有, 3=m/e, 4=c, 5=d, 6=e/m, 7=w, 8=o, 9=t
之后在网上找到了另外一种解法。
第二题以前在校内网讨论过,这里就不多说了。有兴趣的google一下。
——Alan J. Perlis (April 1, 1922-February 7, 1990)
译文:我认为,在计算机科学中保持计算中的趣味性是特别重要的事情。这一学科在起步时饱含着趣味性。当然,那些付钱的客户们时常觉得受了骗。一段时间之后,我们开始严肃地看待他们的抱怨。我们开始感觉到,自己真得像是要负起成功地、无差错地、完美地使用这些机器的责任。我不认为我们可以做到这些。我认为我们的责任是去拓展这一领域,将其发展到新的方向,并在自己的家里保持趣味性。我希望计算机科学的领域绝不要丧失其趣味意识。最重要的是,我希望我们不要成为传道士,不要认为你是兜售圣经的人,世界上这种人已经太多了。你所知道的有关计算的东西,其他人也都能学到。绝不要认为似乎成功计算的钥匙就掌握在你的手里。你所掌握的,也是我认为并希望的,也就是智慧:那种看到这一机器比你第一次站在它面前时能做得更多的能力,这样你才能将它向前推进。
]]>首先说数据对其这种说法怎么来的。我以使 用16位(16-bit)数据总线的处理器来举例。32位的原理相同。处理器将16位的数据总线分为两部分d0-d7为低字节,d8-d15为高字节。相 应将内存分为对应两列。偶地址的接在d0-d7数据线上,奇地址的接在d8-d15位上。这样处理器就可以任意访问一个偶地址的数据,或一个奇地址的数 据,或者一个以偶地址开头的字(Word, 1word == 2byte)。
然而,例如在80×86系列的16位处理器上,它允许从任意地址读 取一个字。处理器从指定的地址取得低端字节,而从下一个地址获得高端字节。如果低端地址是偶地址的话,没有什么问题。但是,如果是奇地址的话,那么数据的 高端字节就需要到下一个字去获得了。例如希望从地址21获取一个字。这个数据位置就是在21, 22两个字节。但是要注意,处理只能将偶地址作为低位放入地址总线,所以处理器首先通过访问地址为20, 21的字节,获得一个字,然后再访问下一个字,获得地址为22, 23的两个字节。并在内部将地址21和地址22的数据交换位置,通过数据总线传递给处理器。
这在大规模访问连续数据的时候就会产生问题(当然其他 时候也可能会有)。例如,一个图像数据顺序保存在内存中,假设每个象素是两字节,数据的起始字节是奇数。并假设处理器没有做任何优化,没有缓存,那么每个 象素的读取都会有两次的内存访问,一次的高低字节数据交换。一个800*600的图像就会有480000次的多余内存访问。这个效率会差很多。所以要想办 法解决。
这里我介绍两种需要手动设置数据的对齐的情况。
1. 在一个大的数据块的开始,如果数据没有对齐,那么后面的数据都回相应错位。设置数据开始部分对齐的方法如下:(从SIMPLE中摘录,为了便于阅读,有改 动,另由于blogger对尖括号的支持不够好,所以下面的代码中可能使用全角的尖括号代替半角尖括号,使用时请自行替换。)
[code:c]< T* > alignPointer(void * raw, int align)
{
T*p = reinterpret_cast<T*>(
(reinterpret_cast<uintptr_t>(raw) + align - 1)
&~(align - 1);
return p;
}[/code]
解释一下,假设我们需要N个字节的数据,并以align对齐。(align==0,1的时候不对齐, 2的时候两字节对齐,以此类推,align为2的幂。)。void * raw是申请的空间,大小为N + align – 1个空间。多余的align – 1个空间是为了对齐而预留的。T类型是指定的每个元素的类型。
首先,通过reinterpret_cast<uintptr_t>(raw)将raw转化成uintptr_t类型,(uintptr_t是一种足够大从而可以保存指针的类型,如果没有定义uintptr_t的系统,可以使用typedef int uintptr_t来自行定义)。
结果加align – 1,相当于这个元素最后一个字节的位置。然后对~(align – 1)取与(and)运算。相当与计算出元素最后一个字节所在的内存的对齐列的第一个字节的地址。这么说有些晕,我来举个例子:例如分配n个字节,其中起始地址是7,对齐字节是4字节对齐。如果不对齐的话那么这个数据的第一个元素占用的就是7~10字节。如果对齐的话,首先7 + (4 – 1) = 10,计算出理论的最后一个字节的地址是10。然后10 & ~3。换算成二进制更好理解:1011 & 1100 = 1000,也就是十进制的8。所以起始的字节是从8开始的,那么地址是7的字节以及分配的最后两个字节会被忽略。再假设起始地址是4,那么计算后还是4,不用对齐。
2. 行对齐。在图像的内存表示中,除了开头的内存对其外,还有一个地方需要注意,就是行与行之间的对齐。例如一个RGB像素3字节,一个2*3的图像,再内存中的布局应该是:
0- 2为(0, 0) 3- 5为(1, 0) 6- 7空闲
8-10为(0, 1) 11-13为(1, 1) 14-15空闲
16-18为(0, 2) 19-21为(1, 2) 22-23空闲
图像的每一行都需要8个字节存储。不过其中仅有6个字节包含象素数据。前三个字节保存该行第一个象素的存储,其后3个字节保存下一个像素。为了另下一行从双字开始,在存储下一行的象素之前必须跳过两个字节。这里的做法跟上面的那种差不多,所以就不再举出代码了。
]]>2. 设置打印机:
打印机和传真->右击HP LaserJet 6P/6MP PostScript(或其它的ps打印机)->属性->常规->打印首选项->布局->高级->文档选项- >PostScript选项->PostScript输出选项: 内嵌的PostScript(EPS)
3. 处理打印的文件:
把打印完成的ps文件用ghostview打开,文件->ps to eps,并选中自动检测边界。这样就可以生成效果极佳的EPS文件了。
eps 图像万能但是愚蠢的办法,是虚拟彩色打印机,分辨率确实高,但是虚拟打印重新得到的eps图像文件的分辨率与打印机设置有关,因为生成的文件有可能比原始文件大很多很多,完全没有必要。因此强烈推荐jpeg2ps这个小软件,命令行转化一下即可。数据作图只需用origin导出即可;若用gnuplot,也很方便。
]]>写在前面
我不奢求我的一点研究体验能够引起广泛的共鸣,但只要有一个人读了之后能有所启发,并对他今后的学习工作产生积极的影响,我就将心满意足。同时,为了让大家觉得我的感受有所参考价值,我先简要说一下我的一些研究成果,因为“不能让人致富的哲学是没有人听的” :-)
我在浙大计算机系读硕期间,在多媒体信息检索领域内发表了近二十篇国际期刊/会议论文,一项美国专利(申请中),曾在西门子、微软研究院短期实习,在香港城市大学担任研究助理近一年。毕业后得到了IBM Research Lab (Beijing),Carnegie Mellon Univ (CMU)的计算机系等一些学校的offer,如一切顺利将选择在CMU读Ph.D。
选择方向
很多人都希望找到一个“容易”的方向。我的个人感觉是的确有一些“容易写paper”的方向,但没有特别“容易”的方向。每个方向上都有一些做得很出色的人物,从这个意义上说也许所有的方向都一样好。当然,每个方向有自身的特点,有的理论化一些,对数学要求高一些(比如人工智能、计算机图形学);有的则接近应用一些(比如我做的多媒体),所以需要找到的是适合自己的方向,而不是盲目听从别人那些“xxx太容易出成果了”,”xxx已经做到头了”。研究领域柳暗花明的例子很多,比如人工智能技术运用于数据库后改头换面成了数据挖掘(data mining),又养活了一大帮人 :-)
很多人都希望有人告诉他们一个方向。事实上,也许导师会给你指定一个大的领域,小的方向大多是要自己去寻找的,办法就是reading!!! 我在硕士期间读过超过150篇专业论文。我的感受是当你开始读别人论文的时候,很自然地就会发现许多问题和不足,有时作者自己也会谈及将来的工作,这就成 为你的研究的契机。
还有如何寻找paper的问题。如果你开始进入一个陌生的领域,你可以咨询那些在该领域中有经验的人(比如师哥师姐),哪些paper是值得读的。CS专业的人可以参考ResearchIndex网站(http://citeseer.nj.nec.com/), 那里对CS的paper按方向进行了分类,大类下面还有小类,因此你可以选择你那个类的paper来读。另一个诀窍是在开始的时候选择综述性 (overview)文章和引用次数很高文章(在CS领域,引用几十次的文章就是经典了)。当你读过一些paper之后,通过它们的参考文献 (reference)就可以辐射式地找到更多的paper。对于一篇已知作者和标题的文章,大多数情况下通过Google 就可以找到电子版,如果没有则可以写email问作者要。
关于天赋和勤奋
关于程序员有这样一种说法:一个有天赋的人通过不断努力会成为一个伟大的程序员,一个天赋平平人通过不断努力也会成为优秀的程序员。根据我有限的观察,大多数成功的人都属于后一种类型。事实上,我几乎没有看到过天赋很高而又很勤奋的人,相反却看到过不少天赋被浪费的例子,这也是我自信的源泉。因为我知道只要我日复一日地努力,总有一天会达到目标的。
我在香港城市大学做研究助理的时候,为了赶paper deadline有过不间断工作40-50个小时的经历。每次投会议的论文,我都会看看举办地,因为deadline基本上是以举办地的时区为准,一般比 中国的时区都要晚一些,所以多出几个小时可以修改(有一次是Hawii的会议,比中国晚18个小时,我开心死了)。我经常在deadline之前5分钟内在网上提交我的论文,为的就是尽可能地改得好一些。我在香港的导师经常说”Good papers are always submitted in the last 5 minutes”。他晚上回家的时候车经常是停车场上的最后几辆。
我3年前在微软研究院的时候,也常常在晚上11点之后在电梯里碰到老板。
看看我一个在MIT读MBA的朋友写的一天流水账,对我有很深的触动,抄在下面:
Last night I read case till 1am and I got up at 7:30 am this morning and went to class From 8:30 to 10:00, then rush to library to finish a homework due at 12:00 pm, then I had a teammeeting from11:30am to 1:00pm, then I had class From 1:00 pm to 4:00 pm, then I have another teammeeting, which we need to drive 40 minutes From Boston, so that last From 4:00 to 7:30, then I have a market research interview, From 7:45 to 10:00 pm,then I came back home….
then there’s a fire alarm, which we need to go out of the building and stand in the -10°C wind and I came back at 11:00 pm, starting reading a case for tomorrow, which is 20 pages long, and I still have a homework I need to look at ….the case will be discussed 10:00 am tomorrow….and the homework is due 1:00 pm tomorrow. I had my dinner when I read the case. It’s 00:49 am now…I will take a shower and start looking at my homework………….
paper!!
首先,绝对不要一稿多投,这个很显然的问题被人在BBS上问了太多次。也许你做过很多次都没有问题,但一旦出问题可能是致命的。我们要像爱护自己的眼睛一样爱护自己的信誉。
其次,态度要认真。这几年来我自己审过超过50篇英文论文,其中大约十多篇来自国内。在这十几篇中,我记得除了一篇之外我都拒了,原因不是因为他们的研究内容不够好(我甚至没能读懂他做了什么),而是语言实在是太差了。更让人痛心的是,不是主要因为作者的英语能力不够好,而是态度太不认真了,在 title中竟然有拼写错误(不要说你不知道Word会自动提示拼写错误)。我甚至看到过title中的一个”the”写成了”tHe”, abstract中几乎找不到一句没有语病的句子。我不相信作者的英语差到这种地步,更有可能的是作者在写完之后连读都不愿意多读一遍。再公正再有耐心的审稿人,读了这样的abstract之后也会准备开始拒了。
一篇成功的paper中语言和研究内容的重要性大约各占50%,这并不意味者英语水平一般的中国学生就不能写出好论文了。论文中语言的要求是清楚、简洁、准确,同时段落组织合理,并不要求复杂的句式和单词。在写完之后,对论文反复修改3-4次是绝对必要的,一定要打印出来一字一句地修改(对着电脑难以发现错误),每次修改之间最好隔一段较长的时间,以避免思维上的惯性。
在写头一两篇论文的时候最好能有一个有经验的人帮你一起修改。我的第一篇论文有幸是在微软研究 院的一位研究员指导下进行的,短短的4页纸我改了近一个星期。我至今还记得他用Word的审阅功能把我的paper改的大红大绿,面目全非的样子,很多是对用a还是the,用for还是of这些细节的修改。经过了那次痛苦的经历,我以后写paper就顺手多了,慢慢有了驾轻就熟的感觉。
还有投稿要持之以恒。我尽管发表和收录近20篇论文,被拒的次数应该也差不多这个数了,也许更多。比如CS领域的顶级会议的接收率在10-15%之间,也就是平均投7篇才中一篇,更何况你的竞争对手可能来自Stanford。就我自己而言,大约投过6-7次顶级会议,投中过一次,符合统计规律:-) 所以被拒绝对不是什么丢脸的事,而是可以从中汲取很多建议的好机会。我有一篇被顶级期刊据的paper,三个审稿人给的评语加起来超过了11页(A4 纸),快有paper本身的一半长了。
天外有天
我在申请PHD的时候尽管得到了一些好学校的offer,也被很多学校拒了,比如Stanford, UC Berkeley, Cornell, MIT。一位朋友对我被很多学校拒感到吃惊,觉得凭我的简历应该是所向无敌的。我的感受恰好相反,我觉得能被CMU这样CS方面顶尖的学校选中是非常非常 幸运的。因为这个世界上牛人实在是太多了 :-) 我敢说CMU再多招50个人,其优秀程度(而非合适程度)是不会有丝毫逊色的!
INTEL 前总裁Andy Grove说过一句话我一直用来提醒自己 “Don’t take yourself too seriously”。即便浙大也绝对是一个藏龙卧虎的地方。我所认识的一些浙大人,他们的知识绝对是上通天文地理,下晓人文历史,而他们的经历更让我只有”景仰”二字形容,而他们中的大多数为人处世都是比较低调的(这也是他们成功的秘诀之一)。一位我一直把他当作大哥的朋友,在我现在的年纪就已经创办过千万级的软件公司,在美国工作过,周游过世界,同时精通登山、摄影、自行车(7次西藏,曾从杭州骑车前往)、多项运动(体校出身),对历史、篆刻、书法、 古典音乐也多有涉猎。此仁兄只不过是杭大(合并前)的一名普通本科毕业生,为人极其谦和低调,对朋友也是两肋插刀(他肯定不想被人提起名号,因此略去)。
这些真正的强人让我为BBS上许多所谓“名校研究生”、“牛人”的狂妄言语而汗颜,深感如芒在背。另一方面,也让我觉得普通人凭借坚持不懈的努力同样能够做出一番事业,因为这些强人的天资并无太多过人之处,就读的学校也很一般,他们成功的第一秘诀是勤奋。“王侯将相,宁有种乎”。我们每个人不管目前处于什么状态,只要用功总是能做出一些事情的。
别看不起导师和学校
BBS上说导师坏话的贴子要远远多于赞扬的。诚然,我们的导师们确实不尽如人意,他们的研究工作未必都是第一流的,个别在师德方面也有问题。但是,导师们也可能在抱怨,他的学生中怎么就没有出几个杨致远(Yahoo创始人),S. Brin和L. Page(Google发明者),要知道他们几乎都是在课余完成大业的。牢骚太盛防肠断!我见过太多的研究生,一边抱怨实验室的东西没意思,一边每天把时间泡在88灌水、看片上,最后一事无成。这样的人即使身处条件优越的实验室也未必能有所成就。无论顺境逆境,我们每个人都没有消磨时间浪费青春的权力。
我很幸运地拥有一个优秀的导师和感兴趣的方向。但是在3年前我也怀疑过我的导师,也觉得当时的方向很无趣。庆幸的是,我没有选择消磨时间,而是争取到自 己比较感兴趣的课题,并通过努力取得一些成绩。现在回头看看,当初的怀疑更多出自误解和缺乏沟通,现在我为做过他的学生而自豪。
还有很多人在抱怨学校,当然我也曾是其中一个。诚然,中国的大学普遍缺乏一个有效合理的system,我深信这是诸如研究水平低下、学术腐败、教师们不安心于校园的深层次原因。比如在现有制度下,国内一个教授通过做学术研究并不能保证他拥有优越的生活水平,这就导致了热门专业的教授一窝蜂地开了公司,学生成了廉价劳动力。去责怪教授的道德水平是没有实际意义的,国外那些学富五斗的教授到了这种制度下会做出同样的事!! 然而,中国特点决定了这些制度的改善在目前只能是缓慢进行的,因为每一步都会有来自许多既得利益者的阻力。所以,作为学生,我更倾向于“穷则独善其身”, 做好自己该做的事情,把你的想法留到“达则兼济天下”的那一天。
另一方面,就在这个离世界一流还相去尚远的浙大,也不断地产生着杰出人物,即便是在落后得多的过去。一个学校的成功最终依赖于学生的成功。过分关注学校排名,为了浙大排在第4而不是第3 而耿耿于怀者,或者以为进了浙大就是高人一等的名校生者,一般都缺乏对自身能力的信任,真正是英雄的应该是“莫问出处”的。作为我自己,最感到欣慰的成就(如果算得上成就的话)就是让浙大的名字出现在一些大陆学者没有或很少出现的学术会议和期刊上,也算对得起浙大给我带来的许多机会。最后,改了美国总统 J.F. Kennedy的那句名言作为结束:
Ask NOT what your school can do for you, ask what you can do for your school!
关于coding
计算机专业的不少学生很早就被灌输了”写程序完全是体力劳动,30岁之后还写程序就没出息”这样的思想。我所接触的一些计算机系本科和研究生 coding的能力之差到了令人吃惊的地步(甚至有完全不会的)!上面的这种观点是绝对错误的。事实上,至少在CS的研究领域,年薪十几甚至几十万美元的人中许多人都自己编写程序,包括一些如雷贯耳的名字,比如Gim Gray。此君因为不喜欢Seattle的雨天,使得Bill Gates为拉他入Microsoft在San Francisco为其新建了一座研究院让他做院长。据说此君在一次学术会议上demo他的新程序,听众中有人提出界面可以如何改进,此君竟然在讲坛上就打开VC++开始改起代码来,然后给人看新效果 :-) 在这一方面,我觉得国内的导师们是比较弱的,当然这和他们从一开始就不缺学生有很大关系。
当然,我对coding重要性的认识比有丰富工作经验的人差很多。我的经验主要是两方面:一、绝大多数CS的研究必须以实验为基础(尽管很多国内学生并不这么做),coding能力直接关系到研究的进展。二、coding可以加深我们对技术的理解,体会各种技术的优劣,使我们能更快更好开发出自己的系统。
结束语
就让这些文字作为我向母校默默的告别吧…… 七年的校园生活已经给我打上了浙大的烙印,我也将永远关注和祝福我的母校。
I love ZJU ! I love her so much …
“为了提高浮点计算的准确性,有必要在计算过程中使用额外的有效数字。这些额外的有效数字被称为保护位(guard digits,二进制格式时为guard bits)。在进行一长串的运算时,保护为极大地提高了准确性。”
有关有限精度计算的几个重要规则
“计算的顺序可以影响结果的准确性。将运算分组,其原则是先对数量级接近的数进行相加或者相减,再对数量级相差较大的数进行运算。
在对符号相同的两个数做减法或者符号不同的两个数做加法时,结果的精度可能比所用的浮点格式所能支持的精度要小。
在进行一系列涉及到加法、减法、乘法与处罚的计算时,尽量先作乘法与除法。
在对一组数作乘法或除法时,尽量对数量级相对一样的数作乘法与除法。
在比较两个浮点数是否相等时,要做的是计算这两个数的差,看它是否小于某个很小的误差值。例如if( (value1 - value2) // then ...”
IEEE浮点数格式
IEEE有三种浮点数格式:单精度(single precision),双精度(double precision),以及扩展精度(extended precision)。这里举单精度为例。
“单精度浮点格式使用24位的尾数与8位的阶码。尾数通常表示的值在大于等于1.0到小于2.0之间。尾数的最高位总是假定为一,正好是在二进制小数点左边的第一个位,而余下的23个尾数位则在小数点的右边。”
“尾数使用一的补码格式而不是二的补码。这意味着尾数的24位值通常是一个无符号数,而位于第31位的符号为决定了正负。”而第23~30位表示阶码,0~22表示尾数。(其中有23个尾数的位,而最高位是1,所以有了24位。)
“为了表示1.0到小于2.0范围之外的数,就需要阶码了。浮点格式将计算二的有阶码的值指定的幂,并将尾数乘以这个数。阶码是八位的,它使用余- 127格式(excess-127),有时候也成为移-127(bias-127)阶码。使用余-127格式,阶码2^0表示为127(0×7F)。因 此,为了将一个阶码转换为余-127格式,只需要加上127就可以了。阶码使用余-127格式简化了浮点数的比较。因为如果我们将符号位(第31位)单独 处理,那么对两个浮点数作小于或者大于的比较就很容易了,只需要将它们看作是无符号整型数作比较就可以了。”
“将尾数左移一位同时递减阶码就不会改变浮点数的值。”反之亦然。
规格化(normalization)与反向规格化(denormalized)数
“规格化浮点数就是尾数最高位是一的浮点数。”
“在两种重要的情况下,浮点数是无法被规格化的,零就是特例之一。浮点格式支持+0也支持-0(取决于符号位的值)。Intel推荐使用符号位来表示该零值是负数下溢产生的(符号位置一)还是正数下溢产生的(符号位清零)。”
“无法规格化浮点数的第二种情况是尾数的高端几个尾是零,但是移位阶码(移位意味着给数值加上偏移量,如余-127格式的移位是127)也是零。IEEE允许使用特殊的反向规格化数来表示它们。”
舍入(rounding)
舍位(truncation):直接舍弃额外的精度。
上舍入(rounding up):将结果置为目标浮点格式中大于当前之中所有数的最小值。对应函数ceil。
下舍入(rounding donw):将结果置为目标浮点格式中小于当前之中所有数的最大值。对应函数floor“舍位与下舍入之间存在着微妙的差别。舍位总是是的结果更接近零。 对于正数来说,设位于下舍入的结果是一样的。但是,对于负数,舍位只是简单地使用尾数中现有的位,而下舍入会给最低位加上一。”
四舍五入 (rounding to nearest):“如果保护位的值小于尾数最低位的值的一半,那么四舍五入方式会将结果舍入到小值中的最大值(忽略符号位)。如果保护位的值小于尾数最 低位的值的一半,那么四舍五入方式会将结果舍入到大值中的最小值(忽略符号位)。如果保护位的值正好等于尾数最低位的值的一半,IEEE浮点标准的说法是 上舍入和下舍入各半。具体实现可以是将尾数舍入到最低位等于零。也就是说,如果当前的尾数的最低位已经是零,就直接使用该尾数值,如果当前的尾数值最低位 等于一,就将尾数加一。”
特殊的浮点数
“如果阶码全一儿尾数非零(不管隐含位),那么尾数的最高位(仍然不包括隐含位)用来表示这个数是一个未明无效数字(quiet not-a-number, QNaN)或者明确无效数字(signaling not-a-number, SNaN),这些无效数字(not-a-number, NaN)结果告诉系统计算过程中出现了严重的错误,计算的结果是完全不确定的。未明无效数字代表不确定的结果,而明确无效数字则代表确定发生了无效的操 作。任何运算中,只要存在无效数字,结果就是无效数字。”
“另两种特殊的浮点数表示是阶码全一,而且尾数全零。这种情况下,符号位表示该书是正无穷大(+infinity)或者是负无穷大。”
“第五位与第六位决定了字符所在的组。”五六位为00,表示控制字符,01表示数字与标点符号,10表示大写字母与特殊字符,11表示小写字符与特殊符号。
“如果你将大写字母与小写字母的编码转换成二进制,你会注意到大写字母与对应的小写字母的符号差别只有一位。”例如,E的二进制编码是 01000101,e的编码是01100101。相差第五位。“这两个ASCII码的唯一不同之处在第五位。大写字母符号的第五位永远是零,而小写字母字 符的第五位永远是一。你可以利用这个事实来快速地将一个字母字符在大写与小写之间进行转化,只须将第五位取反即可。”
数字的十六进制“ASCII码的低端半字节等于被表示的数。只要将ASCII码的高端半字节去掉(置全零),就得到了该数字的二进制表示。”反之可得到对应数字的ASCII码。
]]>1、如果一个二进制数(整型)数的第零位的值是一,那么这个数就是奇数;而如果该位是零,那么这个数就是偶数。
2、如果一个二进制数的低端n位都是零,那么这个数可以被2n整除。
3、如果一个二进制数的第n位是一,而其他各位都是零,那么这个数等于2n。
4、如果一个二进制数的第零位到第n位(但不包含位n)都是一,而且其他各位都是零,那么这个数等于2n-1。
5、将一个二进制数的所有位左移移位的结果是将该数乘以二。
6、将一个无符号二进制数的所有位右移一位的结果等效于该数除以二(这对有符号数不适用)。余数会被下舍入(round down)
7、将两个n位的二进制数相成可能会需要2*n位来保存结果。
8、将两个n位的二进制数相加或者相减绝不会需要多于n+1位来保存结果。
9、将一个二进制数的所有位取反(就是将所有的一改为零,所有的零改为一)等效于将该数取负(改变符号)再将结果减一。
10、将任意给定个数的位表示的最大无符号二进制数加一的结果永远是零。
11、零递减(减一)的结果永远是某个给定个数的位表示的最大无符号二进制数。
12、n位可以表示2n个不同的组合。
13、数2年包含n位,所有位都是一。
有用的位运算
可以使用逐位与(and)运算来检测位串中的各个位是零还是一。例如:IsOdd = (ValueToTest & 1) != 0;
可以使用与运算来检测一组位是零/非零。例如IsDivisibleBy16 = (ValueToTest & 0xF) == 0;比较一个位串中的一组特定的位。(可能不连续)。
使用逻辑与创建模-n计数器(Modulo-n Counters)
cntr = (cntr + 1) & 0x3f; // 0x3f = 31. 这里实现的就是模-32计数器
try/catch结构里面。restart,标记了之前运行得出错误数。这样在yourMain(int restart)中,就可以根据restart来判断这是第几次重新启动,并且可以做一些之前的异常情况的处理。while来不断尝试重新开始程序。并通过标志位running来决定是否继续执行。yourMain都会产生一个异常,这个结构就进入了死循环。你需要手动检查这是第几次运行,如果次数累计到一定程度,应该强制终止。yourMain的时候,全局或静态变量不会重新初始化。必须显式的重新初始化。
[code:c]
int yourMain(int restart)
{
// Your main function goes here.
}
int primaryMain()
{
int retval = 0;
bool running = true;
int restart = 0; // restart count
while(running)
{
try {
// Run the application
retval = yourMain(restart);
if (retval == 0) then running = false;
}
catch (std::exception& ex) {
// TODO: some error info ouput here.
restart++;
// Add code to decide if we should fail instead of restarting
}
catch(...) {
// TODO: some error info ouput here.
retval = 1;
running = false;
}
}
return retval;
}
int catchMain()
{
try {
// Run the application
return primaryMain();
}
catch(...) {
// TODO: some error info ouput here.
return 1;
}
return catchMain();
}
int main()
{
// TODO: set up something you need, like debug stream
return catchMain();
}[/code]
It seems a lot of stuff is attributed to Gauss…
either he was really smart or he has great press agent.
May be he just had a magnetic personality.
最近在看《具体数学 Concrete mathematics》,这是一本值得计算机相关专业任何年级的同学阅读的书。
第一章通过三个问题:The Tower of Hanoi; Lines in the Plane; The Josephus Problem. 前两个问题以前学习的深度已经够了,所以看了没有太大感觉,Josephus Problem的解决给了我很大的震撼。一个看似平常而无捷径的问题,通过用二进制表示,最终居然只要用一个循环移位就能解决。之后又扩展到 Josephus方程一般形式。其间,他的解题方法论很重要,以及其严谨的思路和发散的思维。
现列出书中第一章使用的解题方法:
- Look at small cases. This gives us insight into the problem and helps us in stages 2 and 3.
- Find and prove a mathematical expression for the quantity of interest.
- Find and prove a closed form for our mathematical expression.
已经翻译完成The Josephus Problem小节。对此感兴趣的点此下载(版本1.1.1,2007-12-23)。
]]>