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

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


第一种方法


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


目标


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


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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值