很快是多长时间?
这个问题我十年前问过一个中科院的博士,他告诉我,在量子计算机问世之前,没有任何一台现代计算机能达到“非常快”的程度。 这里引用一个经典的数学难题——旅行者问题:假设你有两位旅行者A和B,他们分别在你前方100公里和200公里,现在你想追上他们,用现在的交通速度(比如汽车或者飞机),需要多长时间? 最显而易见的答案是,开100公里/小时的车跑一小时,肯定能赶上A了;接着赶200公里的路再碰到B。 但是这个答案是最糟糕的!因为实际上A和B之间的距离是在逐渐缩小的,当A走到你前面99公里处的时候,B刚好到了你前面201公里;而当A走到你前面101公里时,B也移动到203公里....如此这般,无论你怎么努力,你永远都不能够同时追上A与B,这是不是一件很荒谬的事情? 这就是“旅行者问题”最吸引人的地方:看起来多么合理的一个问题,怎么解起来竟然那么困难?!这个问题之所以难,是因为它有一个非常讨厌的特性——“路径相关”。这个问题可以重写为如下形式: 在这个问题中,你和A、B的位置都是通过参数给出的——这就导致了这个问题具有“路径相关性”,而大多数的优化问题都不具备这种特性。传统的求解方法都不适用。
十年前的那个时候,我在北大读计算机硕士,正好修了一门叫做“计算智能”的课。讲课的老师是个年轻的中科院博士,他给出了一个问题简单的解决方案:模拟退火算法。 所谓模拟退火算法是一种随机化搜索算法,它的基本思路是这样的:先让电脑生成一组可能的解(就是那两个旅行者可能的位置),然后判断这组解是否满足所有的约束(两个旅行者的距离不能大于100km),如果这步通过了,就把这一组解保存下来;接着让电脑产生更多的解,重复上面的过程……这样不停地迭代,直到得到一组满意的解为止。 这个方案是不是能解决“旅行者问题”呢?其实也不尽然,因为它需要事先知道所有可能的解,而这个问题的解是具有“路径相关的”,换句话说,它是组合数很多的一类问题,很难一一列举,因此这种办法并不能算完美的解决办法。
不过,这位老师告诉了我们一个重要的理念:对于大多数难以解决的问题,我们不需要寻求一个完美无缺的解决方案,有时候只要找到一个“足够好”的方案即可。 模拟退火算法就是一个“足够好”的解决方案,它已经能够在大多数情况下解决问题了,而无需追求一个完美至极的解法。 回到题主的问题上来——如何快速排序?快速排序需要一个标准,这个标准就是“交换次数最少”的排序方法是最快。
根据动态规划的方法,我们可以把整个问题分解成多个子问题求解,每个子问题都是已经排好序的数字(1,2,…,n),然后再根据子问题的解导出原问题的解。为了尽可能降低动态规划的复杂度,我们需要找一些能够降低数据量的边界条件。 一个很简单的边界条件是:首先将最大值与最小值排好序,然后整个数组除了最大的数和最小的数之外都排列好序。按照这样的原则对数组进行划分,然后再逐一处理每一个子集,最后恢复整数的顺序就得到了最快排序的方法。 以上是我当时听这位博士讲快速排序的一部分内容,已尽量还原当时的场景。 后来,我做了算法工程师,也经常遇到难以解决的问题,这时候我就用他的话来勉励自己:很多时候,我们不需要追求极致的解,而是找一个“足够好的解”就可以了。