一些可能有点用的算法
写这篇博客是为了介绍一些有趣的与现实生活息息相关的算法问题,以及解决方案。其中大部分都是我自己亲身经历过,并且自己独立求解的。这里写出分享给大家。
随机数组合选取问题
考虑这样一个问题,要求你设计一个函数random_set(N, K)
,其中N≥K。要求你从0,…,N−1的所有2N种可能的组合中,只考虑其中包含K个元素的组合(总共有(NK)种),并以等概率返回其中任意一个。
这个问题挑战点在于,我们希望最小化理论上的时间复杂度,由于至少要返回一个大小为K的集合,因此时间复杂度的下界是O(K)。
算法1:利用洗牌算法,把0到N−1共N个数先打乱并挑选前K个数,这样可以直接保证得到的结果是等概率的。算法的时间复杂度显然是O(N)的。
接下来考虑另外一个很简单的算法。
算法2:我们维护一个空集合。之后每次随机枚举一个值,如果值不在集合中,就加入,否则丢弃。重复这个过程直到集合大小达到K。
这个算法找到第i数的期望步数是NN+1−i,如果我们用哈希表来维护这个集合,则时间复杂度为O(N∑Ki=11N+1−i),其结果大致为O(NlnNN−K)。
这两个算法看上去都不太行,但是一旦结合在一起,就会产生非常有趣的效果。
- 当2K≤N时,我们采用算法2,这时候找到其中任意数的期望步数都不超过2,因此时间复杂度为O(K)。
- 当2K>N时,我们采用算法1,可以发现时间复杂度为O(N)=O(K)。
红包算法
考虑这样一个问题,你要把N元红包平均分给K个人,最小精度单位为分,设第i个人分到了xi分(必须为整数),要求返回x1,…,xN,且在所有满足∑Ki=1xi=N且0≤x1,…,xN的可能的组合中,每种组合返回的概率相同。其中1≤N≤108,1≤K≤104
由于单位不同,我们可以把N先乘上100,这样我们就是用的分为单位。
这个问题实际上它的组合解释是:有N+K−1个不同的球,从中选择K−1个不同的球,每种选法的概率相等。
这实际上就回到了随机数组合选取问题了。因此我们可以直接得到一个O(K)时间复杂度的算法。
桌游rating算法
这个问题是我当时在设计一个桌游rating板块的时候遇到的。这里我们需要根据玩家每一轮的胜负,来实时计算他的rating。一个好的rating算法应该满足下面要求
- 最近比赛对用户rating的影响更大,这样才能鼓励用户参加比赛
- 很早以前的比赛对用户的rating的影响,因为rating体现的是用户在不断训练后的新的强度
- 用户最近一场比赛胜利,总rating一定不降;最近一场比赛失败,总rating一定不升。
一个简单的方式是设计一组权重w1,…,wN,其中wi表示最近第i场比赛结果的权重,记xi表示用户第i场比赛的胜负,胜利为1,失败为−1。总分为∑Ni=1wixi。
为了满足前面的要求,我们希望w1≥w2≥⋯≥wN≥0。一个简单的选择就是wi=1T+i。其中T为一个固定常数。
设计是完成了,但是很显然它目前只满足要求1和2,下面我们证明它同时还满足要求3。
非常简单,假如x1=1,设I为失败的场次的下标集合,那么新的总分为∑Ni=1wi−2∑i∈Iwi,而原先的总分为∑N−1i=1wi−2∑i∈Iwi−1。计算新老总分的差值可以得到
wN+2∑i∈Iwi−1−wi很显然上面式子的结果非负,因此新分一定不低于老分。对于x1=−1的情况证明类似。
数值信息压缩问题
考虑我们现在有一副H×W的灰度图,每一个点的灰度用Q位来表示。
现在我们希望压缩这个图像。具体的压缩算法非常简单,我们用调色板的方式,只支持特点的K种深度(从2Q中选择),这样每个颜色只需要用logK个bit来表示即可。设原来的灰度为x,则我们选择调色板中灰度最接近的那个颜色替代它。
但是提高了压缩效率,我们也不能过度失真。令Ai表示灰度图的第i个像素的灰度,Bi表示匹配的调色板颜色的灰度。共N=HW个像素。对于i≥0,我们定义如下惩罚函数
Li=‖A−B‖i下面我们考虑如何找到最合适的K个颜色,从小到大排列为c1,…,cK。
我们不难用动态规划来求解。首先我们将所有颜色离散化(很显然只有颜色出现次数会影响损失函数的结果),设不同的颜色数为C。令cost(l,r)表示将灰度l到r之间的灰度映射到同一颜色的最少惩罚值。令dp(i,j)表示仅考虑前j小的颜色,用i个不同的颜色去替代,最少的惩罚函数值。很显然我们的结果是dp(K,C),用回溯法可以找到最优解时所有选择的颜色。
dp(i,j)=mink<jdp(i−1,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)。
实际上,我们仅考虑公式左边代表的增量的时候,且仅考虑颜色k到i,第一步从opt(k,i)到opt(k,i+1)带来的变化,我们记增量为D1,之后再加入颜色i+1,带来的新的增量为D2。
同理,我们考虑公式右边代表的增量的时候,且仅考虑颜色j到i,第一步从opt(j,i)到max(opt(k,i+1),opt(j,i))带来的增量变化,记作D′1,同理加入颜色i+1后带来的新的增量为D′2。
很显然有D′1≤D1且D′2≤D2,因此我们有
cost(k,i+1)−cost(k,i)≥cost′(j,i+1)−cost(j,i)≥cost(j,i+1)−cost(j,i)