Aug 23

     

    经典 Geek 动画 Futurama 上周播出了第 6 季的第 10 集 The Prisoner of Benda 。在这一集中,教授 Farnsworth 发明了一种“心灵对换机”,它可以把两个人的思想互相对换,使得 A 的大脑跑进 B 的身体里,而 B 的大脑则跑到 A 的身体里。 Farnsworth 和 Amy 都想得到对方的身体,便成为了这台机器的第一对实验者。等到他们爽够了想换回来后, Farnsworth 却发现了一个严重的问题:已经互换过大脑的两个身体不能再次进行大脑对换操作。但这并不表示两个人完全没有希望回到自己的身体里—— Farnsworth 突然想到,或许可以用第三者作为一个临时的大脑储存空间,从而实现间接对换。正巧机器人 Bender 进了实验室,于是(身为 Amy 的) Farnsworth 和 Bender 又坐上了机器,这下 Farnsworth 的大脑便跑到 Bender 身体里了,而 Bender 的大脑则进了 Amy 的身体里。此时 Farnsworth 才意识到,引入一个第三者是不够的——再让(身为 Bender 的) Farnsworth 和(身为 Farnsworth 的) Amy 互换大脑,可以让 Farnsworth 恢复原状,但同时 Amy 的大脑会跑到 Bender 的身体里去;这样 Bender 和 Amy 的身体正好颠倒了,而他们却已不能再次使用机器。换句话说,要想恢复两个换位了的大脑,需要引入不止一个新的人。
    但现在,问题已经变得更加复杂了——这下已经产生了三个大脑位置错乱的人。大家很容易联想到一个更一般的问题:给定 n 个人以及他们之前使用“心灵对换机”的记录,至少得引入多少个新的人,才能让所有人的大脑都“物归原主”呢?

查看更多 »

Apr 9

  

    书架的某一层里放了一套百科全书,但它们排列的顺序却是乱的。一个傻子想要把这套书排好顺序,也就是说他想要书架里的书从左至右分别是第 1 卷,第 2 卷,……,第 n 卷。他给这套书排序的办法是这样的:不断取出一本原应放在更左边的书,插进它该在的位置。比方说,某本书的卷号是 3 ,它的位置却是左起第 5 ,位于其目标位置的右侧。那么傻子就可以把这本书拿出来,插入当前左起第 2 本书的右边,把那些占了它位置的书挤到更右边去,而不管这一操作是否会破坏掉已经就位的书。注意到这种排序法很可能捡了芝麻,丢了西瓜,为了一本书的位置而破坏掉一连串原已排好的书,可谓是鼠目寸光,缺乏远见。我们的问题是,在哪些情况下这样的排序法最终一定能实现排序,哪些情况下可能会陷入永无止境的死循环?

查看更多 »

May 30

网上有各种直观的排序算法图形化演示(见这里这里),我自己也曾经做过一个
今天我看到了一个我所见过的最酷的、最可爱的排序算法演示。
某网站被干掉了后,大家会错过很多精彩的视频。我注册了一个土豆网的帐号,把一些精彩的视频搬过来与大家分享。

原地址:http://www.youtube.com/watch?v=vxENKlcs2Tw

Apr 20

昨天我突发奇想,写了几段Mathematica代码,生成了各种排序算法的内存变化图。图中每一个新的横行都表示数组的一次更新,数字大小用颜色来表示。你可以直观地看到这些算法是如何把乱序数组一点一点变为有序的。效果还是很令人满意的,不少算法的内存轨迹都相当美观,相当有艺术性。

图很大,我就不在首页上显示了,大家点“查看更多”看图吧。

查看更多 »

Mar 6

    上次我们谈到,我们考虑时间复杂度时往往假设任意大的整数运算(赋值、四则运算、取余运算、比较运算、位运算包括左移右移)都可以在常数时间内完成,殊不知这留下了一个非常具有研究价值的漏洞:能否利用计算机理想模型中的整数运算,把问题打包成超大整数后并行计算,从而办到一些在普通计算机上无法办到的事情?我们在上一次的文章中介绍了利用“大整数随便算”的漏洞“耍赖”得到了一个线性时间的排序算法。这个漏洞真的已经被充分利用了吗?我们还能从里面榨出多少汁水来?令人无法想象的是,线性时间的排序算法远远没有挖掘到理想大整数运算的巨大潜力,事实上我们能做到常数时间的排序!问题和解答仍然来自Using your Head is Permitted,在这里向Michael Brand表示深深的膜拜。
    自然,说“常数时间排序”是有前提条件的,否则即使读入输出也得耗费线性的时间。不过,我们可以假设所有待排序的数都已经打包进一个大整数里,输出时也无需解包,直接返回另一个大整数即可。在这样的情况下,我们完全可以用常数时间完成排序。换句话说,我可以用O(1)的时间,“一下子”就把0100 0111 0001 0010变成0001 0010 0100 0111,不管这个大整数里面装了多少个数。为了方便大家阅读和思考,我们再取一些名字,方便描述。我们把由多个数构成的大整数叫做“整数串”。整数串中所含的数都是二进制,它们用空格隔开。整数串中每个数的位数都必须相等,位数不够用零补足。我们把这个位数叫做“定宽”,本文例子的定宽都是4。

查看更多 »

Jan 10

    当我们研究复杂度时,我们往往会将现实机器进行理想化。例如,我们说冒泡排序是O(n^2)的,这其实是不准确的。这个论断假设整数之间的比较运算是O(1)的,而事实上它们是O(log(min(|a|,|b|)))的。多数时候我们都认为这种机器模型的理想化是合理的,毕竟这让问题简化了不少,并且也能反映出算法的本质。但大家有想过吗,这个“大整数随便算”的假设其实是一个超级大漏洞,我们可以利用理想模型中的这一漏洞来作弊,获得时间复杂度更低的算法。上个月,Michael Brand在他的UyHiP里就提出了这样一个问题:假设计算机对任意大整数的赋值、四则运算、取余运算、比较运算、位运算(包括左移右移)的复杂度都是常数级别,你能否设计出一个O(n)的排序算法来?

    我非常喜欢这个题目。月初的时候我就提交了一个正确的算法。我们将用左移和加法运算把整数序列编码成一个超大整数,然后利用排序网络进行并行排序。这个算法比较复杂,你可以按照下面的思路一步一步得到这个算法。

1. 如何用位运算来取绝对值

2. 给出两个正整数a, b,不用比较运算和判断语句如何把小数赋给a,大数赋给b?
    提示:和加差除以2等于大数,和减差除以2等于小数

3. 如何利用位运算把整数序列编码成一个超大整数?
    例如把(二进制数)11, 1011, 1110, 1编码为一个数00011 01011 01110 00001

4. 如何用位运算给超大整数中的所有数同时取绝对值?

5. 给出两个超大整数a, b,不用比较运算和判断语句如何把对应位置上的小数赋给a的对应位置,大数赋给b的对应位置? 例如把
      a = 000010 000111 000100 001001
      b = 000001 001011 000011 011111
    变成
      a = 000001 000111 000011 001001
      b = 000010 001011 000100 011111

6. 如何实现奇偶移项排序

    最后,由于奇偶移项排序只有O(n)层,因此整个算法是O(n)的。

    但是,这个算法太繁琐了,不具有美观性。虽然这个算法是我自己想出来的,但我仍然很不满意。待我看了这个月Michael Brand发布的答案后,我一拍大腿,哎呀,还有一个如此简单巧妙的算法我没想到!相比之下,我的算法太复杂了,原因就在于我还没有充分挖掘到“大整数的常数级运算”的潜力。这个理想模型的假设太强大了。打开思路,放宽思维,大胆想象,从更大的尺度上来思考,我们可以得到一个简单得出奇的线性排序算法来。

查看更多 »

Oct 25

    今天是10月25日。祝古汉MM生日快乐。
    曾经有一段时间这个Blog的访问量和订阅量剧增,后来才知道因为这个Blog上的一道牛B题目被出成POJ的月赛题了。那道题目真的很好玩,题目和解答都很简单有趣。其实我挺喜欢这种“给出一个算法并证明该算法最优”类型的数学题目。这里再和大家分享一个类似的比较老的题目,有兴趣的话不妨先想想再看答案。
    一次“交换”操作是指将数列中的两个数位置对换。我们假设,互不相交的若干个交换操作可以一次同时进行;换句话说,如果k个交换中任两个都不会对同一个数进行操作,那么这k个操作可以并行完成。例如,在数列

  10, 6, 8, 5, 2, 3, 1, 4, 7, 9

    中,我们可以同时交换第4个数和第6个数,第8个数和第9个数,以及第3个数和第7个数。经过这一次“并行交换”后,数列变为:

  10, 6, 1, 3, 2, 5, 8, 7, 4, 9

    任意给定一个长度为n的全排列,请问对该序列进行排序最坏情况下需要多少次并行交换?给出一种具体的算法,说明这个次数足够了;并且给出一种最坏情况,证明这个次数是必需的。

查看更多 »

Oct 4

    上次那篇日志发布之后,据说大家解题的热情相当高。Michael Brand告诉我说,他收到了很多来自中国的邮件,他感到非常高兴。在揭晓谜底之前,还是让我们先回顾一下题目:

    对数列的一次“块移动”是指把一段数取出来插入到数列中的另一个地方(说穿了就是一次选择剪切粘贴的操作)。例如,数列1,4,5,6,2,3,7可以通过一次块移动完成排序(把456挪到3后面)。那么,想要让一个1到n的逆序排列n, n-1, ..., 3, 2, 1变为顺序排列,最少需要多少次块移动?给出你的算法,并证明这个移动数目不能再少了。

    需要指出的是,答案并不是n-1那么简单。当n=5时,只需要三步就可以搞定了:

 5  4 [3  2] 1
 3  2  5 [4  1]
[3  4] 1  2  5
 1  2  3  4  5

查看更多 »

« 更早的日志