一些可能有点用的算法

Published: by Creative Commons Licence

  • Tags:

写这篇博客是为了介绍一些有趣的与现实生活息息相关的算法问题,以及解决方案。其中大部分都是我自己亲身经历过,并且自己独立求解的。这里写出分享给大家。

随机数组合选取问题

考虑这样一个问题,要求你设计一个函数random_set(N, K),其中NK。要求你从0,,N1的所有2N种可能的组合中,只考虑其中包含K个元素的组合(总共有(NK)种),并以等概率返回其中任意一个。

这个问题挑战点在于,我们希望最小化理论上的时间复杂度,由于至少要返回一个大小为K的集合,因此时间复杂度的下界是O(K)

算法1:利用洗牌算法,把0N1N个数先打乱并挑选前K个数,这样可以直接保证得到的结果是等概率的。算法的时间复杂度显然是O(N)的。

接下来考虑另外一个很简单的算法。

算法2:我们维护一个空集合。之后每次随机枚举一个值,如果值不在集合中,就加入,否则丢弃。重复这个过程直到集合大小达到K

这个算法找到第i数的期望步数是NN+1i,如果我们用哈希表来维护这个集合,则时间复杂度为O(NKi=11N+1i),其结果大致为O(NlnNNK)

这两个算法看上去都不太行,但是一旦结合在一起,就会产生非常有趣的效果。

  • 2KN时,我们采用算法2,这时候找到其中任意数的期望步数都不超过2,因此时间复杂度为O(K)
  • 2K>N时,我们采用算法1,可以发现时间复杂度为O(N)=O(K)

红包算法

考虑这样一个问题,你要把N元红包平均分给K个人,最小精度单位为分,设第i个人分到了xi分(必须为整数),要求返回x1,,xN,且在所有满足Ki=1xi=N0x1,,xN的可能的组合中,每种组合返回的概率相同。其中1N1081K104

由于单位不同,我们可以把N先乘上100,这样我们就是用的分为单位。

这个问题实际上它的组合解释是:有N+K1个不同的球,从中选择K1个不同的球,每种选法的概率相等。

这实际上就回到了随机数组合选取问题了。因此我们可以直接得到一个O(K)时间复杂度的算法。

桌游rating算法

这个问题是我当时在设计一个桌游rating板块的时候遇到的。这里我们需要根据玩家每一轮的胜负,来实时计算他的rating。一个好的rating算法应该满足下面要求

  1. 最近比赛对用户rating的影响更大,这样才能鼓励用户参加比赛
  2. 很早以前的比赛对用户的rating的影响,因为rating体现的是用户在不断训练后的新的强度
  3. 用户最近一场比赛胜利,总rating一定不降;最近一场比赛失败,总rating一定不升。

一个简单的方式是设计一组权重w1,,wN,其中wi表示最近第i场比赛结果的权重,记xi表示用户第i场比赛的胜负,胜利为1,失败为1。总分为Ni=1wixi

为了满足前面的要求,我们希望w1w2wN0。一个简单的选择就是wi=1T+i。其中T为一个固定常数。

设计是完成了,但是很显然它目前只满足要求1和2,下面我们证明它同时还满足要求3。

非常简单,假如x1=1,设I为失败的场次的下标集合,那么新的总分为Ni=1wi2iIwi,而原先的总分为N1i=1wi2iIwi1。计算新老总分的差值可以得到

wN+2iIwi1wi

很显然上面式子的结果非负,因此新分一定不低于老分。对于x1=1的情况证明类似。

数值信息压缩问题

考虑我们现在有一副H×W的灰度图,每一个点的灰度用Q位来表示。

现在我们希望压缩这个图像。具体的压缩算法非常简单,我们用调色板的方式,只支持特点的K种深度(从2Q中选择),这样每个颜色只需要用logK个bit来表示即可。设原来的灰度为x,则我们选择调色板中灰度最接近的那个颜色替代它。

但是提高了压缩效率,我们也不能过度失真。令Ai表示灰度图的第i个像素的灰度,Bi表示匹配的调色板颜色的灰度。共N=HW个像素。对于i0,我们定义如下惩罚函数

Li=ABi

下面我们考虑如何找到最合适的K个颜色,从小到大排列为c1,,cK

我们不难用动态规划来求解。首先我们将所有颜色离散化(很显然只有颜色出现次数会影响损失函数的结果),设不同的颜色数为C。令cost(l,r)表示将灰度lr之间的灰度映射到同一颜色的最少惩罚值。令dp(i,j)表示仅考虑前j小的颜色,用i个不同的颜色去替代,最少的惩罚函数值。很显然我们的结果是dp(K,C),用回溯法可以找到最优解时所有选择的颜色。

dp(i,j)=mink<jdp(i1,k)+cost(k+1,j)

这个公式暴力求解的时间复杂度是O(KC2)。在C比较大的时候求解难度很高。

我们可以用决策单调性来优化这一步骤,这样可以将时间复杂度降低到O(KClogC)

下面我们证明决策单调性的必要条件满足,即对于任意P范数惩罚函数,下面公式对于所有k<j<i满足

cost(k,i+1)cost(k,i)cost(j,i+1)cost(j,i)

我们记opt(l,r)表示让cost(l,r)最小化所采用的颜色。很显然有opt(k,i)opt(k,i+1)opt(j,i)opt(j,i+1),同时还有opt(k,i)opt(j,i)opt(k,i+1)opt(j,i+1)

实际上,我们仅考虑公式左边代表的增量的时候,且仅考虑颜色ki,第一步从opt(k,i)opt(k,i+1)带来的变化,我们记增量为D1,之后再加入颜色i+1,带来的新的增量为D2

同理,我们考虑公式右边代表的增量的时候,且仅考虑颜色ji,第一步从opt(j,i)max(opt(k,i+1),opt(j,i))带来的增量变化,记作D1,同理加入颜色i+1后带来的新的增量为D2

很显然有D1D1D2D2,因此我们有

cost(k,i+1)cost(k,i)cost(j,i+1)cost(j,i)cost(j,i+1)cost(j,i)