中科大的那位,敢更不要脸点么?

  日前,中科大软件学院二年级研究生 HCOONa 发表奇文 驳 GarbageMan 的《一个超复杂的简介递归》——对延迟计算的实验和思考。据他自称发此“文章”是为了“打”我“脸”,下面就来看看他是如何“打脸”的。

  在C语言初学者代码中的常见错误与瑕疵(5)中,我剖析了一位初学者针对如下问题所写的代码,

在世博园某信息通信馆中,游客可利用手机等终端参与互动小游戏,与虚拟人物Kr. Kong 进行猜数比赛。

当屏幕出现一个整数X时,若你能比Kr. Kong更快的发出最接近它的素数答案,你将会获得一个意想不到的礼物。

例如:当屏幕出现22时,你的回答应是23;当屏幕出现8时,你的回答应是7;

若X本身是素数,则回答X;若最接近X的素数有两个时,则回答大于它的素数。

指出其中的一个主要问题是从X分别向前后两个方向查找素数的解决方案。因为其中的一个素数可能离X很近,另一个则离X很远。在这种情况下,去查找较远的素数是在做无用功。

  我提出的改进办法是在X前后依照下面次序

  X X+1 X-1 X+2 X-2 X+3……

交替寻找素数。这种方案在最坏情况下(X两边素数到X距离相等),与原方案计算量相同,但对于距离不等的情况则不会做无用功。

  判断素数的方法为试除法,即用[2~X1/2]之间中所有的整数试除X,如余数都不为0则X为素数。

  当X较大时,由于区间内素数个数远低于整数个数,所以,更优的解决方案是用[2~X1/2]之间中所有的素数试除。在一个超复杂的间接递归——C语言初学者代码中的常见错误与瑕疵(6)中我实现了这种方案。

  由于这种方案过于精打细算,所以代码比较复杂。

  对此,HCOONa发表评论:

这道题不是应该预先计算素数表,然后用二分查找吗?怎么会搞得这么复杂?

  这是明显的外行评论。因为,
  • 计算[1,x+](到大于等于X的素数)的素数表,需要试除的次数远远多于判断X附近几个数是否是素数的试除次数;
  • [1,x+]间的素数表有一大半求得毫无意义,很无聊;
  • 问题的解明显为[1,x+]间素数表最后两项之一,除了脑残,没人会想到用二分法。
  不过我还是给与了认真的回复
我考虑过你说的这种方案
但我认为计算出直到大于等于X的素数表得不偿失
举例来说X=100
要计算出直到101的素数表
计算过程中大约100个数都需要试除2 3 5 7
而文中的方案只需要对100 101这两个数分别用2 3 5 7 和 2 3 5 7 11试除一下就可以了
这个差别太大了
如果X=1000000000这个量级
两者之间确实是天壤之别
计算素数表还有一个问题
就是事先不清楚到底有多少素数
因此很难估计数组大小
另外二分查找应该是用不上的
  点到为止,在这里我很含蓄地指出了他的错误。没想到,HCOONa竟然回复:
这就看你这个问题需要回答多少次了,
  尼玛,题目里说得清清楚楚了,“当屏幕出现一个整数X时”,幼儿园的孩子都能看懂的条件,他却硬要装傻充愣。你一个二年级研究生,这么简单的就连二年级小学生都能看懂的问题都看不懂吗?无非是为自己前面的错误找辙罢了。
  我当时明确回复:
我只考虑回答一次的情形
  尼玛,我的算法是为什么样的问题设计的,瞎子都看得出来吧。
一般说来,10^9 以内的素数并不多,用筛法的话很快就能得到一个素数表,接下来使用二分查找一下就能找到给定的数是不是素数,以及距它最近的两个素数是什么。
  是吗?10^9以内的素数的数目是5*10^7量级,居然不多?用筛法的话需要几个GB大小的数据,难道内存不是资源?而所谓“很快就能得到一个素数表”纯属瞪着眼睛说瞎话。稍有编程常识的人都知道,问题规模在数量级上的提高意味着什么。尽管筛法可能不需要进行除法运算,只要简单的加法和赋值就可以了,但若说10^9次方的赋值比10^5次除法更快,只能骗骗不懂编程的中学生。
而你的方案则有可能需要更多的计算,比如说最近的素数差的比较多的时候,尤其是在 10^9 这个数量级,素数会变得比较稀疏,重复的计算可能会花费更多的时间。
  这个就不知道是真傻还是在装傻了,给出了一个似是而非的依据。最近的素数差的比较多又能多到哪里去呢?这个量级区间上素数的密度最多是几百个数中有一个素数,几百个数试除几千个素数(109/2以内的)与进行109量级次加法和赋值到底孰轻孰重?作为计算机专业二年级的研究生,难道这也不懂吗?
如果你觉得维持一个素数表所需要的空间太大了,难以接受,那么我建议使用 Rabin-Miller 素数测试。
  看来是说完大话,自己也有点心虚。于是扯出Rabin-Miller法遮掩。
  我当即回复:
据我所知
Rabin-Miller 素数测试只是一个可靠性很高的近似测试
不能保证一定测试出素数
就本问题的要求来说
我认为不适合应用这种方法
  实在倦于这种无聊且无理的纠缠了,我只好建议
或者
建议你抽空也写一个
或者写两个
比较一下
如何?
  可他居然不依不饶:
之前我假设这个问题是需要回答多次的,即给定若干个数字,分别回答这个问题,也是ACM中常见的情形。
  尼玛,题目看懂没有啊,你做这个假设有意义吗?“分别回答”你的方法就占优吗?ACM“回答多次”能多多少次啊,有回答1000000000次的吗?如果不回答1000000000次,你的方法又有什么优势可言?!你前面的“这道题不是应该预先计算素数表,然后用二分查找”不是屁话吗?你这句话中的“这道题”是什么意思?说错了不要紧,至于要用“之前我假设这个问题是需要回答多次的”这种不用拆就穿帮的谎话来掩饰吗?再说“回答多次”你这样写法有耍赖皮的嫌疑懂不懂?我当时的回复是:
原问题确实是需要回答多次
但我没按照那个写
因为我觉得那有些过于偷奸耍滑了
所以只按照回答一次写的
但即使是回答一次
也需要测试这个数周围几个数是否是素数
所以才有了这种方案
 
素数的个数和分布情况见,其中指出,10^9 以内的质数有 5761455 个,所占比率是 5.761455%
另外一个已知的结论是,素数会随着数的增大而变得越来越稀疏。
  靠!中学生都知道的事情,扯这些没用的就表示你高明?但在当时,我只是心平气和地回了一句:
但我的方案只需要3000个左右的素数表
3000/5761455 = 0.052%

你的方法只是惰性计算质数表,本质上并没有什么提高,而且是用的比较原始的质数表生成方式,生成的效率比筛法要慢,粗劣的分析见这里
  什么叫“惰性计算质数表”,自创这些稀奇古怪的别人都不懂只有你自己才懂的术语你不就是想装高深吗?后面引用的狗屁博文连我用脚丫子写出东西的水准都比不上,你也好意思在这里献宝?什么叫“比较原始的质数表生成方式”,你的筛法难道不原始?卖弄驴唇不对马嘴的Rabin-Miller法才是不原始?不过当时我还是耐着性子回复: 
你把用筛法生成质数表的成本给忽略了
那可不是可以忽略不计的一项
而且这不干稀疏什么事情
理由我前面已经说了
 
  除此之外,你是对给定数值附近的每一个数“由近及远”的分别进行判断,因此在质数已经非常稀疏的情况下,难免会计算很多数据。而如果已经有了质数表,则可以直接找到相邻的两个质数,无论它们距离给定的数值有多远。
  “因此在质数已经非常稀疏的情况下,难免会计算很多数据”是基于狗屁不通逻辑上的结论。客气点说是水平太低,不客气地讲就是在强词夺理无理狡辩文过饰非。至于“如果已经有了质数表,则可以直接找到相邻的两个质数,无论它们距离给定的数值有多远。”则纯属是废话,那还用你说吗?我的算法不就是为了回避计算素数表的巨大成本吗?纯粹从逻辑上来说,这话也是狗屁不同,因为既然前面讲我的算法“难免会计算很多数据”,后面难道不应该论证你的方法不需要“计算很多数据”吗?你论证了吗?“可以直接找到”就是不需要“计算很多数据”吗?你的方法在“可以”之前首先必须计算更多数据,为什么你选择性地视而不见呢? 
你把用筛法生成质数表的成本给忽略了
那可不是可以忽略不计的一项
而且这不干稀疏什么事情
理由我前面已经说了
  我话已经说到这份上了,应该算到位了吧。可是还要强词夺理:
毫无疑问,在生成同样大小的素数表的时候,筛法比这种直接的素数判断并生成的算法要快。我给的链接不知道为什么被吞掉了,这里 http://plussai.iteye.com/blog/1070387 有一个粗略的分析。即便是你的方法,把惰性生成素数表改成提前生成素数表,也是一个能够简化问题,提高效率的方法。
  第一句是屁话。没有数组你怎么用筛法,不知道有多少个素数你怎么建立数组?第二句中的链接狗屁不通但还要当作宝贝献出来。第三句是闭着眼睛胡说式的装B,一定要比装得我高明不可。实际上提前生成素数表,不做无用功是不可能的。比如对于100,可以建立2 3 5 7 这样的素数表,但由于最近的一个素数大于100,所以链表在适当的时候必须增补新的素数。如果事先建立2 3 5 7 11 这样的多一个元素的素数表,11也有可能根本用不上。这就是不可能提前生成素数表的原因。
  还是不死心:
下面再说只问一次的情况,如果只问一次的妥妥的用 Rabin-Miller 素数测试。Rabin-Miller 素数测试可以以任意给定的精确度来判断一个数是不是质数,虽然本质上是非确定性的算法,但是如果你要求的精确度非常高,就可以近似认为给出的结果是正确的,比如说这个精确度比 10^-11 还高。
  麻烦你看看题目要求好不好?题目要求的是给定精度前提下吗?是要求给出“非确定性”的解答吗?早已经文不对题了,还厚着脸皮在这里胡扯什么呀?
  回复只能实话直说:
“就可以近似认为”
你弄错了场合
连问题的基本需求都不管不顾了
这个问题根本没有说要求一个“近似”素数 
具体要比较性能的话比较麻烦,我的想法是在给定的区间上均匀的生成 10000 个随机数,然后分别问你和我的两个程序,然后判断总的用时,我相信我的程序会比你的快 :P
但是鉴于你说你考虑的情况是只问一次的情况,我就没做这个不公平的测试。如果使用 Rabin-Miller 素数测试的话,省却了打表的时间,对于 10^9 这个数量级,理论上会比你的算法快出好几条街。
  嗯,给大家看看什么叫不要脸。不公平条件下,我比你快,因此我不测试就是让着你了。使用近似算法比你的精确算法“快出好几条街”,尽管题目要求的不是近似算法。这是不是在狗戴嚼子——胡勒呢?
  请大家顺便注意一下这里的“10000”,在本文后面还会讲到这个数字的故事。 
所以其实我觉得你这个方案不好,增加了不必要的复杂性,还没有更好的解决问题。思路毫无疑问是正确的,但是延迟计算而不是预先计算这个策略选择的不太好,由此带来的问题就是代码非常复杂。
再加上你标题写的是给初学者,我觉得就更应该注意一下,有点把问题搞复杂了。
  不装B会死啊?什么叫“增加了不必要的复杂性”,你的方案简单,小学生都会,但前提是巨大的内存成本和时间成本,是“更好的解决问题”?我的代码复杂,用得着你来下结论吗?我标题里早说得清清楚楚——“超复杂的间接递归”。
   妈的,空话屁话胡搅蛮缠无理取闹,我实在是烦透了。只好回一句
Talk is cheap , show me your code.
  我以为我终于懂得了Linus Torvalds讲这句话时的心情。
  然而我错了,事实证明Linus Torvalds比我幸运,这句话能让在他面前空谈的家伙闭嘴,但却不能让我面前的这位停止批发谬论: 
Rabin-Miller 素数测试可以给出一个非常精确地答案,该答案的精度高于浮点型数据的精度时我看不出来什么理由不接受这个“近似”答案。更何况,整形计算也是有非常微小的几率发生错误的,这是由于CPU制程越来先进,而由量子力学理论所预言的。
 
  理屈词穷!连“精度高于浮点型数据的精度”这种狗屁不通的狗屁都放出来了。看来我实在低估了他的无耻。这不是逼着我说实话吗? 
越来越扯了
直接回答"非常精确地答案"是不是"精确"
连浮点数都上来
这个问题和浮点数半毛钱关系都没有
“整形计算也是有非常微小的几率发生错误的”
天哪!
你们科大就学这个?
老师怎么教的?
回去跟你们老师讲讲
看你们老师脸红不脸红?
  看见没?你跟他谈精确,他跟你扯“近似”,你跟他讲一次计算,他跟你扯“回答多次”,你跟他讲整数运算,他跟你扯“浮点精度”,你不接受他的“近似”答案,他就跟你耍流氓。 
那你是对我说的第一段话不信呢还是第二段话不信呢,还是都不信?我要是写了程序可就要发文章打你脸了。
  大家看看,是不是颇具流氓与无赖的风采和神韵?
  耍流氓不算,还要向你泼脏水:
如果楼主的数学和物理没有学好,不如再去读读书补习一下,没有必要泼脏水。
  尼玛,我数学再怎么不好,也不会在要求证明等腰三角形底角相同时用“高于浮点型数据的精度”的量角器去量啊。你自己脑残要我读书补习是什么道理? 你自污母校还赖我“泼脏水”?尼玛,难道这是要将不要脸进行到底的节奏吗?
  果然,次日又来了:  

  然而事实说了什么?

  首先是自说自话,伪造前提条件:

该题有两种模式,一种是只考虑一次问答的情况,另一种是考虑连续问答

   但又不得不用虚伪的态度承认:(MD,似乎有点露怯啊。露怯还怎么将不要脸进行到底)

首先肯定一下,GarbageMan 的思路是正确的:在给定的 n 附近由近及远的进行素数判定,直到找到一个素数为止。

  尼玛!昨天不是说什么“其实我觉得你这个方案不好,增加了不必要的复杂性,还没有更好的解决问题”吗?不是大言不惭什么“但是延迟计算而不是预先计算这个策略选择的不太好,由此带来的问题就是代码非常复杂。”吗 ?怎么开始自己打自己脸啦?

  然后在伪造的前提下开始圆谎: 

如果考虑多次问答的情况,考虑平均情况,延迟计算最终也会计算出大部分的素数表。与其动态的不断补充素数表,还不如用更有效率的方法直接计算出整张素数表,所以对于这种情况,GarbageMan 的优化是毫无意义的。

  我的算法本来就是为一次问答设计的,对此他早就一清二楚。所以所谓“如果考虑多次问答的情况,考虑平均情况”“所以对于这种情况,GarbageMan 的优化是毫无意义的”难道不是屁话吗?

Rabin-Miller 素数测试,是一种素数判定法则,利用随机化算法判断一个数是合数还是可能是素数。

听起来像是一个不靠谱的算法,但是该算法可以以任意给定的准确率给出可能正 确的答案。当这个准确率足够大时,我们可以近似的认为这个算法给出的答案正确。(这一点遭到了 GarbageMan 的疯狂嘲讽,我猜他不知道为什么无穷大的倒数等于 0

  弱智想赢得辩论,就只能把对手说成弱智。

有关 Rabin-Miller 素数测试是否真的比通过试除法检验素数快,我们暂且将这一问题留待实验结果说明

  嗯,分歧在于Rabin-Miller近似法是否适用于精确问题,但却用Rabin-Miller比试除法检验素数快来论证。尼玛!你这什么逻辑啊?猪都不会这么想!

 GarbageMan 自鸣得意的一个优化就是延迟计算素数表。在我看来,这是一个完全没有必要的,并且极大地增加了代码复杂度的优化。在多次问答的情况下

  看到了吗。偷来的锣鼓敲不得。偷换的前提,只好鬼鬼祟祟地猥琐地藏在后面扭扭捏捏小声地说。

我的建议是,使用初始化方法预先计算从 [2, log MAX_N] 之间的素数,然后再用 GarbageMan 的 get_nearest 方法进行计算。在问答量比较大时,这种方法甚至会比 GarbageMan 优化过的算法还要快。

  这不但是卑鄙的掩饰,同时也是自供。等到您后面知道了“在问答量比较大时”里的“比较大”到底是多大时,您就明白了。 

素数筛法已经十分先进了,甚至有亚线性时间复杂度的算法,因此,在实现生成素数表的情况下,没有理由不选择素数筛法。

  用好听的“亚线性时间复杂度的算法”来蒙骗读者,因为一般人都会觉得线性时间复杂度很好。但对我的算法只有二分之一次方时间复杂度绝口不提。什么叫欺骗,这就是欺骗。选择性地告诉你一部分事实,同时选择性地故意隐瞒一部分事实。 

我建议在计算素数表的时候,直接计算到比 n 的上限还要大的一个素数之后再停止生成素数表,然后通过二分查找,直接确定给定的 n 在素数表中的位置,从而找到距离 n 最近的素数。

  不解释。这个二分法的愚蠢程度和陈良乔对链表使用二分法查找有得一拼。 

算法的思路至此介绍完毕,下面将设计实验来验证我的想法是否正确。

  嗯。伪造前提的铺垫已经完成,下面可以进入角色了。我不知道他在写这段话的时候有没有脸红。

实验分别比较在单次问答模式下,GarbageMan 所用算法和 Rabin-Miller 算法的用时;在多次问答模式下,GarbageMan 所用算法和其他算法的用时。其中,在多次问答模式下,需要对 GarbageMan 所用的算法进行一些微调,以保证测试的公平性和正确性:

  尼玛。把我专为单次问答设计的算法进行肢解篡改,以便绑架到他自己亲口承认的“不公平的测试”中去,但还要假装“公平”。这等的虚伪和伪善需要几吨包天狗胆和寡廉鲜耻啊?!

编译选项 /STACK:10485760,1048576 /O2

  轻描淡写地提了一下编译选项。这个编译选项很多小朋友不知道,因为几乎没人用过。这个选项是因为他自己的筛法计算109内的素数表需要巨大的内存开销。素数表对他自己的方案是有利因素,但对于我的方案来说,由于根本不需要这么大的内存,所以不但没好处,反而有坏处。因为访问大块内存是有时间开销的。我相信,微软如果知道这个编译选项会被无耻之徒这样利用,一定会对设立了这个选项而后悔不迭。微软,你欠我一个道歉。

给出的测试代码依赖于 C++11 标准中提供的随机数生成函数,因此只能在 Visual Studio 2013 和较新版本的 g++,clang++ 上不需要修改的通过编译。使用较低版本的编译器编译时,可以结合 boost 库提供的支持,进行有限的修改后通过编译。 如果使用 g++ 或者 clang++ 进行编译的话,请使用参数 -O2 -std=c++11

  大家看到了吗?结果依赖于随机数生成函数,很多编译器的随机数生成函数都有毛病,这个毛病就是不那么“随机”。这里他留了一个伏笔,以后有人发现作弊,他可以赖到C++11 标准中提供的随机数生成函数上去。另一个作用就是是暗补他前面说过的“均匀的生成”“随机数”的谎话。除此之外,把我的C代码当作C++代码编译,“结合 boost 库”,这都是在不动声色地作弊。这就是他口口声声的“公平”。

实验结果

当设定 n 的范围在 [1, 10^6 - 10],且生成 50,000 个随机数时,多次问答模式下的测试结果如下:

Elapsed : 3900005ms    // GarbageMan 的方法
Elapsed : 500001ms     // 积极计算的方法
Elapsed : 2890109ms    // Rabin-Miller 算法
Elapsed : 270015ms     // 素数筛法和二分查找

再测一次:

Elapsed : 3920017ms    // GarbageMan 的方法
Elapsed : 500001ms     // 积极计算的方法
Elapsed : 2800004ms    // Rabin-Miller 算法
Elapsed : 300001ms     // 素数筛法和二分查找

大数定律可知,GarbageMan 的算法在平均情况下运行效率较低,并且低于积极计算的方法,可见不仅白优化了,还起到了负面效果。

由于实验结果显示,在多次问答模式下 GarbageMan 的算法运行效率仍然低于为单次问答模式所设计的 Rabin-Miller 算法,因此没有必要再进行单次问答模式的实验。

  明眼人一下就能看出这段的毛病,但许多见识不多的小朋友可能看不出来。注意到“在多次问答模式下 GarbageMan 的算法运行效率仍然低于为单次问答模式所设计的 Rabin-Miller 算法”这句话的玄机了吗?在“多次问答模式下”,无论如何我的算法都是吃亏的,因为我的算法本来就是为单次问答设计的。“多次问答模式下”会把我的算法的优势彻底铲除。但是Rabin-Miller 算法不受单次模式或多次模式的影响。他刻意造了这个生涩难懂的句子,以回避公布单次问答模式下我的算和Rabin-Miller 算法的结果。然后用刻意营造出的结果,以及八竿子打不着的不相关的用来蒙人的“大数定律”,以便装腔作势地宣布他精心炮制的谎言。

  前面他曾信誓旦旦煞有介事地表示,他要“分别比较在单次问答模式下”,但要宣布结果时,却硬生生地把这话自己吃了下去。无耻至斯,叹为观止!

  注意到这段开头的那个“ 50,000”了吗?还记得前面提到过的他在回复中说的那个“ 10000 个随机数,然后分别问你和我的两个程序,然后判断总的用时,我相信我的程序会比你的快 ”“会比你的算法快出好几条街”吗?为什么他要把“10000”个随机数改成“50,000”呢?原因很简单,生成一个109之内的素数表需要很多时间成本,为了“证明”他的方法快而又想用“事实”来编造谎言,这个巨大的时间成本就需要被分摊。这就是原因,没有之一。

  后面的结论就不引述了,谁愿意相信谁相信!这个世界上一向不乏乐于相信谎言的傻B!

  回过头来看这篇奇文标题—— GarbageMan 的《一个超复杂的简介递归》——对延迟计算的实验和思考。我们不禁要问,他到底驳了什么了?

  我认为Rabin-Miller 算法不适合精确计算,他驳了吗?他吭哧瘪肚地用移花接木的手法刻意地营造了一个实验,我就算是退一万步,抛开他的种种龌蹉的作弊作假不提,就算假定他的结论是对的,那也只说明了Rabin-Miller 算法比我的快而已,那是对“Rabin-Miller 算法不适合精确计算”这个观点的驳斥吗?

  从这里不难发现,中科大的这个HCOONa的科学素质极低,基本上是一个逻辑文盲。他的奇文中这种低素质不自觉的流露到处都是,比如标题中竟然明晃晃地出现了两个错别大字“简介”,经验告诉我,优秀的程序员对错别字非常敏感,一般不会写错别字。再比如素数的定义: 

素数,又称质数,指除了 1 和该整数自身外,无法被其他正整数整除的正整数。

  尽管他装模作样的在参考资料部分引用了

  但维基百科中对素数的定义全然不是这么回事。按他的定义,1也是素数,这和谭浩强是同一个水平。一个认真的小学生都能发现他的这个错误。所以科学素质其实跟学历没有必然关系,有很多高学历者的科学素质并不高,有的甚至是自觉或不自觉的骗子。比如,清华大学的几朵奇葩教授,……以后有机会再跟大家聊。

  我认为计算109量级的素数表,哪怕是用简单的筛法,也必然要付出高昂的时间成本和内存成本作为代价。他驳了吗?他只是用精心构建的实验去掩盖而已,而且掩盖的手法实在不咋地,以至于处处露马脚。即便抛开这个人极其龌蹉低下的技术品德不谈,作为一个骗子,他也是一点技术含量都没有。被扒光内裤之后,你看到的无非就是一个无知无耻之徒在裸奔而已。

  曾经有人说过,世界上有三种谎言:谎言,弥天大谎,统计局公布的数字。现在我们有幸见识了第四种,就是HCOONa创造的这种用似是而非的理论,精心炮制的实验,无耻的伪公平架势,选择性的事实构筑的谎言。

  为什么他要炮制这些谎言欺骗大家,原因只可能有一个,前一天晚上在我的博文评论中放了半宿不通的狗屁,导致肚子里憋了两大泡稀屎:

在此也劝告 GarbageMan,没什么本事就别在那叫嚣了,还写什么《C语言初学者代码中的常见错误与瑕疵》,误人子弟。
GarbageMan 多次对我进行人身攻击,并且侮辱我的母校。在此我要说一句,GarbageMan 你真是人如其名——渣男,人品渣,技术也渣。

就这样一直忍到第二天中午,实在憋不住了,没有手纸找不到厕所也顾不得了,只好~~~~喷吧!

  好臭! 

  终于彻底地应验了鲁迅老先生的那句话:“始于作伪,终于无耻!”。

posted @ 2013-12-05 10:17  garbageMan  阅读(6403)  评论(127编辑  收藏  举报