普林斯顿公开课 算法2-5:洗牌算法

来源:互联网 发布:2016网络诈骗案破案率 编辑:程序博客网 时间:2024/05/15 23:54

洗牌就是将已经排序好的数列打乱顺序


第一种方法


基本想法就是给每个元素标注一个随机的小数,然后再对序列进行排序。但是这种方法的开销比排序算法还要高。


目标


寻找一个线性复杂度的洗牌算法。


Knuth排序


将原始序列分成A B两个序列,A序列表示已经乱序,B序列表示尚未乱序。


步骤

  1. 将B序列的第一个元素取出,加入到A序列的末尾

  2. 从A序列中随机取出一个元素,与末尾的元素互换

  3. 循环直到B序列中没有元素为止


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import java.util.Random;
 
public class Shuffle {
    public static void shuffle(Object[] li) {
        Random r = new Random();
 
        for(int i=0;i<li.length;i++){
            int b=r.nextInt(i+1); // 注意这里使用nextInt(i+1),而不是nextInt(i)或nextInt(n+1)
            Object temp = li[b];
            li[b] = li[i];
            li[i] = temp;
        }
    }
}


洗牌故事


某在线纸牌游戏的洗牌算法是这样的:


for i := 1 to 52 do begin
    r := random(51) + 1;
    swap := card[r];
    card[r] := card[i];
    card[i] := swap;
end;


这里有多个BUG


  1. 随机数不会随到最后一张卡片

  2. 乱序不是均匀的,取随机数应当是random(i),而不是random(51)

  3. random()使用2^32的整数作为种子,因此有2^32种洗牌结果

  4. seed=从0点到现在的毫秒数,因此只有8640万种洗牌结果


根据这些BUG,攻击者只要知道5张牌的顺序,时间与服务器同步,就可以实时计算出后续所有的卡片了。


最好的洗牌方法


  • 有煽值高的随机数源,比如随机数硬件

  • 经常检查随机数是否符合统计学规律,因为随机数硬件坏了之后是完全没有症状的

  • 采用均匀的洗牌算法


最后,要说明一点洗牌算法不是那么简单的。

0 0
原创粉丝点击