zoukankan      html  css  js  c++  java
  • 转:浅谈洗牌算法(面试题)

           很多人都有耳闻过洗牌算法,时常会在面试中碰到,我们下面来定义一下这个问题。

           所谓洗牌算法,就是给你一个1到n的序列,让你随机打乱,保证每个数出现在任意一个位置的概率相同,也就是说在n!个的排列中,每一个排列出现的概率相同。

    最朴素的做法

           对于这个问题我们从最朴素的解法谈起。每次随机选出一个没有被选过的数放到一个队列中,如果随机出来的数已经被选过,那么继续随机直到遇到一个没有被选过的数放入到队列中;重复这样子操作直到所有的数都被选择出来。

           我们看看这样子作为什么是对的。首先选第一个数的时候有n个数可选,第2个数的时候有(n-1)个数可选,-----这样子看来我们的排列有n*(n-1)*(n-1)*-----*1种,也就是我们最后选出来的排列是n!的排列中的任意一个,这样子显然是符合随机洗牌的要求的。

           但是我们反观该方法的时间复杂度,由于我们每次随机选出一个数都有可能是之前选过的数,需要进行再次随机,因此对选出一个数的随机平均情况下需要随机O(n)次,因此该方法的时间复杂度是O(n^2)的。而且另外还要记一个队列,甚至需要记一个数是否被选出来过,因此该最朴素的做法的时间和空间复杂度都不是最好的,我们需要考虑更好的办法。

    经典的洗牌算法

           很多面试官需要你会的洗牌算法当然是经典的洗牌算法,也就是我们考虑如何对上面的洗牌算法进行优化。

           我们考虑,当一个数被选之后,我们是没有必要在下一次随机的时候再考虑它的,因此,我们每次只从可选的数的集合中进行随机,也就不用考虑是否会碰到已经选过的数了,这样子直接将算法的复杂度降了一个数量级。

    void MySwap(int &x, int &y)
    {
        int temp = x;
        x = y;
        y = temp;
    }
    
    void Shuffle(int n)
    {
        for(int i=n-1; i>=1; i--)
        {
            MySwap(num[i], num[rand()%(i+1)]);
        }
    }

    参照上面的代码,我们可以看到,我们每次都是随机的从没有选的数中选择一个,该洗牌算法的时间复杂度为O(n),空间复杂度为O(0)。正确性如上面一样证明。

  • 相关阅读:
    微信小程序如何获取openid
    js经典试题之常用的方法
    js经典试题之运算符的优先级
    js如何使浏览器允许脚本异步加载
    es6从零学习(五):Module的语法
    es6从零学习(四):Class的继承
    js如何处理字符串中带有↵字符
    Zuul中聚合Swagger的坑
    阿里Sentinel支持Spring Cloud Gateway啦
    Spring Boot中的Mongodb多数据源扩展
  • 原文地址:https://www.cnblogs.com/lakeone/p/5934094.html
Copyright © 2011-2022 走看看