Data Structure

今天(昨天) DS 考完试结课,第一次上姥姥的课有点感慨,晚上正好没有事情趁闲写一个总结。

高中从 OI 过来的同学应该都会不太重视 DS 以及接下来的 ADS 课程,因为这两门课里面学的数据结构以及算法在高中的时候已经被我们写得滚瓜烂熟了。但是结课后再想想,其实认真学与否真的存在很大差别。

写程序时心态的变化

栈、表、队列、堆,这些最基础的数据结构和 DFS、BFS 这种最基本的算法我们几乎已经熟悉到可以无视他们的地步了。但是他们是出于什么目的被设计出来的、其中有哪些需要注意的地方,是只学 OI 很难接触到的。看到作者在书上每一次

1
malloc
1
pop
等都要检查内存溢出,每一个可能出现不可预知错误的地方都会加上处理,就突然明白这作者其实是在以软件开发的角度在教学,与单纯做题不是同一个等级。

我们用的这本教材给出了比较详细的代码实现。每个数据结构都会根据 ADT 先把所有的操作和结构定义在头文件中给出,然后再在实现文件中给出具体的代码。开始的时候按照这种方法写了栈和队列的实现,感觉比之前想用的时候直接在具体的程序中撸一个要优雅和靠谱许多。后来时间太紧张就没再继续,想想如果每个都坚持这样做,课程结束的时候就可以拥有一个自己写的库了。虽然不比成熟的库实现,但作为课程的一项成果也是蛮好的。

代码风格的变化

命名方面,书上的代码基本上所有的变量、函数都用了首字母大写的驼峰命名法,我在实现的时候感觉略微不科学。在 OOP 中,首字母大写通常表示类名,在 C 里完全没有必要这样做(而且按得好麻烦啊)。一般来说,使用

1
typedef
声明的结构名,比如
1
List
1
Heap
这样做有类似 OOP 中类的意思,是可以说通的,不过函数和普通变量感觉就没有必要了。

另一个印象比较深刻的地方就是指针的显式化命名,比如

1
node
1
typedef node *ptrToNode
避免了打很多星号,也在变量声明的时候就说明了含义。

报告

姥姥对报告格式内容要求很严格,我觉得是很有道理的。平时很多同学写报告的时候不太注意格式,即使用 Word ,方式也不太正确。排版其实也是门学问,应该正确使用 Word 的模板定义功能,让文字处理软件知道这里的内容属于哪一部分,而不是通篇都使用正文,最后再手动逐个调字号字体。正式报告英文正文应该使用 Times New Roman ,代码使用等宽的 Courier New 应该都是最基本的常识(最起码不要用 Comic Sans)。不过写了这么多报告,还是觉得这些文字处理工具都多多少少的有点 buggy ,所以用了 LaTeX(这应该开一个新坑来讲)。这里不多说,总之排版是很重要的事情,能做 Best Report Writer 也是一件值得骄傲的事情。

总结

姥姥上课是很认真地在讲课,连包袱都很认真地抛(虽然不好笑)。所以觉得选上姥姥的课但没认真上课的同学真的很可惜(后来快考试周我也没认真听,有点后悔)。对比学院其他专业课的老师,有的是科研型,科研做得很厉害但是对教学一点也不重视,上课就只是念 PPT ,还有的是虽然很认真,但逻辑真的很混乱,听课还不如自学。真正教学到姥姥这个水平的很少。我觉得如果学校能把教学重视起来,根本不会有那么多挂科的同学。我周围很多人挂科都是因为老师实在讲得太烂,上课听不进去,自学又不得法,最后只能放弃。暑假上的 Patt 的课和这学期上的姥姥的课让我感觉好老师真的还是很重要的,同时也是很少的。所以才要更努力去一个比较靠谱的学校吧。

冬学期要把每门课都跟上。

入了大件之后,生活一直过得比较拮据,看电影和出去玩的次数也少了很多,某人一直很不开心。上次 MSTC 聚会后,发现野餐真是种既省钱又很小资的户外活动。反正只要有吃的,某人是不会拒绝的。计划订了好久一直拖来拖去,今天终于成功实施了。

前期最主要的工作是选址。其实这个季节出去野餐还挺尴尬的,想想两个人坐在寒风中冻得牙齿打颤还在顽强地吃着食物的画面,路人如果不是笑骂傻逼的话,估计会被我们的精神给打动吧。但杭州的天气到深秋才会表现出的早晚温差这个时候正好派上用场。中午太阳直射的时候,气温大概会到达一天中的顶峰,如果不是阴天,并且穿着深色衣服,其实还应该可以是蛮温暖的。于是完全被树木覆盖的公园肯定不行了,想来想去,还是西湖边上次聚会的地方比较合适。沿路有竹林和小溪,野餐点有一棵大树、草坪、阳光,游客比较少,还就在离玉泉不远的地方。

food

一大早上完课,各自乘校车到玉泉会师。买零食,出发。虽然提前觉得温度没问题,但我们两个还是都上了冬衣加围巾这种最强装备。到地方的时候,气温还算可以接受,就先选了一个树阴的地方,结果冷风吹来还是受不了。于是移到太阳地,一下暖和起来了。

laptop

铺在地上的单子就是普通的桌布,超市里不到十块钱一个,一次性用完即扔。零食一大袋。电脑、手机、平板、单反作为娱乐设备,野餐就正式开始了。开始用电脑建了一个热点,手机当手柄打 Bombsquad ,可惜电脑的屏幕在阳光直射下实在很难看清楚,只玩了一小会就受不了了。之后玩了一会单反。下午趁着暖和背靠背看了一会书。太阳下山之前我们就收拾东西打道回府了。

canon

不得不提的是下午成群结对出现的在附近拍婚纱照的夫妇们。在这事儿上男士还真就是个道具,他们不停地被摄影师要求拍各种姿式,一遍不行再来一遍,还不能一点儿不耐烦。我在想要是我被这样摆弄一下午早就疯了,谁爱拍谁拍,老子才不伺候呢。后来冷静下来想了想,这样也不现实,其实折衷的方法是找一个假人啊模型之类的拍,然后再把自己后期上去,我觉得应该能实现,贵点不要紧,才不受这气。

回来问了问某人的意见,大体上还算满意。现在一想到出去玩,总是吃饭、唱歌、看电影这老几样,既花钱又没意思。想来还不如多点这样的户外活动(虽然我还是想带着电脑打游戏)。出门,走路,呼吸新鲜空气,这才是健康的娱乐方式吧。

steps

Turing Machine

前段时间看到 Scott H Young 那篇介绍费曼技巧[1]的文章,一下想到了程序员界流传甚广的小黄鸭调试法(Rubber Duck Debugging)[2]。几天前晚上散步的时候,聊天儿时提起了罗素悖论,让我联想到图灵对停机问题精妙的解法。想拿这个试试费曼技巧,讲到一半发现卡壳了,自己把自己绕了进去。我想到刘未鹏说过的那种状态,学习一个算法,初看觉得太牛逼了,然后花十分钟理解了人家花了十年才想出的成果,再花十天完全忘记,只能想起那个算法十分精妙,该不会的还是不会。所以应该由本溯源地学习数学和算法,并且写一个博客记录下学习的过程。于是这篇就作为费曼技巧和写作学习实践的第一篇。

在计算机科学领域,最广为人知的问题是关于 P 与 NP 问题的讨论,但不论 P 还是 NP,它们只是对解决问题效率的讨论。其实还有一类问题,它们难到计算机永远无法解出,这类问题被称作不可判定问题(Undecidable Problem)[3],其中一个可能是最有名的问题就是停机问题。

停机问题提出了这样的疑问:是否存在一个程序,可以判定任何一个程序是否会停止。

要解决这个问题,我们先假设存在一个程序满足上述条件,比如:

int yourBrilliantProgram(char * program, inputType  input){
    if (program(input) stops)
        return 1;
    else //It loops forever
        return 0;
}

其中,第一个 if 语句中的判断是人类智慧的结晶,它用了神奇的方法得知了 program(input) 是否停止。

接着我们就要进行破坏了,再来考虑这样一个程序:

int myDisgustingProgram(char * program){
    if (yourBrilliantProgram(program, program)){
        while(1);
        return 0; //Never reaches here
    }
    else
        return 1;
}

我的程序专门跟你作对,如果你说这个程序可以正常退出,那么我就作一个死循环;你说它是死循环的,我就正常退出给你返回一个true

So far so good. 最精彩的地方来了,考虑 yourBrilliantProgram(myDisgustingProgram, myDisgustingProgram) 的返回值。这里有点绕,我们一步一步跟踪。

首先,进入 yourBrilliantProgram 的第一个判断语句,这里执行 myDisgustingProgram(myDisgustingProgram) ,进入 if(yourBrilliantProgram(myDisgustingProgram, myDisgustingProgram)) 这一句。这里可能出现两个结果:

  • 返回 1,说明 myDisgustingProgram(myDisgustingProgram) 正常退出,那么当前程序进入死循环,此时上一层的 yourBrilliantProgram() 应该检测到这个死循环,并返回 0,说明 myDisgustingProgram(myDisgustingProgram) 是个死循环。矛盾。
  • 返回 0,说明 myDisgustingProgram(myDisgustingProgram) 死循环,那么当前程序返回 1 并且正常退出,又证明 myDisgustingProgram(myDisgustingProgram) 不是死循环。矛盾。

也就证明,根本不存在这样的程序可以判定任何一个程序是否停止。

Deadline

蔡式效应

蔡式效应又名蔡戈尼效应,大意是说人们对未完成的工作往往有很深的印象,换个好听一点的说法也可以表述为人们天生有种办事有始有终的驱动力。我在这学期的积极心理学课上接触到这个概念,蔡式效应被看作拖延症的成因之一。

蔡式效应和拖延症

这两者的联系在什么地方呢,其实原理也很简单。心理学认为,人们在全身心地执行一件事情的时候,需要消耗一定的意志力。而如果之前有未完成的工作积压,那么它们就会持续地占用一部分意志力。因此我们不能专注于当前的工作,导致低效,甚至无法开始,即对当前工作的拖延。这将变成一个恶性循环,最终只能要么放弃一部分工作,要么完成所有工作,但质量低下。

期中考试周的时候我就经历了一段这样的生活,几个课程论文的 Deadline 逼近,我还完全没有开始,却又要为了应付即将到来的考试而复习。我想先把论文的事情往后放放,就算过了死线也可以事后补交,先应付考试是要务。但结果是复习效率极低,从考试结果来看也差强人意。

平时我是很注重效率的,这样花了时间却没有收到成效的事情让我十分懊恼。积极心理学刚好讲到拖延症这一节,听完以后觉得收获很多。

解决办法

对于蔡式效应导致的拖延和低效,有一个简单的办法可以解决。先举个例子,你大概有这样的经历:在街上听到一家小店里传来一首口水歌的一个片段,然后接下来一整天你的脑子里一直萦绕着那一句单句循环。这其实也是蔡式效应的一个体现,解决办法是插上耳机,把那首歌从头到尾完完整整地听一遍。亲测有效,你可以试试。从这个例子可以看出来,抵御蔡式效应的办法就是把未完成的事情做完。那怎么解决像我上面提到的那种情况呢,论文是没写完,但我还有更重要的考试要准备啊,我总不能放下考试去写论文吧?当然不用。用老师的话讲,大脑「很傻很天真」,你其实不用真正地做完这件事,你要做的只是给它一个提示,告诉它「这件事情已经『处理』过了」。具体怎么操作呢?

GTD

GTD 是 Get Things Done 的简写,它是一种时间管理的方法。具体的内容可以参见 Getting Things Done: The Art of Stress-Free Productivity 这本书,中文译本是《尽管去做——无压工作的艺术》。它提供了一个有效的手段来对付蔡式效应,让人可以全身心的投入到当前的工作中。

它要求你首先把所有要做的事情罗列出来,称为「搜集」。然后按照任务的类别和特点进行处理归类:马上要做的事、委托别人做的事、要延期的事、要存档的事、要扔掉的事。注意归类这一步很关键,它对任务进行了一定的处理,让大脑认为我们已经完成了对这件事的操作,而其实我们本身并没有执行这件事。一件事情完结,蔡式效应失效,我们大脑中的工作台被清空,专注于执行当前要紧的事。

GTD 是一套完整的时间管理方案,我只是提取其中这一点来说,如果还想了解其他的话可以买本书来看看。另外,推荐这款最经典的 GTD 工具—— OmniFocus ,虽然贵了点,但至今没有任何其他相关工具可以超越它。

GTD 的副作用

用 GTD 的方法可以解决蔡式效应带来的问题,但就如同开头提到的,蔡式效应并不是完完全全不好的东西,它常常是人们办事有始有终的驱动力。GTD 解决了蔡式效应的副作用,但也要警惕它消除了蔡式效应给我们提供的驱动力。所以常常会有这样的事情发生,你信心满满地去作了一大堆计划,但作完计划后你就把它们抛诸脑后,再也没有理会过。原因就是大脑认为你「作计划」的这个动作已经把这些事情完成了。

参考资料

  1. 蔡式效应 (The Zeigarnik Effect)
  2. 罗伊•鲍迈斯特 意志力:关于专注、自控与效率的心理学
  3. David Allen 无压工作的艺术

Hair

为了吐一个漂亮的槽,我耗费了整整一下午的时间。

当我睡了一觉起床看完这一周的《生活大爆炸》,然后在调戏完群里的同学后又连着看了两集《暴走大事件》,最后把梁边妖大人的所有回答都仔仔细细地全部研读一遍而心情还是没有任何好转的时候,我就在思考如何才能吐好这个漂亮的槽了。

我这么高冷,我会对着大街喊「全天下的理发师全都是臭傻逼」这么屌丝的话么?我不会。

我这么不羁,我会仰望天空眼含泪水说「理发虽易,理好不易,且理且珍惜」这么矫情的话么?我不会。

我这么正直,我会做出从此以后努力学习黑掉他们的收银系统然后把所有人的帐户都清空这么没品的事么?我不会。

因为我知道,这一切都是注定了的。

我从不畏惧走进这家理发店,因为当洗头小哥按照惯例搭讪完在哪上学上几年级学校怎么样家在哪里住着为什么到这么远的地方上学北京有清华你咋不去呀这几个问题,准备正式进入主题,并且抛出那个看似转折平滑平淡无奇实则暗藏杀机危机四伏的问题「你,是不是我们这的会员呀?」的时候,我可以嘴角微微上扬,眼神里透露出一种似不屑非不屑的不屑,手不经意间地从钱包里掏出那张金黄色的卡在他面边不经意的晃一下然后迅速放回去,保持着嘴角的微扬慢慢的吐出那几个字「我,是会员。」一般这个时候,识相一点的都会被我强大的会员光环镇住就此闭嘴,我则可以安心地躺在躺椅上,等他安静地完成后续工作。

事情按照我预想的节奏有条不紊地发展着。直到遇到那个理发师,请大家记住他的编号,他是 10 号。十号只轻瞄我一眼,眼神就游离到了其他地方,象征性地问过我的意见之后,在我还没开口的时候就已经拿起剪刀剪了两剪子了。

卧槽,那你问我干什么呢?

我顿时心生畏意,这货之前不是玩摇滚的吧?带着 Fuck off 的精神来报复社会?想到这里又有些小激动,他会不会给我弄个麦扣杰克森出来呢?

摘下眼镜的世界,模糊,美好,充满诗意,也充满了对未来的不确定性。

再次戴上眼镜,眼前的这个人已经不再是我之前认识的那个人。我凝视了他五秒钟,然后对他做了个鬼脸,看到他也不客气地对我做了个鬼脸的时候,我深深地意识到,没错,这他妈的确是我。

洗头小哥再一次接手,他说「今天这个头理得真帅」的时候,我全身打了一个冷颤。我定睛看了看这位小哥的发型,我意识到了他审美下的「真帅」。

真帅。

接下来的事情记忆变得恍惚,我不知道怎么骑了车回到学校锁上车子打开楼门走上楼梯进入宿舍再坐到电脑前的,等我反应过来,我已经坐在电脑前了。

再起身,看一眼镜子。你们这些以发型取人的傻逼,你们以为我会在乎你们的看法吗?你们以为我会在意你们的指指点点和鄙夷的目光吗?我混了这么大,是靠头发混出来的吗?

有人说,没图你说个XX。

你傻吗?

要是敢放图,我他妈还用吭哧吭哧说这么一大堆骗你?