zoukankan      html  css  js  c++  java
  • 闲话shuffle(洗牌)算法

    工作中经常会用到洗牌算法,看到这篇文章不错,原文摘自:http://www.atatech.org/article/detail/11821/928  作者:子仲
     
    场景
    洗牌算法的应用场景其实很多,运营的坑位固定,需要随机显示是一种场景。用音乐播放的时候,随机播放列表,其实主要用的就是shuffle算法。谁都不希望随机一两次之后又听到同一首歌。IPod shuffle的卖点其实就在这。我们平时在人数固定的情况下就会用到shuffle算法。总得来说洗牌算法场景较多,下面我们来看看它的实现原理。
     
    实现
    洗牌算法是实现方式
    第一种我叫它正常洗牌抽牌法
    桌上有n张扑克牌,第i次从桌子上等概率随机取出一张扑克牌,此时牌堆的减少一张,在n-i的牌堆里面再随机等概率抽取一张,直到抽完为止。
    deck 为扑克牌 
    1    for i <-  0 to  n
    2    d<-random(n-i)
    3    swap(deck[d],deck[n-i])
    还有一种方式就是标记法,就是抽牌的人从牌堆里面随机抽出一张牌,然后标记为已读,然后放回牌堆里面,继续随机抽取,如果遇到“已读‘标记的,就放回。直到抽到无已读为止。这种方式和前面的方式实现简直就是高富帅和屌丝之间的差别。因为这种方式就是你要随机到所有的牌都出现过为止。一句话“远离此方法,争做有爱青年“
     
    javascript实现
     
     
    第一种实现:
    1    function shuffle0(array) {
    2        array.sort(function() {
    3            return Math.random() - 0.5;
    4        });
    5        return array;
    6    }

     这是一种不完全的洗牌算法

     
    第二种实现:
    1    function shuffle1(array) {
    2        for (var i = array.length - 1; i > 0; i--) {
    3            var j = Math.floor(Math.random() * (i + 1));
    4            var temp = array[i];
    5            array[i] = array[j];
    6            array[j] = temp;
    7        }
    8        return array;
    9    }

    这是理想的算法

     
    第三种实现:
    01    function shuffle2(array) {
    02     var copy = [], n = array.length, i;
    03     
    04      while (n) {
    05        i = Math.floor(Math.random() * array.length);
    06     
    07        if (i in array) {
    08          copy.push(array[i]);
    09          delete array[i];
    10          n--;
    11        }
    12      }
    13     
    14      return copy;
    15    }

    这就是前面说到的屌丝算法

     
    第四种实现:
    1    function shuffle3(array) {
    2      var copy = [], n = array.length, i;
    3      while (n) {
    4        i = Math.floor(Math.random() * n--);
    5        copy.push(array.splice(i, 1)[0]);
    6      }
    7     
    8      return copy;
    9    }
     
    看来像O(n) 但是由于splice的缘故,这个算法也有可能出现O(n^2)的情况
    demo
    alt
    多做一步
    谁才是中国好随机?才是真随机?
    接下来让我们重复30遍来看看效果
    我们来制定一个判断标准:随机之后出现在自己的位置的数量越少说明随机的越彻底,反之则说明越不彻底
    1    var sum = 0;
    2    for(var i=0;i<30;i++) {
    3         var a = [0,1,2,3,4,5,6,7,8,9];
    4         console.log(shuffleN(a));
    5         for(var j=0;j<10; j++) {
    6              if(a[j]==j) sum++;
    7        }
    8    }
    9    console.log('随机重复数:'+sum)

     第一种

    alt
    第二种
    alt
    第三种
    alt
    第四种
    alt
    性能对比
    我们来对四种方法做下性能对比: http://jsperf.com/sm-shuffle  
    alt
    通过对比
    第二种方法的效率最高
    第三种效率最差
     
    总结
    选择一种算法实现,时间复杂度、空间复杂要考虑,对于像这种随机算法,我觉得还需要一个准确度。
    第一种方法由于不能做到真正意义上的随机,所以可以不考虑。
    第二种效率最高,但是也会出现重复,但是可以接受,因为更接近现实中的随机情况
    第三种效率最差,而且是全随机,效率差到爆,那就不多说了
    第四种如果做全随机的话,我觉得可以选用这种方式,也会出现效率问题。
     
  • 相关阅读:
    省市区选择器
    查找算法 分享1:顺序查找
    查找算法 分享2:折半(二分)查找
    编程实现对键盘输入的英文名句子进行加密。用加密方法为,当内容为英文字母时其在26字母中的其后三个字母代替该字母,若为其它字符时不变。
    查找算法 分享3:分块查找
    NSMutableDicitionary 的setValue和setObject的区别
    查找算法 分享4:哈希查找
    玩转博客园的5个小技巧
    WCF读书笔记安全:基础知识(身份验证、授权、传输安全)
    北京的房价
  • 原文地址:https://www.cnblogs.com/summer_shao/p/3633632.html
Copyright © 2011-2022 走看看