SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  207 随笔 :: 19 文章 :: 1637 评论 :: 12 Trackbacks

2004年6月29日 #

我最近用Linq2Sql比较多,积累了一些小小的经验,也遇到一些挫折。今天只讲其中一个挫折:Linq2SQL与AsQueryable水土不服。

 

在我的一个项目里面,经常发现系统无故Down掉。这种Down掉以前从来没有遇到过,因为系统的EventLog里面没有任何记录,try...catch也不管用——即使用了try...catch,也仿佛是遭遇了逻辑黑洞,整个进程就在那一点突然消失掉。由于第一次发现这个问题的时候,是在一个能够根据用户输入的条件拼装查询条件(Expression)的地方,因此怀疑可能是表达式太复杂了,.NET的某些部分出错了(比如说编译出错)。由于时间比较紧迫,最终直接改成不是产生表达式,而是直接进行Where等函数的调用。由于Linq2Sql用的是IQueryable接口,实际上是要到最后才产生最终的执行调用的,于是这么改性能不会变差,甚至可能会变得更好也不一定。这么改了之后倒是正确了,于是也没有仔细考究。

 

可是,后来程序又莫名其妙的崩溃了。有了之前一次经验,立刻把问题定位到Linq2Sql的部分。该页面唯一一个比较特殊的地方,就是用了一个AsQueryable。具体的情况类似网上的一个已知的问题反馈:

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=355026

 

int[] ids = new [] { 1, 2, 3 };
IQueryable<int> queryableIds = ids.AsQueryable();
var query = dataContext.MyTable.Where(x => queryableIds.Contains(x.Id));
Console.WriteLine(query.ToString()); // This fails

 

网上其实还有很多其他的人也遇到了类似的情况,例如:

int[] ids = dataContext.MyOtherTable.Select(x => x.Id).ToArray();
IQueryable<int> queryableIds = ids.AsQueryable();
var query = dataContext.MyTable.Where(x => queryableIds.Contains(x.Id));
Console.WriteLine(query.ToString()); // This fails

 

有人(貌似是MS的人)就说,这个改成join,或者改成 var ids = dataContext.MyOtherTable.Select(x => x.Id) 就好了。得到这样答案的人固然欢天喜地,确实不出问题了,只是大家都会有困惑,为什么原来的写法就有问题呢?实际上是一个Bug造成的,而且似乎是Native部分的堆栈溢出,只是这个Bug在我上面查到的页面里面说是已经给修正了。可问题是,这个修正发布了没有?在不在VS2008/.NET 3.5的SP1里面?没有说。而且这个问题怪就怪在,似乎遇到问题的人不多,连国外的网站都找不到这个问题的解决方案。(难道我太孤陋寡闻了?)

 

找不到补丁,又没有人明白说如何解决,只好自己想办法定位一下。既然是在使用了AsQueryable后才出的问题,那么在AsQueryable那边出问题的可能性就比较大了。由于实际上是一个native的stackoverlow,而且try...catch失效,无法定位出问题的位置。假如重做整个代码,然后进行调试,也许能够在中间某个断点被截获,最终可以发现问题所在。AsQueryable实际上是用EnumerableQuery<T>(一个internal的类,用Reflector才能看到)把IEnumerable<T>包装了一层,于是呢,我找了个周末,把整个EnumerableQuery相关的代码复制出来重做了一份,除了ExpressionCompiler是直接通过反射调用的之外。结果让我非常诧异,竟然完全没有任何作用——我自己实现的EnumerableQuery里面的任何地方的断点都没有到达!换句话说,问题可能不在AsQueryable里面,而更可能是Linq2Sql的某个地方。目前我的分析是,由于System.Core.dll已经本地化为NativeCode,因此某些.NET CLR的保护机制在某些特殊情况下可能会失效,所以才造成了自己实现EnumerableQuery无法得到断点,同时整个进程也还是悄无声息的崩溃掉。关于.NET CLR保护机制失效,请参见MSDN里面的CER。

 

目前这些都是猜测,想看看博客园是否有人能够找得到这个问题的答案。我更想知道的是,除了放弃使用AsQueryable之外,还有什么解决办法。目前似乎也只能够是尽可能把各函数的参数和返回值修改为都是IQueryable的,但是估计难度有点大。

 

P.S.:

似乎Linq2Sql还有别的地方有Bug,我忘了具体的情况了,当时情况类似:

int[] ids ={1,2,3};

var q = from item in SomeCallWithIQueryableResult()

          where ids.Contains(item.Id)

          select item;

需要说明的是,SomeCallWithIQueryableResult()这部分内部也有一些处理。最终报告的是不支持IEumerable的Contains操作,这个错误网上也有见到,建议的解决方案是想办法用join来替代。这个Bug我没有尝试进行简化重现,时间也稍微久远一点,所以描述的可能不太准确。但可以肯定地是,当时确实报错了,而且感觉上比较莫名其妙。你们还遇到过什么别的Bug吗?

posted @ 2008-09-03 09:32 Sumtec 阅读(1314) | 评论 (8)编辑

我完全没有想到,上一篇的随笔那么快就有那么多的反响,感觉在后面回答各人的问题和意见,会是一件很漫长的事情。所以最终我决定还是补充一篇——貌似我很多的随笔都会出现这样的状况。
首先我想说明的是,我是从一个反面的角度去写的:并不是总结自己写简历的心得体会,而是我如何期望一份好的简历。当然了,我不可能全都是对的,你也大可不必相信。但至少我觉得如果我看到一份我所说的那样一份简历,会比较吸引人。有人给出两份在线简历:
http://catchen.biz/
http://www.wangjunyu.net/
这两份简历我没有细看,也不是特别有兴趣点评。不过既然有人提到了,我也就稍微花了两分钟看一下。各人感觉都还可以,至少有一个介绍自己做过的事情,并且有一个可以看作品的地方。当然了,这是在线简历的一个好处。此外,履历部分写的还算简洁。问题也有,第一个是履历上没有特别突出自己最重要的经历,同时也没有说明这些经历对你的人生产生了什么影响(其实说一个就好)。第二个是,作品内容太多了,目不暇接。最后一个是,你最擅长的是什么写得不够好。我一个个说明一下:

1、其实有人提到了,说其实你现在会什么不重要,重要的是你的学习能力如何。请站在面试官的角度想想看,你面前摆着一份简历,这个候选人的学习能力如何是怎么看出来的呢?比如说看这个人做过多少事情,这个是否可行?我觉得不可行,因为做过很多事情只能说明做过了很多事情。很有可能在你做这些事情的时候,你已经被教会做这些事情的知识了,不一定证明你的学习能力很强。我也不是说完全没有关系,只是会打比较大的问号。我说一个比较可行的:看看他过去的经历又没有遇到“全新的”挑战?如果说一个以前没有做过的事情,不会的事情,最后干得不错,你说这个人是否学习能力可能比较强?如果你同意这一点,那么想必下面这一点也应该是同意的:在简历里面,写出至少一个挑战,说明挑战是什么,如何挑战成功的,对你人生的帮助是怎么样的。
还有一种办法,就是看这个人是否会最新的东西,尤其是有较长工作经历的人。比如说一个工作三年的候选人,他居然会Linq或者WCF,你可以断定这些知识肯定不是上学的时候老师教的(其实即使是现在正在上大学的同学,十之八九也不会学习Linq或者类似的知识)。好,既然不是老师教的,工作期间也不太可能上什么进修班,那么自学的可能性应该比较大。至少也是在有人点拨的情况下,学会了这些知识。这个逻辑问题不大吧?其实“学会”这个说法比较笼统,什么叫做会呢?我想各人看法不一样,也没法说清楚。那么如果你在项目里面应用了,那就至少是实战过的,准确性又高了一步。

我想看果上一片随感的同学们,一定会有一部分人这么想:
会WCF又怎么样?三五年还不是旧知识了!再说了微软的技术就好了吗?
没错,我从来也不会说谁谁家的东西就怎么样了。不过不同的公司一定是有自己的方向,如果你不喜欢这个方向,别投这个公司简历就是了。但站在特定公司的角度讲,总是要考察本公司必须要用到的知识的,例如微软相关的知识。那么此时拿WCF来考察,就比较靠谱了。其实在回复里面,也有人提到了:“……贵是贵在了解它的为什么……”没错!这也是我的一个重要的标准,这就是我问“interface它为什么存在,到底是干什么用的”这个问题的原因。可就这样的问题,十之八九不是回答不上来,就是不屑于回答。interface这个玩意儿出现的时间可比design pattern早多了,如果看英文资料就知道,这玩意儿在OO之前就有概念。这么经典的东西都只是知其然,不知其所以然,那我就可以断定,你的“精通”和我的“了解”还差那么一点点;还可以断定,你写出来的东西很可能是不符合计算机编程的科学规律的。
说白了,我的“会什么”,要求是很高的,至少你得能说出个所以然来——不一定精确命中或者精辟,至少打个擦边球说个意思出来总要吧?

2、内容太多,基本上就是我认为最头痛的一个问题。如果站在候选者的角度讲,也许没什么感觉,甚至恨不得把所有的东西都拿出来炫耀一番。但是不要忘了,你是去应聘,你写多少人家不见得看多少。不知道有多少人看过卡耐基的《人性的弱点》,没看过的真建议看看。这本书我是相见恨晚,它教会我如何做人。其中就有一部分告诉你,你要了解对方的需求,才能达到你的目的。据一个书中的例子:有位做银行高层的女士,因为小孩读书的缘故,要搬到别的城市。为此,她需要重新在当地找一份工作。你觉得她会怎么做?比如说,给该城市的十二家银行发简历说,我儿子要来你们城市读书,所以你们行行好吧?还是说,我有多少年的工作经验,我希望我能为你们银行的进一步成长提供帮助?显然后者更有效,前者说的是你想要的,对方其实不想知道。这个例子对于我要说的内容来说略显夸张了,也有点不贴切,但意思是一样的——你要换位思考一下,对方需要什么,对方是一个什么样的心态。对方如果手里有一个空缺名额,那么少则要看20份简历然后面试大概6位,多则可能看几十然后面试其中的大约10位。那么大家都想着多写两段话,也就意味着面试官可能将要多看几十段话,面试官会很头痛的。
说到这里,我就想,至少有一半看官会想?有那么夸张吗?不就花两分钟时间看一份简历么,一个小时能看20来份简历呢!可是,你看完之后,如果我随机挑出中一个,让你说说对这个人你有什么看法,能否说的出来?我想这些同学做面试官的时候,很可能都会犯下和我曾经犯过的同样错误:面试的第一个问题,就是“请你花2分钟时间介绍一下自己”。啊!这有什么啦,很正常啊!在我说出我的结论之前,我说另外一个场景,然后再问一个问题:
你是面试官,A同学来面试,你问问题他都回答得非常好。现在轮到他提问了,他第一个问题是:你们公司是干什么的?
请问:你会怎么想?
我不知道你怎么想,我的想法是:我X,搞了半天你连我们公司是MS阵营还是Linux阵营,搞IT还是搞TI的都不知道,你投我们公司简历干什么?来之前就没看看我们公司的网站?多半你对我们公司也不怎么向往,十之八九你都在研究MS或者Google它们公司干的什么吧?
那如果我是应聘的候选人,你是面试官,你第一个问题就问我:你介绍一下自己吧?我肯定这么想:我X,我简历上面不是写的清清楚楚的吗?要简历来干什么,你什么都不看,多半要么看不上我的简历没印象,要么就是你们公司对人才其实就不重视,至少我未来的上司或者同事对我并不关心。

所以啊,一个认真负责的技术领导,在面试之前,应该要做认真细致的工作的,不是两分钟就完事的。我想大概平均一个简历下来,我要花上大概10分钟,也许还不止。我会记录下,这份简历的疑点在哪里,例如为什么工作经历有断档,为什么某些地方前后似乎有矛盾,为什么有些说精通的地方在工作中从来没有用过,说了十几个精通,到底哪一个才是最强的;我还会记录下,这个候选人需要问那些问题,是管理方向的哪几个问题,还是技术方向的哪几个问题,还是兼而有之;我还会争取顺便看一下简历中提到的,网上能够看到一些成果。这么一圈下来,十分钟还真是比较保守。

有那么复杂吗?有!你想,你如果是领导,手下的人是否能干是多么重要的事情啊!能干的人,你就指挥指挥,点拨点拨,不用操心,准时完成;不能干的人,你就亲自坐镇,催促鞭挞,不时要擦他屁股,能否完成都是个问题。如果你手下有4个好用的人,基本上你原来手上的事情就可以全部分出去了;否则,基本上你比以前还忙。这么重要的事情,难道专门花个三五天来准备准备不值得吗?如果你能招来一个能完成现在你手上所有事情的人,能有什么比这件事情还重要?你现在手上的工作?别开玩笑了!

我这篇文章不是说,面试官如何准备面试的,所以也不再继续展开了。现在你明白了吧,你的简历如果简练且生动,对负责任的面试官来说,是多么的有吸引力啊!当然啦,我不是说你明明做了20个项目,你就提都不提,而是说可以参考上面列出的简历中,履历部分的写法,简练说明即可。完全不必每一个都写:用ABC.NET、OrnDB技术以及Junk语言,构建了一个BC结构的系统,该项目用0.5层技术,实现了XXX、YYY、ZZZ的功能,全程参与了该项目的开发。都这么写,我看起来就跟看了一堆的“BlahBlahBlahBlah”差不多。人是有审美疲劳的,没有特点的简历,最后结果就是“Zzzz……”。(夸张了点)

更头痛的是,在线或者不在线的作品介绍得太多了,却没有侧重性说明。如果你有十个网站作品,BlahBlahBlah一列,那我看那个好呢?随便吧,看看第一个吧。看完第一个我一看表,时间不多了,剩下的就算了吧。也就是说,你列出这么多,其实没有达到你的效果,白列了。甚至本来挺出色的,却因为太多东西了,让我觉得没有惊喜的感觉,可能错过了也不一定。如果真这样,两个选择:1、你就详细介绍两个精品,剩下的简单罗列一下,面试有兴趣再详细介绍;2、有侧重性的介绍,让人知道你为什么要列着一个项目出来,例如:
AJAX应用项目:某某项目,我遇到了什么困难,我如何解决,我学到了什么。
SilverLight项目:……
……
其实我倾向于第一种方式,理论上你就应该给不同侧重方向的公司提供不同的简历。如果我这个公司搞的Linux,你大谈WCF与我何干?如果我公司搞的编译器,你大谈GIS系统与我何干?实在是有客观条件限制,例如是在招聘网站上提供简历的时候,才会采取第二种措施。

3、对你擅长的东西写得不够好。或者说你没有详细介绍你最强的地方,这个最致命。你有10项全能,每一项能力值不一样,有高有低,假如说分别是1到10,越高越好。那么很详细的全部罗列出来,给我的感觉就是你大概水平是5.5(1+2+...+10/10=5.5)。如果你简单的全列出来,我可能会随机挑一个我关心的(面试时间不可能让我什么都问清楚了),也许正好是3那一项,于是你就是3。如果你挑了一个符合我们公司方向中的最强的一个,比如说是8,那么你给我的印象就是8了。效果哪一个好,自然不必说。其实对我来说,我知道有8的那一项能力,我就可以认为你可能能胜任和这一项工作相关的事情了。至于你3的那项能力,其实也许不是每一个工作岗位都是必须要的。所以从面试官的角度来说,其实也应该是欢迎这样的写法的,至少不会白白放跑了一个人才。

看我写了这么多,应该知道,负责任的面试官,尤其是业务相关的面试官,更应该仔细准备他要提的问题了吧?有人说,到了技术面试官的这步,就不看简历了,我觉得这真是大错特错了(我以前也犯这样的错误)。“稍微Senior一点的职位,基本上都是face to face的聊天了。”Face to face没错,电话谈话不算是严格正规的面试,因为你看不到对方的面部表情。聊天就不对了,聊什么?欧洲杯吗?还是NBA?你要聊的,应该是技术吧。要聊得技术,应该是你们需要,他也懂的吧。不看对方简历,你知道从何问起呢?也许简历里面就已经告诉你,其实我是一个啥也不懂的人,或者其实我是一个很粗心大意的人。你都不看,怎么全面了解未来你的准下属呢?我就看过这么一个简历,简历里面错字连篇,甚至把CPU写成CUP了,也不给改改,排版格式几乎没有。如果不是HR不懂技术,又或者我当时有时间亲自筛选,也许就不会被邀请面试了。看到这个简历,你就基本可以判断此人不是追求完美之人,代码风格或者排版会比较差,很可能会粗心犯错。最后这个人录用试用了,不得不说是幸运——完完全全是因为没有由于简历的缘故被刷下来。我看其他简历的时候,是看完了简历内容,写下疑点和问题后,再写下我对此候选人的兴趣有多大。而我看这份简历的时候,就是先写上无兴趣,然后再耐着性子把疑点和问题写下来的。最后也得亏我认真的做了准备,在我有针对性地提出问题之后,发现其实此人懂的东西还不少,对一些东西大概还是知其所以然的。如果我没有准备,天南地北随便乱侃,即便侃的是技术,我也不见得能发现这个潜在的可能胜任职位的准同事。

我的结论就是:其实如果是负责任的面试官,会好好给你看简历的。人家都说简历是一块敲门砖,自然是有它的道理的。不好好看你简历的公司,没有文化底蕴的可能性会偏高。我推测可能有些很有名的公司,面试工作其实也做得不咋地。但是咱们平心而论,不咋地的公司,会有更高的概率不好好对待你的简历。

其他同学的观点或者问题,我就简要回答一下吧:
Q:技术人员、管理人员和纯HR,看得东西不一样。
A:没错,当然不一样了。HR更可能和你闲聊,更可能不认真对待你的简历,更可能看中你有多少多少的经历。技术人员会看你技术多深,自学能力如何,管理人员要看你为人处事如何,如何管理项目等。所以,如果你真有一个非常想得到的机会,准备3份不同的简历是非常值得的。你可以告诉HR:这份简单的介绍,更便于你了解我的个人众多经历,至于比较详细精确、便于技术领导深入了解的是另外一份简历。如果您看了我为您准备的简历后,认为我可能适合面试,请把另外一份为技术领导准备的简历交给技术面试官,非常感谢!我猜测比较负责的HR,应该会照做的,前提是你的经历确实能打动HR。(不按照我说的方法,你也至少得有这么一份能打动HR的简历吧?)

Q:写出原来工作的内容涉及泄密。
A:当然不能干涉及泄密的事情了!但不能泄密不代表你什么都不能做了,否则你打算如何写你的工作经验呢?B/S结构不算泄密吧,那么我画一个拓扑图,里面有一个服务器,一个客户端,不算泄密吧?进一步画出一个数据库,也不算泄密吧?你实在把握不住的,可以请求你原来的上司过目,作一定的删节。一般来讲,不涉及具体数目和详细的功能,应该不会算作泄密的(当然,最好咨询一下原来的公司),因为有些东西是可以推断出来的。比如说,你做的一个数据密集型的系统,假设某银行的结算系统,谁都能够猜到,这大概会有一个数据库集群,那儿会有一个应用服务器集群,还会有一堆的备份服务器等等。这要算泄密就怪了,应该是业界的公知常识才对。当然,你别把非常具体细节的东西都画出来。画出拓扑图来,目的有两个,一个表明你确实是业内人士,大概处理多大范围的事情,另一个是减少文字描述的数量,让人花五秒钟就知道你原来的经验到底是什么样级别的。用到什么技术的介绍,也完全可以在不涉密的范围去写。难道说你原来公司的客户问你,我要装你的系统,要准备好Linux系统还是微软系统,你原来公司会回答保密?是否要装.NET 3.5这也保密?这生意还怎么做!甚至不少公司会宣传,我们的产品是采用了3层结构的,扩展性、稳定性良好。这些原公司公开的,那当然是可以说的。
前面我提到的,让原公司上司给过目,要求你离职时在原公司仍保持有良好的形象。其实,如果你做得好,甚至有可能拿到介绍信或者推荐信,这比你自己的简历又高了个档次,这是题外话。至于说保持形象,如果你真想越做越好,这点很重要。即使你不想按我说的,因为想避免泄密而要照原来的上司过目,良好的形象也是很必要的。因为你参加面试的公司有可能会找到原单位核实情况,甚至了解你的为人如何,和其他同事的关系如何等。你要是形象糟糕,你说你的原上司会在这个时候说什么?扯得有点远了,呵呵。

Q:在校项目就一定差吗?
A:当然不一定啊!但通常而言——是的。这个从专利申请的质量就可以略知一二,高校申请的质量通常无法和商业公司的申请相比较,也许有些学术性比较强,但是通常确实是有差距的。如果你身边有认识的专利审查员,或者专利代理人,可以核实核实,看看是否和我说的类似。当然这不能特别准确的说明问题,甚至不准确,但至少也是一个侧面嘛!

Q:"Interface这种东西需要懂吗"  vs  "LZ不要觉得那新出的技术问人能考验这个人能力,恰恰是他以前积累了多少,能看出他是不是一个善于学习和总结的人"
A:想必大多数人这两个问题只会支持其中一个吧?如果两个都支持,就有点矛盾了,连总结(或者学习)为什么会有Interface都不能做到,何来善于学习和总结呢?顺便也说一下,面试的时候,是需要列出你要考量的有限的若干个维度的,例如:自学能力,总结能力等等。我问Interface事想要知道总结能力如何,或者是否对思想性的内容感兴趣;问WCF则向知道是否对新技术保持着旺盛的好奇心,是否能自学。其实这两个并不矛盾,甚至是相辅相成的。大家没必要拿着其中一个命题攻击另外一个不冲突的命题。另外,我这里给大家说的是,面试官会如何考虑问题。你是否愿意按照面试官的需求供应你的产品,那就是你的问题了。也就是说,你完全可以带着下面描述的情绪去写你的简历,这没有人能阻止你,但你也确实无法控制HR和面试官如何筛选你的简历。

Q:
HR都是小白
技术总监都是装B
老板都是骗子和吸血鬼

记住这三点,就够了

很有道理, LZ也有这个味道

你们是什么的XX公司呢,你们招的又是什么用的人呢

要求别人很多的时候,也是你们应该反思的时候
A:这种说法没有道理,绝大多数不代表一定这样才是好的,更不能证明就只有这样的情况。举个例子,大多数股民都比较傻,这不是你就应该同样稀里糊涂的进入股市的理由。也许你遇到很多小白HR,装X的CTO,吸血鬼老板,但是这不代表你就只有这样的选择。如果你带着这样的情绪写你的简历,不属于你描述范畴的人估计是不会看上你的简历的,于是最终结果就是越来越有待在你所描述的地狱圈子的趋势。
我有这个味道?你说的没错,兴许我确实是有的,只不过我没有发觉。不过至少我意识里面没有这样的东西,因为我不需要。招人的时候,我的目的是要招人,不是装X自我感觉良好一番。难道说我装X就能招到好用的人?明显不可能嘛,简直就是南辕北辙。装X肯定招不来好的人,至少你就不服,其他真正牛X的也不服。我这里写这么多,也只是想分享交流一下,说说面试官的需求,好让大家找准需求而已。也许我说的是错的,没有道理的,你也可以不相信,我也乐于承认自己的不足。
我们公司不牛X,也没有要怎么显摆的意思。只是招人谁不想招到好的、合适的人呢?因此用心去做面试之前的准备,问一些必要的问题,不能说是我们想要装做很牛的样子。Interface这种被人鄙视的问题很高深吗?不算吧?
我们招的什么用的人,可以略说一下:鉴于公司当前所处的阶段,不是最底层的,因此需要一些管理方面的经验,或者如果技术能力特别强的也可以。当然,不是说这就是我们的一贯标准。公司成长的不同时期,对人员的需求是不一样的。这点就不多说了,不是本文的目的。
此外,我也想问问,如果说HR都是小白,技术总监都是装B,老板都是骗子和吸血鬼,那么普通候选人是啥?我真不知道,请教请教。不过我猜你肯定是很牛X的了,我在你面前自当是班门弄斧,你就权当没看见好了。见谅见谅!
至于说到反思,我想我一直站在这种角度去思考的。我认为,作为一个管理人员,要思考的是如何为下属提供优质的服务。比如说,提供各种资源,甚至提供提升员工共能力的机会。我的做法是,尝试每周做一些简单的交流活动。

Q:既然叫“简历”,就应该突出它的“简”和“历”。
A:没错。我一直想表达的也是类似的意思,也许我表达能力比较差,没说明白。假设一个简历最多不超过2000个字,那么大规模简单介绍所有项目,还不如挑出其中一个详细说说,然后其它的就列出条目即可。同样的字数,我认为后者简单:我只要好好看其中一个介绍就行了,不需要忽略一大堆的内容。此外,这样写更可能写得更精彩一点。
补充说明:精彩不等于罗里八嗦,也不等于写个小说散文什么的,纠缠这个真没啥意思。你是一个什么样的人,这是面试官最想知道的东西,而不是你做过什么项目。要求你写出参与过的项目,也只是希望通过项目看出你的人是怎么样的,因此我认为精力和篇幅应该花在说说你这人怎样这上面。如果列出十来个不同的项目,结果自然不会比仔细描述一下你的思想效果好。这是我要表达的。

Q:我工作经历中有三个月在一个大公司干,然后试用期因为组织结构调整工作内容变化就离职了,这段要不要写,怎么写呢?
A:你在这期间是否真学到了什么东西?对你的人生有什么重要的转折作用?没有的话,可以不用介绍了。你可以选择列出来,或者不列出来,都没有问题的。面试之前我要准备的其中一件事情,是看简历中的疑点,包括了履历的断档期。但是有疑点不代表你是犯罪嫌疑人,只是这些东西需要问清楚,万一呢?对吧。如果你不是有什么不光彩的事情,无论写不写,面试官问到了简要回答一下即可。面试官一般不会因为你没有写出无太大价值的经历,而感到你有什么可疑之处的。但有的时候,有人可能不愿意写出一次失败的创业经历,如果因为不做准备而没有问出来,可能会影响你对此人判断的准确性。准确说,这一点不是特别重要,只要不是刻意隐瞒一些重要的经历(哪怕是失败的)就可以了。

Q:看来又要开始写了
A:这个还真不好说,有时间有心情就写。其实时间硬挤出来是可以的,不过那样心理疲惫我不乐意。所以现在我的空余时间优先考虑修复自己的精神世界,比如娱乐娱乐。呵呵!记得有人也回复说,谁会自己愿意懒惰的。对了一半,人累了都应该有想要偷懒的心理,否则人类社会也不会进步了。只不过很多人在这个时候会用不同的方式鞭策自己,至于我嘛,我认为保持良好的心态似乎可能也许更重要一点。萝卜青菜,各有所爱,咱就不在这里瞎掰了。


p.s.:
我想,我在现在这个公司做了这么些年,真的学到到了非常非常宝贵的东西。未来如果我真需要写简历(暂时我没有看到我需要动这个念头的可能性),我一定会把这段时间的某些经历写上一笔,比如说我学到了如何当管理人员,应该如何面试等等。有很多东西确实不是随便哪里都能够学到的,无论现在的领导们是否能看到,我都需要在这里感谢他们。

直接回复Wayne Zhang
我和你的观点其实差异并不大,有可能我写的东西比较片面一点,所以你误解我的意思了。我的面试题目要考量多个方面,所以会有这么几个问题:
1、OO和面向过程方面的
2、interface是干什么用的
3、过去用过的某个设计模式
4、是否知道甚至用过WCF、SilverLight、Linq、WF等最新的东西
当然,问题不是这么问的,详细就没必要说了。我只是想解释一下,我并非只看某个方面,我不是一个偏激的人。同时我的培训就告诉我,要注意避免被思维定势影响。

如果有很多人有兴趣的话,我可以写写如何面试。
posted @ 2008-06-30 12:50 Sumtec 阅读(2702) | 评论 (30)编辑

博客园技术帖子太多了,这是我真实的感受。也许是因为博客园定位于技术博客,所以会把其他内容划分到别的区里面,这一直是我觉得最不爽的地方。因为这样,有可能很多优秀的文章就被埋没了。其实完全可以都集中在首页显示,不好的刷下来,好的顶上去。以前说是没有人管,那也确实是有心无力。现在有点人力资源了,我觉得还是希望赶紧考虑一下这方面的事情。我写这个随笔的时候,就犹豫,该不该发在首页?最后还是觉得发吧,我觉得我想和大家交流的这些内容,估计还是会对一部分人产生一定帮助的。其实,我还想听听dudu的创业经验谈。

好,牢骚发完了,回到正题。

今天一晚上连着看了十几个简历,看得我头都大了。什么?不就是看看简历么,有什么好头痛的?没错,以前我也这么想的,可最近公司做了一个有关招聘的培训,才知道面试之前、之中、之后的工作事项有这么多。于是,最近的面试就要好好做功课准备准备了。可当我真看这么多简历的时候,就有点麻木了。不是么?平时已经够忙的了,各种大小事务纷涌而至,而且我所在部门还是“重要且紧急”甚至“不重要但紧急”事项的窗口。可想而知,当你看一堆的简历的时候,就多么的期望你面前的简历最好能够回答你一切想要知道的问题。可是偏偏最头痛的是,大部分的简历给我带来的问题,比我想要知道的答案还要多。

举例说明之前,先说说我从培训中得到的知识吧:
一个公司,尤其是靠脑力挣钱的公司,其人力相关成本是极高的。这里面,自然也包含了招聘和解雇的成本。不要以为招聘成本就是一个HR员工的成本,还应该包括:
1、因为面试,需要占用的人力资源成本;
2、因为试用失败,所支付的资金成本和时间成本;
3、因为最终雇佣了错误的人,所造成的资金损失,人力资源损失等。

具体数字我还真忘了,但是可以肯定的是,最终找到的合适员工,和你面试过的候选人之比,是非常小的,所以说招聘成本高。这还不包含招聘后,该员工所占用空间所需要花费的资金成本,该员工造成规模扩大之后的管理成本等。由此可见,招聘是一件多么重要的事情。比如说,找到一个合适的人,可以完成十个不合适的员工才能完成的工作量,前者的人力成本比后者的要明显低很多。那么为什么我们不多花一点经历,来认真对待招聘的事情呢?据我所知,很多公司的招聘工作,可能主要是HR那边出力,然后具体相关部门的主管进行面试。实际上这是有很大问题的,应该是相关部门主管要在面试前做很多的功课,具体做什么这里不多说了,只说一个:你打算问候选人什么问题?

问题有哪些呢?这个我也不多列,至少得有下面这几个吧(不限于此):
1、简历中有什么疑点?例如工作经历断档等;
2、过去的工作中,面对了什么问题,如何处理(而不是假大空的问:如果你是经理,你能面对压力吗?回答:能!@_@)

其中,我发现最有趣的一个问题是,很多人的简历中普遍存在一个(套)疑点:你回报的上级是什么人,职责是什么,你管理多少人,职责是什么,在你所在的工作中具体做什么事情?
很多人的简历都是这么列的:
2007.1-2008.1   北京XXX公司
职位:程序员
工作内容:用ABC.NET、OrnDB技术以及JUNK语言,构建了一个BC结构的XXX项目,该项目采用了0.5层的技术,实现了XXX、YYY、ZZZ的功能。全程参与了该项目的开发。

那么,我上面的疑点,一个都没有解决。还有一个更严重的问题,是使用大量烂掉的关键字,例如:熟悉、精通、理解——当我面对这些词的时候,几乎已经看到发腻,甚至都搞不清楚到底是熟悉比精通厉害呢,还是理解才是万岁。最糟糕的莫过于列出了一大堆的熟悉精通理解,我看到这样的情况,就想不明白,到底哪个才是你最强的方面,哪一个是你最弱的方面,基本上说了等于没说一样。

好,反过来说,如果你是求职者,如何从几百上千份的简历中脱颖而出呢?除了不要犯上述的常识性错误,还要注意突出你的不同之处。如何突出呢?要看你的工作经验如何了。

如果你是刚毕业的毛头小子

记住,不要列出一大堆的在校工作项目,尤其不要列你的毕设。中国大学的毕设水平有多烂,大家心知肚明,你又何苦把它统统列出来让人鄙视一番呢?很不幸我当年就犯了这个错误。虽说现在想起来,我的毕设水平仍然有资格说是比普通的学生好上那么一点点,可是如果和真正的、成功的商业系统比起来,那都是小菜一碟。你向往的某个公司真的会觉得你做过的文件拷贝程序感兴趣吗?那只是一个程序,不是系统,因此人家不会觉得你做过什么。很不幸,我以前曾经也做过类似的事情。不过,我并不是说不列,列出一条你认为最自豪的就可以了。虽然比不上商业系统,但这足以证明你是一个积极的人,真正做过一点事情的人,不是那种浑浑噩噩过一生的人。其他的项目,一笔带过就行了,写多了简直是浪费你的精力和版面,也浪费面试官的时间和感情。

除此之外,你还可以提供程序的源代码(全部或部分),这是展示你的程序风格的一个很好的方式。不过建议你在给出这样的代码之前,仔细地考虑一下,你是否应该规整规整,重构重构?对于一个企业,要决定是否聘用一个员工,是一件很严肃的事情,因此真正面试之前,理应进行相应的准备工作。那么当你去准备面试的时候,也更应该如此了。我收到过一个源代码,里面写了一些.NET其实已经提供的功能,除此之外也出现了一些很长的方法,还有一些遗留的、被注释掉的代码,写着这个没有做出来。这样的代码其实在向你的主顾宣示着:其实我很糟糕,看,我的代码就是这么糟糕,甚至于我来面试了,我也不乐于稍微修饰一下。您觉得这样能获得你想要的工作吗?如果是你不在乎的工作,那另当别论,可你都不在乎这样的工作,为什么要投这份简历呢?
 
你应该重点展示什么呢?相对于有经验的人来讲,缺乏经验是你的缺点,同时也是你的优点。在雇主看来,你就是一张白纸,未来在这张白纸上可能会画出一幅很棒的画。可并不是每一张白纸都有同样的结果,原因在于纸张的质量。上好的纸张应该是好的东西点拨一下,就能自然展开,而糟糕的东西不会被画上去。糟糕的纸张则相反,美好的东西画了半天没反应,糟糕的东西不知道怎么自动就有了。这个比喻还不太直接,直白的说,雇主期望你是这样的一个人:
1、你很勤奋并且你很好学
2、你很好学并且一学就会
3、你学会了不止,还学得很精
4、学得很精了不止,还态度好

一个不勤奋的不好学的人,如何展望未来能给公司带来价值呢?一个好的公司,会有一些培训,纵然没有,也会要求你自学。如果你学了半天都不会,你是勤奋好学那也没有用。这个一学就会,其实是平日长时间进行自学锻炼的结果,我也没有一个“银弹”供你解决这个问题。接下来,你不仅仅要学会了,还要精。所谓的精,就是你能说出道道来,能说出个所以然来。我非常头痛的一点是,每次我面试都会问“interface”你是怎么理解的,其结果十有八九就是大眼瞪小眼。可是这些人的简历上,会写着自己会X、Y、Z还会三层结构,那interface是干嘛使的、为什么存在怎么会说不出来呢?每天花点时间,先把这些最基本的思想弄清楚了,你才可能找到一份好工作。也许会花你几个月的时间,不过如果不花时间做这样的事情,你花多少时间也找不到一份你觉得满意的工作的。最后一点其实是很重要,有句话:态度决定行为,行为决定习惯,习惯决定性格,性格决定命运。如果你整天懒懒散散,上面交待你做的事情不到最后一天不做,又或者没有明确的利益你就不做事情,这样的态度肯定会决定你没有什么好命运的(含着金钥匙的除外,这种人不需要找工作)。

上面这些是刚毕业的学生的写简历时的一些基本思路。

如果你是工作几年,有了一定经验,甚至是丰富经验的人

那么很显然,你的优势在于经验。这个经验不在于你坐过了什么项目,而是你用什么知识、工具做了个多大规模的事情,其间遇到些什么样的困难,最后如何克服了。很多的简历里面,都只是说作了一个什么项目。至于说这个项目有多复杂,你负责其中的什么内容,你在其间使用了什么知识和工具,遇到什么困难,统统不说。这样的结果就是我看完了还是等于什么都不知道,就算你说我05-08年期间参与的项目有Windows Server 2008,Visual Studio 2008,那又怎么样呢?我心里面也许会觉得,可能其实你只不过作了其中里面一个很小很小的部分,比如写了一个计算器,或者OpenFileDialog。这样对于你是不利的,因为如果另一个和你竞争的候选人,把他的经历写得比较具体,那也许就会约见他而不约见你。

当我看过这么多的简历之后,我就觉得,如果我再找工作的时候,我就会用PPT把我这几年做的工作列出来,把我参与过的最大的一个系统的拓扑图画出来,把我遇到过并解决过的问题举一个出来,把我参加过的某个重要培训以及心得列出来。这样的PPT,至少让人能看得津津有味,直到我参与过什么样的事情,也能证明我能胜任我想要争取的岗位。说到这里,我也想提一下,我认为,文字太多其实不是好事,所以简历文字要精炼在精炼。详细列出10个项目,你说有多少人能耐着性子看完?几乎没有。详细列出1个项目呢?我觉得大部分人应该还是愿意看的。所以你觉得用同样多的文字,粗略列出10个项目好呢,还是详细介绍1个?我建议剩余的可以一笔带过,附带说明如果面试时有兴趣了解,可以详细说明。同时,图片比文字的说明力强多了,为什么不多摆几个图片呢?一个拓扑图,基本上就能把你的项目复杂度给说清楚了,用文字可得要写好大一段,是否能看得明白还不一定。图基本上瞄一眼就明白,文字可得要反复琢磨,对于面试官来讲前者绝对是赏心悦目,后者绝对是折磨人。

与刚出道的人比起来,你的劣势在于经验。经验多了难免容易坐享其成,不愿意接受新东西,或者有自觉牛X的感觉。
先说前者。我遇到过工作多年的候选人,面试时问3.5的东西例如linq、WCF等一问三不知,倒是不停的说很古董的解决方案。不懂就算了,有的东西还强装了解,说出来的不对。其实不知道就不知道好了,多数企业需要的是踏实的人,不希望你掩盖真相。俗话说有问题不可怕,有问题不知道不解决才可怕。也许你也是对很多最新的知识不了解,毕竟工作经验多的人,可能会负责比较多的事情。公司也可能不愿意冒风险使用新技术,平时也没有时间学习,怎么办?其实解决的办法很简单:开始投简历之前,赶紧先多学习一些新的东西。宁愿少工作一两个月,也要先把这些工作做好,磨刀不误砍柴工嘛。再说了,也算给自己放个假。当然了,如果工作中有机会,或者能够争取机会,那是再好不过的。
再说后者。平时自己给自己打气,没人的时候,或者对着老婆的时候,你可以自觉牛X一下,但是写简历的时候千万不要。先不说面试官是否比你厉害,这样的感觉面试官至少会判断你态度有问题。其次,你要真那么牛X,为什么还要去面试呢?猎头早该找上门来啦,准东家早就对你求贤若渴啦,三顾茅庐啦。嗯,有人会觉得,牛X怎么可能写简历里面呢?我举一个例子,我就看到一些简历,会罗列很多会的东西。其实完全没有必要,首先,这么十几个技术知识里面,总有强弱之分,列出强的那么两三个就够了。再牛X,面试官也没有时间问你超过5个以上的技术知识,所以你也没必要列超过5个。其次,这就是一种想告诉别人“我其实是很牛X的”。如果万一别人问到的,就是你列出的那十几个技术中最弱的那一个,而恰好面试官最强的就是这项,后果可想而知。

工作经历比较多的人当中,还有一部分的是有创业经历的,我也可以分享一下。对于这种候选人,面试官最担心的恐怕是“你的心很野”的问题。因此,如果你能够把你的人生规划说清楚,也许更能打动面试官。此外,由创业经验的人,一定不是平常人。不是平常人有两种:一种是非常优秀,只是暂时失败;还有一种,就是想法偏执,其实运气再好,也就那样。前者后者该如何写简历,我没有什么可以分享的,因为我还没有仔细想过。但有一点,无论前者后者,都是很重要的:就是要承认你的失败,同时还要总结原因。理由是,不承认失败的人,感觉不太踏实,或者不太现实;而没有总结的人,有可能就是总结能力欠缺,或者从来不做总结。当然,其实这些问题有没有创业经历的候选人,都有这种“维度”——即需要衡量的方面。但是有创业经历在简历上,无疑就在提醒面试官这方面的问题。你不解答这些问题,如果面试官忘了问,那么这个困惑就会存在,你就很可能会被刷下来。我已有的案例中,就有一个我觉得个方面都不错的,结果上司担心他干一段时间还会再次创业,于是没有考虑。

最后,当你面试的时候,一定要想办法弄清楚面试官的困惑点,要解答这些困惑点,才能得到你想要的工作。当然了,我上面说的那些,都是假设你已经比较有料的情况。如果你自觉能力不足,简历再好,面试能力再强,也是不能解救的,试用期必定暴露问题。因此,能力不足的,首先补能力,这篇文章恐怕对你帮助不大。
posted @ 2008-06-26 20:18 Sumtec 阅读(2731) | 评论 (61)编辑

     摘要: 前面那么多篇文章都太抽象,这次来一个稍微实际一点的。F模式是我实际上选用的模式,对该模式我做了不少实际的测试,因此代码也算是比较稳定的。不过由于实际上为了得到该算法的效率,算法本身做了一些优化,对于初学者,理解起来可能会有点困难,因此不适合直接贴原始代码。为了便于大家理解,这里出示的代码会比较好读,但是并不能取得我宣称的效果。大家可以在此基础上进行一定的优化,以便达到你所想要的性能。我目前版本的性... 阅读全文
posted @ 2008-02-08 22:49 Sumtec 阅读(2890) | 评论 (6)编辑

前面一片文章,已经提到了TTMP算法的核心概念,就是“终止符”触发检索。其实这个概念也不是我创造出来的,如果看过编译理论,应该会很熟悉,其实我是偷师其中的归纳法。那么何谓之B模式呢?
TTMP -B指的是,在触发对关键字的检索时,从后面往前面检索。比如说:
脏字表:
wxyz
yz
待检文本:
wxyza
当我们遇到了结束符z的时候,我们会回过头来查看刚才到底都遇到了什么文字。由于我们之前的扫描已经得到了两个“起始符”的相关信息,因此我们只要按发现起始符的逆序找 yz和wxyz。于是,最终我们命中的关键字是yz,而不是最先遇到的wxyz。

我们先说B模式的好处:
1、找到的一定是最短的匹配
2、在分析正常文本的时候,效率可能相对会更高

为什么是最短匹配很简单,就不说了。重点说一下为什么效率可能会相对更高呢?这里有两个原因:
1、如果我们的脏字表存在以某一个有n个短模式X作为结尾的其他模式,例如AX、BX,由于算法的特殊关系,是不可能进行AX、BX扫描的。这样的话,我们可以少算若干个字符的运算量。
2、对于正常文本,以及正常的脏字表,有如下特性:
a)文本中出现脏字条目命中的概率是非常低的,也就是说,大多数遇到起始符的情况,到最后都不见得能遇到结束符。于是在扫描到起始符就开始计算哈希值,很可能是得不偿失的(大多时候预先的计算是白做的),因此我们很可能选择在触发检索之后,再重新回过头来计算哈希值。同样是在触发检索之后,再重新计算哈希值的情况下,B模式比F模式所需要额外损失的性能就会更小。
b)脏字表中的词条通常都是小字符串占多数,而且通常比较有可能会有交集的情况,比如说ABC和C。在此情况下,如果从后往前搜索,实际上ABC这个条目是永远也不会被命中的。换句话说,B模式会自动达到使得脏字表最小化的实际效果,而不需要进行一个取交集的运算过程,包括初始化的时候和进行扫描的时候,都不会有额外的性能损失(不预先计算哈希值的情况),或者至少性能损失较小(预先计算哈希值的情况)。当然了,这样会占用一些额外的不必要的空间,不过一般来说脏字表要超过1万个条目都很难,1万个条目撑死了也就占用100k。

回头来说B模式的坏处:
1、要做最大匹配,意味着要付出比F模式更高昂的额外计算成本。不过有可能即使加上这个额外成本,与会比F模式要高效。
2、由于是逆顺序检索,如果我们选择遇到起始符就预先计算哈希值,就很有可能做了一些不必要的运算,即使是在命中的情况下。考虑:
脏字表:
ABC
C
待建文本:
ABC
则在遇到A的时候,就会开始计算哈希值,直到C字符。但是可以看到,对AB进行哈希值计算,很有可能就是不必要的。
关于这个缺点,其实只是“眼看着还有白费的运算无法消除”而已,实际上相对F模式可能还是更快的。

综上所述,如果你的任务只是检索某篇文本是否有命中,B模式更适合你。如果你是要做替换,也可以考虑F模式。由于我所选用的是F模式,并没有实际实施,因此我无法给出任何代码,大家只能够抽象的看一下。同时有的地方因为没有经过实际检验的缘故,有可能会有谬误的地方,请大家原谅。

posted @ 2008-02-04 17:51 Sumtec 阅读(1728) | 评论 (6)编辑

真是被人言中了,这简直就是一个星战系列。原因很简单,回到我的老本行——算法。这么一个领域,哪怕是芝麻绿豆那么小的问题,我也会发现很多有趣的东西。上一篇前传说完以后,意犹未尽。不过当时还没有更多可写的,而今天突然发现了那么几个问题,于是又可以写出一篇长篇大论了。

事情的缘由是这样的:早上看到XingD的一个修正,赶紧实测一下,把我发现另一些问题修正之后,还是发现有不匹配的状况。后来才发现原来是XingD的算法本身不处理大小写问题,或者说大小写是敏感的,而解决的办法是脏字表和待检文本都做一个ToLower这样的操作。当我如是修改之后发现,如果脏字过滤是地球问题,那么ToLower就是一个太阳问题了:ToLower消耗掉的时间,几乎是算法本身的500倍。实际上有的时候真是理论归理论,实战归实战。实战的东西就必须放到战场上反复检验,你才会发现这种你脑袋像破了也未必想出来的问题。

其实.NET的任务和我们的任务目标是不一致的:.NET要求绝对严格遵循标准,否则估计又会有敌对阵营的人跳出来说,喏,微软的东西就是破坏标准的东西。而我们要求的是什么呢?要求的是能把脏字检索出来就好了,至于哪些字符要变小写,不需要全照着标准来,只要按照我们自己的定义来说,是正确的,那就可以了。由于两者的任务不一样,导致了.NET的代码会比较复杂,要考虑是否不同文化转变的方式不一样的因素等,于是会消耗比较多的时间。而我们任务相对简单,于是如果我们自己写代码的话,速度可能会快很多。这是其一。
其二,由于ToLower必然会生成一个新的字符串副本,因此很明显,这样的操作会消耗内存分配的时间。这种时间可长可短,如果内存不足还需要动用VM,这个时候估计就会不只500倍的差异了。

那么怎么做这个大小写“脱敏”处理呢?很简单,自己做一个FastLower(char c)的函数即可,然后自己做一个StringCompare的函数。包括StringCompare,其他的函数也一样,只要涉及大小写的,都要调用这个FastLower来处理。看起来很笨的办法,看起来会要产生很多次调用,但是不要忘了,即使你用ToLower,里面也一样会有同样多,甚至更多的运算量在里面。这个FastLower我提供两个方案:
1、给出一个byte[] LowerAdd = new int[char.MaxValue]的数组,只要是大写字母,就给一个指向小写字母的增量,其他字符都是0。于是乎,FastLower不需要对近来的字符作什么判断,只要直接加这个偏移量就好了。
2、乖乖的写一大堆逻辑比较。

大家猜哪一个快?答案是第二个——跟我原来的想法不一样,为什么我就不管了,有兴趣的人自己研究去。使用方法1的过滤系统,总运行时间在50-60ms左右,而方法2的系统只需要运行21-26ms左右。下面给出一个差不多凑合的FastLower函数(注意,由于我的TTMP算法,需要计算HashCode,因此返回的是uint。各位可以根据自己的需要进行调整):

        static public uint FastLower(char ch)
        
{
            
uint val = (uint) ch;
            
if (val > 0x2179 && val < 0xff21 || val <0x41)
            
{
                
return val;
            }

            
else
            
{
                
if ((val >= 0x41 && ch <= 0x5A|| 
                    (val 
>=0xC0 && val <=0xDE && val!=0xD7|| 
                    (val 
>=0x391 && val <=0x3A9)
                    )
                
{
                    val 
|= 0x20;
                }

                
else if (val >= 0x400 && val <= 0x42F)
                
{
                    
if (val >= 0x410)
                    
{
                        val 
+= 0x20;
                    }

                    
else
                    
{
                        val 
+= 0x50;
                    }

                }

                
else if (val >= 0x2160 && val <= 0x2169)
                
{
                    val 
+= 10;
                }

                
else if ((val >= 0xff21 && ch <= 0xff3A))
                
{
                    val 
+= 0x20;
                }

            }
 
            
return val;
        }


这个函数看着不怎么严谨,我自己觉得没关系,能用就行。这个函数可以转换的大小写包括:
半角英文 A-Z
全角英文 A-Z
特殊发音符号(那种a上面带两点等符号)
希腊字母 Α-Ω
西里尔文 Ё、А-Я
罗马数字 -

基本上算是涵盖了,如果觉得没有涵盖的也没有关系,自己给改改就好了,应该很简单的。上面的算法有个特别给改的地方,就是把大部分中文用第一个判断给排除了,因为对于经常遇到又不需要处理的字符,尽可能减少判断可以提升性能。


除了ToLower之外,还有一个问题引起了我的注意:被匹配的,通常都是些什么样的条目呢?我稍微分析了一下,发现对于正常文本,1-2个字符的命中率合起来超过95%!我有两个稍微大一点的文本,都在280K个字符左右。这两个文本的1-2字符命中率分别为99.5%和98%。这其中1个字符的命中率在20%以内,也就是说,绝大多数命中的,还是2个字符的词条。这样就有几个可以值得思考的地方了:

1、如果我们为了提高性能,对于第3到n个字符采取空间换时间的策略,是否值得?如果算法正确,是不值得的。除非说算法有问题,会经常在其实不可能命中的情况下,检索第3-n个字符。从这个角度来看,TTMP算法应该是正常发挥了它的优势的:
如果找不到结束符,它是不会傻乎乎的就开始检索的。而基于上述的统计结果,命中条目的长度绝大多数集中在2个字符以内。那么对于TTMP算法,它通常能够拒绝绝大多数的无效检索。

2、另外一个值得我们考虑的地方是,对于1-2个字符的情况做优化是最划算的。其实这个结果,我在XingD的一篇文章里面的回复里面已经详细分析了一遍,可以说超过第3个字符开始,优化对算法的影响就已经是万分之一级别的了。当然了,这是理论,是在一个完美的前提下的,即你不会对不可能的命中进行检索。而XingD后来的改进能够提高命中率,实际上是另外一个原因:把需要检索的概率降低了。原来是所有出现的字符都在一个表里面,于是k2、k3的概率会相对较大。而后来的改进,主要是把各个位置出现的字符分离开,于是可能第二个字符的位置所出现的字符集会相对减小,于是k2、k3就减小了,运算量也就相对减小了。这也应该是XingD的改进,其性能提高并不仅仅是提高了万分之一的原因。

3、回过头来看,TTMP可能还没有发挥最高的效率,因为TTMP对于1-2个字符的作用并不明显,越长的关键字,其可能节省的运算量可能会越多。因此如果再要提高性能,应该考虑对TTMP做一些针对1-2个字符的改进。1字符命中很简单,就是对1个字符做一个单独的命中处理,不做复杂的记录初始位置、哈希值运算、查表等运算。对于2个字符,目前没有怎么想好。在原本已经非常高的效率下,任何优化都需要特别谨慎的考虑,因为很容易就会造成优化得不偿失的状况。比如说,也许有人会考虑利用编码压缩的办法来提供一个更大的字符类型映射表(表明是直接命中,还是起始符,还是中间最多会有多少个字符,或者组合起来的情况等)。但这样的优化我认为是极有可能得不偿失的,因为相对于命中来讲,绝大多数的还应该是不命中的部分。如果采用编码压缩的方案,那我们就不得不对每一次扫描都进行这样的压缩运算,而这些运算量总量说不定比该方案所“优化掉不执行”的运算量要大几个数量级。也许等我想好了,会把解决思路奉献给大家。不管如何,可以肯定地是TTMP-B模式,肯定会比我现在使用的TTMP-F模式要高效,后面会有文章进行解释的。

4、WM等算法也许对于我们的“脏字过滤”任务真是毫无发挥余地的。理由太简单了:首先我们的多个模式里面有1个字符长度的情况;其次,即使能够进行分化,剩下来的匹配中占绝对地位的2个字符情况,也是WM所无法发挥作用的(相对TTMP来说)。因为WM算法其根本逻辑就是,构造一个2-3个字符的前缀表,这也就以为这几件事情:
a、模式的长度至少是2个字符以上。搞不好大多数情况下,也只能够乖乖的继续扫描下一个字符;
b、由于2个字符的长度是32位,因此如果不进行编码压缩,至少需要{ [(2^32)/8] * 2 }个字节来构造他的转移表,也就是1GB。对于普通的服务器来说,这有点太奢侈了。如果说要进行编码压缩,参考上面第三点的解释,可以肯定是不值得的。比如在我的两个正常文本里面,命中数量都在1000个左右,也就是说非命中字符数,是命中的字符数的140倍。即使WM算法全部都给跳跃了,可能带来的性能优势,真是不好说能否覆盖编码压缩所带来的性能损耗。

最后小结一下(无论是否是核心算法,只要是代码应该都适用):
1、一定要注意你的任务目标,如果目标和框架的目标不一样,即使功能是近似甚至相同的,都需要考虑是否要自己写一个东西了;
2、做优化的时候,一定要考虑到底那些状况是最常见的。例如在这里,非命中是最常见的,触发了扫描不命中是最常见的,命中的时候极短的字符是最常见的。只有搞清楚了这些问题,你才知道性能瓶颈都在哪里,而不是瞎优化一气。很多时候,有些优秀的算法只是在特定的状况下是有效的,对于例如扫描不命中时的问题,大小写问题是无能为力的,或者不是你的处理范围。如果你选择的一个核心算法能达到10ms级别的效率,却使用了ToLower,那么有可能你的努力就白费了。

和星战一样,前传也是不可不看的。


posted @ 2008-02-03 15:13 Sumtec 阅读(2139) | 评论 (11)编辑

前面一篇稍微说了一下我自造的TTMP算法思路,貌似很好很强大——估计不是最强的,不过至少我自己觉得比较满意了,至少算是达到可用级别了。那么还有啥好写的呢?好久没有写这种类型的技术文章了,满脑子的想法:
1、除了算法本身,还有什么是效率损耗点?
2、TTMP算法本身也只是讲了一个大概,远没有详细到论文级别,抽空可以给大家详细讲一下。
3、其实上次说到的TTMP算法,还有至少2种实现方式,分别是前向检索和后向检索两种模式,他们都有什么区别呢?

嗯,今天的主题就是第一个:我们先抛开算法本身,看看还有什么会影响我们的性能的。

当我们进行敏感词过滤得时候,必然涉及3个要件:
1、脏字表
2、待检文本
3、检索过程

首先说脏字表的问题。通常来说,这个脏字表不是我们发明的,我们没事情搞这个图啥啊?不是自找麻烦么。这个问题通常是有人要求你这么这么干,于是你不得不那么那么干。所以,通常会有人给你一个脏字表。可是通常给你的人不是一个人,又或者说给你的文件格式乱七八糟。那么于是乎就有一个条目重复的问题!对于这个问题,可能有人是自己整理出一个没有重复的表。而我却觉得这个整理通常很费工夫,还不如让程序给整整。这样做有两个好处:
1、我们不必担心人家给你的文本就是有重复的。
2、当又有新的脏字表文本时,直接粘到原来的文本结尾即可,可以节省整理的人工成本。

于是,我们启动脏字过滤的过程时,首先第一个需要做的,就是排除重复。当然了,这个排重的过程也是一个性能损耗点,然而通常这个过程耗时相对来说还算可以接受,而且做一次这种操作,可以检索起码几百上千的文章,平均下来几乎可以认为没有任何的成本。这个过程很简单只要用下面的几句代码就可以完成了(用HashSet也一样的):

            Dictionary<stringstring> fixKeys = new Dictionary<stringstring>(StringComparer.InvariantCultureIgnoreCase);
            
foreach (string key in scanKeys)
            
{
                fixKeys[key] 
= key;
            }

            scanKeys 
= new string[fixKeys.Count];
            fixKeys.Values.CopyTo(scanKeys, 
0);
            
return scanKeys;

注意:上面的代码是假设你的算法能够忽略待检文本的大小写问题的。
有人就这么问我:为啥我用了一个很好的算法,最后效果还是有点不理想呢?我想这是一个可能的原因。通常我们所说的算法,都是讨论一些核心的问题。而类似这种问题,通常不会在WM/grep等算法的论文里面讨论。因为无论我们使用哪一种算法,这一个排重的步骤都是一样的,你可以认为是必须的,或者是不必要的。但是如果你真的希望提高效率,这也是一个值得你关注的地方。或者说,你得考虑一下当开始做最核心的事情之前,你是否已经将准备工作做得充分了。

从这个方向引申开来,还有一个可以优化的地方,就是是否考虑把脏字表转换成最小集合?比如这么说,当XYZ和Y都是原始脏字表条目的时候,你是否打算去掉XYZ呢?这个取决于你的任务。如果你的任务是,看看某篇文章是否有敏感词汇,如果有,就转人工检查。这个时候只要有最小集合就足够了,不需要最大匹配。但是如果你的任务是,只要是脏字,都需要用星星覆盖,不但不应该转换为最小集,反而应该增强你的算法(即使是牺牲性能)。记得有本书就说过,不要无的放矢。首先要知道目标在那里,才好射到靶上,射到靶上了,我们再来讨论如何给射准。通常我们看一段代码他是否能瞄准其目的,也可以判断出开发者在某些方面的水平如何。而我的任务目标是前者,于是乎就不需要作这个缩小最小集的工作了。顺便提醒一下,如果是后一种任务,普通算法是会有缺陷的,例如:
脏字表:
XYZ
X
待检文本:
WXYZA
被屏蔽之后会变成:
W*YZA
而不是我们所期望的:
W***A
这样的结果有可能是不理想的,因为这样可能还是有一些不雅的内容会被读出来。
这个时候即使检索出X,也不能够退出循环,需要继续检索更长的文本。这个过程也许是可以优化的,但无论如何都是要比原来多消耗一定的性能的。

当然了,如果说要做到极致,我可以用这么一段程序把原始脏字表给整理一下,缩小成最小集,然后生成修正后的脏字表文件供核心算法使用。而我目前并没有选择这么做,一个是觉得开发这么一个程序花的时间不见的能节省可观的时间。另一个问题是,有可能未来任务会转换为后者时,这个时候就不应该用最小集,原来做的这个工作可能就白费了。

下一个性能损耗点是在读取待检文本的时候,这个在什么位置呢?如果看过操作系统设计的相关书籍,我们应该知道,当我们进行磁盘操作的时候,CPU会是空闲的。我们的待检文本,不是储存于数据库中,就是储存于一个个单独的文件里面。无论是哪一种情况,我们都可能存在CPU等IO的情况。除此之外,通常一台服务器上面可能会有不止一个CPU,一个CPU也可能有不止一个内核。因此为了提高整体的检索性能,使用多个线程也是另一个必要的手段。关于这个话题,相信网上也会有很多的文章会讨论到,至少是类似的问题,这里就不再熬述了。如果你打算通过多线程进一步提高效率,我可以提醒大家的一点是,不可把性能压榨到极致,否则可能会影响到普通用户的使用。另一个需要注意的问题是,无论你是否打算用多线程,都要注意同一个时刻应该只允许有唯一一个进程在处理。如果是多个进程同时在做这样的检查,就很有可能都在检同一篇文章,这样无谓的性能损失是,无论算法再快也挽救不了的。可行的方法有很多,比如说把这个东西做成一个服务,永远就只有一个进程在做,同时内部有一个同步锁,不允许重入。又或者有一个数据库级别的锁,锁上了就不让其他人通过页面去启动这样一个检索过程。

有没有必要弄个分布式呢?一般来说,我们不是做搜索引擎,自有数据量还是比较有限的。能有一台服务器单独作这么一个工作,我估计就已经很了不起了。通过上面的多线程来压榨服务器性能,通常已经足够了。分布式处理可能会让你面临不小的技术难点,开发调试过程也比较麻烦。我建议这个作为最后的手段,在这之前还不如考虑配置一台有更多更快速CPU的服务器,或者考虑简单的空间换时间手段。其实我个人觉得,很难想象用户上传文字的速度,会超过一台服务器的处理速度。如果到了这样的一种程度,估计首先需要分布式处理的不是这个脏字过滤程序了,而是你的站点象用户提供服务的程序了。

上面说的这些,你说是算法吧,严格说来也不是,不过确实也是一段代码,也是一种解决问题的思路,这就是我标题里面“算法前传”的意思了。剩下来的,就是检索过程了。这一个前传就到此为止,希望能给大家另外一个提高效率的思路,同时也为达到你的算法的最高效率做好必要的准备。下一篇会先拆解一下TTMP-F和TTMP-B两种模式的概念、优缺点以及实施概略,详细地代码实战,还要酝酿一下。

posted @ 2008-02-02 14:57 Sumtec 阅读(1845) | 评论 (7)编辑

什么是TTMP算法?不好意思,我发布这篇文章之前,估摸是没有其他地方能找着该算法的,因为那是俺生造的。
TTMP是啥意思呢?是Terminator Triggered Multi-Pattern 的意思,也就是结束符触发多模式算法。
-_-! 有点难理解,没关系,看完了也许就理解了。

不过这个自造的算法有点复杂,为了保证大家能够顺利阅读,请大家配合做一个测试:
拿出你的手表,或者其他计时器,看看你能用多块的时间阅读完下面这篇文章。
判断标准如下:
如果你的时间少于15秒,就可以不用读我的文章了,完全有能力造一个更强的算法;
如果你的时间少于30秒,我们可以沟通交流一下;
如果你的时间少于45秒,你可以仔细阅读一下,说不定可能也许有点启发作用;
如果你的时间少于60秒,你一定能够在这里挖到宝矿;
如果你不属于上述情况,我建议您啊,还是不要费力气阅读了,有点面为其难了。

Do you raelly know Engilsh?
At laest in Egnlish, wehn pepole raed, tehy
usaully wlil not noitce taht the charcatres bewteen
the frist ltteer and the lsat leettr are not in a
corrcet oredr. In fcat, hmuan brian does recongize
wrods by seeknig the fsirt ltteer and the lsat leettr,
and tehn fnidnig whcih charatcers are insdie of tehm.
See! All the wrods hree wtih mroe tahn 3 leettrs are
all wirtten in a worng way! Do you niotice taht?

嘿嘿!其实刚才那段能力测试的话是瞎扯的,主要是让大家快速阅读,而不是认真阅读。有意思吧?
这个不是我瞎扯出来的,是一个著名大学的研究结果(好像是剑桥),原文我没工夫找,瞎造一段对付一下。不知道你读上述文字的时候是什么感受,反正我自己觉得比较震撼,也比较有意思。

确实,如果按照自动机理论,一个字一个字的去认真阅读,那么也还是很有可能能够理顺语法结构,搞清楚一句话的含义的(理论上如此吧,实际上还没有任何一个机器能做到真人般的感知能力)。但是如果每个字都认真读,并查找语法表,一来速度会慢,二来需要海量的空间去做这个事情。而人脑比较聪明,经过若干年的锻炼之后,已经自动的学会了放弃细节,比如读"cerroct"这个词的时候,找到前面是c开头,后面是t结尾,中间有eoc各一个,r两个,一查表就知道肯定是“正确”这个词而不管他正确与否——哦,不好意思,我又写错了,应该是correct!

嗯?这个跟我们这次的主题——字符串多模式精确匹配,有什么关系呢?
有啊!当然有啦。不过在我告诉大家这个关系之前,我们先来分析一下,字符串多模式精确匹配的效率问题是什么?写之前我先给大家说一下,我下面的说明也许不会很严谨,因为有时候太严谨了,就不好理解了。例如什么令X=Y……反正我最近为了这个事情找的一些资料,尽是这个,看着也觉得头晕。

所谓字符串多模式精确匹配是啥意思呢?字符串不多说了,实际上能用于搜索字符串的,也能搜索其他东西。多模式嘛:比如
string s="xxx";
string t="xx";
s.IndexOf(t);
这个是在一个字符串s中,找出另外一个字符串t所在的位置(或者说是否存在),这种叫做单模式,只有一个要被寻找的字符串t——唯一的一个搜索模式;如果说是
string s="xxx";
string[] t= new string[]{"x1", "x2", "x3"...};
s.Scan(t);
这种呢,就叫做多模式匹配了。因为我要在s里面找出一组t中任意一个所在的位置,或者说是看看我们的文章里面是否有脏字表里面的敏感词汇。

关于多模匹配问题,有很多已有的算法,我没有仔细的看,只看了一个可能是WM的算法,实际上可能还有什么grep/agrep等算法。不过需要提醒大家的是,还有不少的算法是讨论模糊匹配的,比如说容许其中有一个字不正确,那些算法就不是我这个主题要讨论的内容了。我要讨论的是精确搜索,即要找“地瓜”就找“地瓜”,不要“地鼠”。

多模式精确匹配很难吗?不难,很简单:我们只需要循环一下,先找s.IndexOf(t1),再找s.IndexOf(t2)……但是如果你果然这么做,效率就会很低了,因为你会需要扫描文本很多很多遍。可以想象,我们的目标是只要扫描整个文章一遍就能够找出这个文章里面都有哪些敏感词汇。不过,很明显该目标并不容易达成,但至少我们可以尽量接近“只扫描一次”这个目标。在进一步分析之前,建议先看另外一篇文章:
(重发).NET脏字过滤算法
这篇文章的算法(比如叫做XDMP算法)其扫描速度已经是比较快的了,并且其思路也比较好理解,我们在这个文章的基础上进行讨论会比较有意义。首先我们先整理一下这个算法的思路:
1、首先扫描文章里面的每一个字符,只有当某一个字符是脏字表中任意一个脏词的第一个字符(称为“起始符”),我们才试图看看接下来是否是脏字(触发检索)。
2、但是我们也不是毫无头绪的就开始循环脏字表的每一个词条:
2.1、我们往后检索一个字符,先看一下这个字符是否是脏字表里面的任意一个字符,如果不是,就表明不可能是脏字表中的任何一个条目,就可以退出了。
2.2、如果是,我们就取从第一个被检出字符到目前扫描到的字符之间的字符串,求哈希值,看看能否从哈希表中检出一个脏词。
如果检出了,那就大功告成,否则继续检索后面一个字符(重复2.1、2.2),直至找不到,或者超出脏字表条目最大的长度。
2.3、如果都找不到,或者超长,那么接下来就回到刚才的那个“起始符”后一个字符继续扫描(重复1、2),直至整个文章结束。

我这里先引入了三个重要概念:
1、扫描,指扫描文章,看看是否有需要和脏字表开始进行对比的情况;
2、检索,指已经发现可能存在情况了,在将文本和脏字表进行对比的过程;
3、起始符,指脏字表中条目中的第一个字符。

如果我们只要扫描,不需要检索就可以完成任务,那一定是最快的,不过目前我比较孤陋寡闻,没有找到这样的算法。
又或者,如果我们扫描一遍,而检索全中,那也很不错,很不幸,还是没见过。
很明显,扫描不应该多于1遍,否则肯定效率不可能高。那么检索就是算法的关键了!拆开来,提高检索质量有下列几个方式:
1、尽可能不触发检索;
2、如果确实需要触发检索了,那么每次触发检索的时候,要尽可能减少检索所需要遍历的字符数量;
3、每次对比脏字表的时候,减少运算量。

回过头分析上面的XDMP算法,是:
1、一次扫描;(很好,没啥好说的)
2、只要发现“起始符”就触发检索;
3、检索的时候,需要遍历的字符数是 1+2+3+...+n,这里的n是被命中的脏词的长度,或者最接近的长度;
4、每次检索,需要重复计算HashCode,不要忘了,计算HashCode,也是需要扫描字符串的,也就是又要遍历1+2+3+..+n个字符。

于是,我就有了一下问题:
1、难道每次遇到“起始符”了,就一定要触发检索吗?哎呀妈呀,这个也要检索(因为脏字表里面可能有MB)?!
2、难道每次触发检索,都非得要检索长度为1的,长度为2的,长度为3的……直到检索成功,或者出现非脏字表字符的时候吗?
3、难道每次检索,我们都需要把特定长度的待检文本截取出来吗?
4、难道每次检索,都需要从头开始计算哈希值吗?不能利用同一次触发检索后,上一次检索的哈希值,来减少本次计算的不必要运算量吗?

这四个问题,基本上是我想要解决的问题。其中前两个是一类问题,后两个是另一类问题。首先我们检查第一类问题:
好,我们回顾一下最开始的那篇英文,我们是否有点什么启发?对!我们触发检索的条件太简单了!
如果一个单词我们都没有看完呢,为什么要开始想这个事一个什么词呢?
另外,我们触发检索之后,也作了很多不必要的检索,因为当我们遇到"cao"这个字符的时候,很可能脏字表里面只有"caoT妈","caoN妈"这两种情况。如果有文章里面是"操作",脏字表里面正好又有"作LOVE",上述XDMP算法还是会乖乖的搜索两个字符的情况,而实际上又是没有必要的。

那么我们如何减少这些不必要的运算呢?首先,我们改一下,不要每次遇到“起始符”就触发检索。我们扫描到起始符怎么办?记录下来他的位置等信息,然后继续扫描下去。当我们遇到了“结束符”,也就是脏字表每一个词条中,最后一个字符中的任意一个时,我们才考虑是否要开始触发扫描。而扫描的时候呢,也不一定非得要脏字长度为1、2、3……的情况。因为之前记录了各种起始位置,我们可能只需要扫描1、3两种情况,或者5这种情况。

接下来是第二类问题:
上述算法里面,为了加快检索某串字符是否在脏字表里面,使用了哈希表。为了能够查表,所以就必须把这个哈希值给截取出来。可是这就引发了两个性能损耗点:
1、每一次截取,都要重新计算哈细值;
2、每一次都需要截取出一个字符串。
要避免这个问题,首先我们需要了解哈希表大致是怎么工作的:
哈希表实际上是根据当前的字符串内容,得出一个概率相对比较平均的散列值(这样哈希效表才不会容易出现冲突,即内容不同数值却一样),然后找出表中哈希值相等的第一个结果,然后对内容进行比较,如果相同就是找到了。否则就找下一个,直到没有相等哈希值的条目为止。

于是,我们可以这么来解决上述问题:
1、首先,我们造一个哈希值的计算方法,使得我们可以利用上一次的计算结果,接着计算下一个结果。
比如说,我们可以一个字节一个字节的进行异或(好处是方向性不敏感),或者也可以规定从字符串后方往前开始计算。
为什么规定从尾部进行计算?因为TTMP是结束符触发扫描的,比如说有文本:
ABCDE
如果E是结束符,那么就会检索ABCDE、BCDE、CDE、DE、E(还要看是否扫描到这些起始符)。如果我们是从后方往前计算,那就可以利用E的哈希值以及字符D,就可以计算DE的哈希值,而不需要再次对E字符进行计算了。
2、其次,我们可以构造这样的哈希表:
Dictionary<int, List<string>> hash;
其key就是我们刚才算出来的哈希值,根据算出来的哈希值,我们就可以得到一个该哈希值下的脏字列表,然后我们一个个的和待检文本进行字符对字符的比较。这里看起来很奇怪,为什么有了哈希值,还不能够通过哈希值直接找到对应的字符呢?
不要忘了,哈希值本来就是会冲突的,我现在只不过把冲突的情况单独取出来自行处理,这样实际上的检索次数并没有增加(放在哈希表里面,也必须一个个的进行字符对字符的比较,才能够确定Key值是否完全相等,而不是Key的哈希值相等但Key值不等)。而好处是,我们不需要非得取出一个字符串,好让哈希表去获取这个字符串的哈希值(需要从头遍历每一个字符)。
通过以上的措施,我们就可以让每一次对n长度待检文本触发检索,只需要最多遍历n个字符,就可以得到最多n次遍历的所有哈希值了,而原XDMP算法则需要遍历Sum(n)个字符。

当然了,上述这几个措施,其效果并不会非常明显,原因有三个:
1、通常我们的文本都是很正常的文本,顶多偶尔有点敏感词汇,因此并不会经常挑战前面说到的性能损耗点;
2、通常我们的脏字表数量不会极其巨大,起始符和结束符也应该集中在有限的那些字符里面,因此绝大多数时候首字符表,以及结束符表就已经能够极大地提高性能了;
3、即使我们真的需要触发检索了,我们的脏字通常长度会比较短,或者大多数会比较短,因此上面的改进所带来的性能提升会比较有限。比如说两个字符的情况下,原算法计算哈希值需要遍历3个字符,而TTMP则只需要遍历2个字符……汗
而如果是5个字符,原算法需要遍历15个字符,而TTMP则只需要遍历5个字符,开始有差距感了。
可惜的是,5个字符的敏感词毕竟还是比较少的,而一篇文章正好中这个5字敏感词的地方也是很少的。

目前我这个TTMP算法还没有优化,已经能够做到和XDMP算法消耗时间比为1:1.5-2.5,算是很不错了。当然了XingD后来又做了一个新的算法,测试速度很快,可是当时我测的时候还不稳定,有漏检的情况,因此暂时不做评论了。
至于我的TTMP算法,也还有不少可以挖掘潜力的地方,比如现在是前向检索的,以及预先计算哈希值的。如果改成后向检索,检索时计算哈希值,性能应该会更好一点。不过暂时不打算继续挖掘了,准备把他先放到实战里面应用再说。

呃,其实本文开头说的还是没错的,本文还是有点难度,而本人描述能力也不是特别好,不知道各位看官有没有看懂了?
源码?嘿嘿,私货,先收藏一段时间再说。当然了,如果你有一段源码,能够合法制造让制造者合法拥有的人民币真币,能够用VS2005编译通过,部署过程只需要点一下鼠标,运行过程无需看管,并且你愿意和我交换的话,我会考虑一下的……真实的情况是,我现在还要继续让算法更稳定,不能放出一个问题多多的代码出来吧?
私下说一下,这个程序比XDMS算法复杂不少,如果将来放出来,并且各位想要整明白的话,还需要自己花点心思。

哦,顺预先给某人回复一下:
KMP算法是单模匹配算法,BM据说也是单模式的算法。
WM算法是多模匹配的,我找了一个据说是WM的算法看了看:
http://blog.chinaunix.net/u/21158/showart_228430.html
不知道你说的是不是这个。
我发现思路其实和KMP/BM类似,主要是通过“跳跃”技术来提升性能的。但是该文里面也提到了这么一段话:
假设其中一个模式非常的短,长度仅为2,那我们移动的距离就不可能超过2,所以短模式会使算法的效率降低。

可问题就在于,一般脏字表的长度都是1到2个的居多,因此绝大多数跳跃的作用并不强。即使是5个字符,再TTMP里面,也很可能因为超出长度没有遇到“结束符”而不会触发扫描。而WM需要有一个Shift表,为了节省空间还需要压缩,这就意味着需要对每一个扫描单元进行一个压缩计算。综上所述,TTMP和WM进行搜索脏字任务的PK,谁胜谁负还不一定呢。顺便说一下,即使是WM,也不是一次扫描的,因为如果不跳跃的话,就会要多扫描一下某些字符。

TTMP效率描述:
Ot = Ot(文本长度) + Ot[ 起始符与结束符在出现在扫描窗口中的次数*Avg(同一个结束符中哈希值相等的词条数目) ]
=Ot(N) + Ot[f*Avg(h)]

Om = Om(字符类型表) + Om(结束符表) + Om{ 词条总数*[哈希表内部变量消耗内存+列表消耗内存数量+Avg(词条长度) ] }
=256K + 256K + Om{n * [12+12+Avg(k) ] }
=512K + Om[n*(c+k)]

^_^ 唐僧回来了……
posted @ 2008-02-01 18:31 Sumtec 阅读(2944) | 评论 (14)编辑

早上看新闻,中国男女性比例失调,男性多出3千多万。
这个还不是最大的问题,最大的问题是,超过晚婚年龄的人群中,有超过60%的人处于非结婚状态。
还好,07年结婚了,也算是为社会做贡献了。
换一种说法,就是自己把自己给和谐了。

08年,你还是中国不和谐因素中的一部分吗?
Think again.
posted @ 2008-01-02 18:54 Sumtec 阅读(395) | 评论 (3)编辑

虽说近年感受到越来越多的骄傲,中国还是有那么多不尽人意的地方。不说亚运村汇园公寓北面的那条路修了两年多都没有修好,埋好了又挖,挖好了再埋,那条路上的饭馆都倒闭了。也不说上个礼拜天,鸟巢施工把供水水管挖爆,造成附近地区停水一天。(当天下电梯时里面有两个人,说:“……挖爆了。要是这件事情在我们台湾,至少得要一个礼拜。不知道这便怎样,就看这边效率了。”结果当天晚上就好了。这个关乎ZJ的东西怎么可能慢呢?)

今天最让人郁闷的就是门票的官网崩溃了,搞得我都没有兴趣去买票了,之前还兴致勃勃的。


很明显,抗不住压力了,全国人民的力量还是很大的。其实,抗不住情有可原,毕竟人多。但是从技术角度,这里面有几样东西是不应该的。
第一个不应该的就是在加密连接的页面里面,有非加密的内容。只要选择了不现实非加密内容,样子就成这样极丑。
第二个,是这样的系统,应该有相应的设计,不应该是出现“对不起,现在无法处理您的请求,请重试。”这样的东西。
理由很简单,如果系统能够承受的处理能力是1000,实际需要处理10000,就会出现无法处理的情形。这个时候如果提供一个很简单的方式让用户刷新,问题就会变得极其糟糕。因为当这个系统刚处理完1000个请求的时候,上一次9000个请求,以及下一次10000个请求就会到达。于是,实际需要处理的请求就会越滚越大,毫无恢复正常的希望。

解决的办法有好几种,一种是不允许刷新,种下一个Cookie,服务端如果发现Cookie中的上次尝试时间距现在不到1分钟,就不允许刷新。这样9000个剩余请求就不会立刻转入下一次请求当中,系统符合的增长速度相对就会放缓,恢复正常的希望也就比较大一点。缺点是,对这剩余的9000个用户有点不公平。但是实际上,如果系统不提供刷新,你必须重新从搜索结果到现实内容页面,到点击订票按钮,到最终系统受理,也差不了多少。反而提供了一些方便。

另一种方案是,给每一个具体操作请求排号,一进来就发一个号码,每过一段时间,页面自动刷新。服务端如果发现现在这个号码进入了系统能处理的范围,就换发一个令牌,允许进入实际操作或者处理。这样的话,处理比较当前排号是否在系统处理范围的页面功能相对简单,服务器吞吐量比较大,可能是实际订单操作(数据库操作)的100倍。于是用户不会觉得有问题,相对第一种方案会更公平一点。
这种东西又不是没有见过,最简单的,大部分提供公开下载功能的网络硬盘的下载页面都有类似的功能。很难吗?应