zoukankan      html  css  js  c++  java
  • 洗牌算法

    假定随机数生成器可视为“真随机”的话,我们随机洗乱一副牌,非常有趣的是一些很自然的思路,并不一定能够产生均匀分布。假定牌面存储在一个数组里。

     思路一:

      每次生成2个随机数索引,交换在这2个索引位置上的牌。伪码:

            static void ShufflePukes(int[] plist)
            {
                for (int i = 0; i < plist.Length; i++)
                {
                    int ia = rand() % plist.length;
                    int ib = rand() % plist.length;
                    swap(plist[ia], plist[ib]);
                }
            }

      用到了2次rand(),似乎是不必的。如果每次与第一个位置交换,直觉上是等价的。而且如果rand()是伪随机数列的话(几乎肯定是),相邻数之间关联性是很密切的,即伪随机数列按组分割的话不能看做是独立的,也即不是均匀分布的,印象中一本随机过程的书有提到,我只记得结论了。

      于是我们去掉多余的一个rand()。

    思路二:

            static void ShufflePukes(int[] plist)
            {
                for (int i = 0; i < plist.Length; i++)
                {
                    int ia = rand() % plist.length;
                    swap(plist[0], plist[ia]);
                }
            }

      看起来很漂亮。但实际上,无论思路一或思路二,牌面组合都不是均匀分布的。

      正确的算法叫做Fisher_Yates算法

      每次产生随机索引后,分别与第一位,第二位,第三位...的位置进行交换。伪码如下(为方便我从后往前索引):

    思路三:

    static void ShufflePukes(int[] plist)
            {
                for (int i = plist.Length - 1; i >= 0; i--)
                {
                    int ia = rand() % (i + 1);
                    swap(plist[i], plist[ia]);
                }
            }

      为何思路三是正确的?其差别相当细微,就是已经交换过的牌不再参与交换。

      这可以从排列组合来考虑。n张不同的牌其排列组合共有n!种组合。很自然的一种算法如果产生的组合不等于n!,自然就是不均匀的。

      对于思路三,其随机索引每次都不计入之前的位置,其组合为n!,所以是正确的。

      对于思路二,每次迭代,若除去自身,每次都有n-1种可能,其组合上界为(n-1)^n,必定有一些牌被抽中的特别多,所以是错误的。

  • 相关阅读:
    hud 3336 count the string (KMP)
    JSOI2008星球大战(并查集)
    HAOI2006受欢迎的牛
    十二月个人考核
    十二月个人考核
    CentOS配置Tomcat监听80端口,虚拟主机
    CentOS配置Tomcat监听80端口,虚拟主机
    如何调试一个无法重现的错误?
    如何调试一个无法重现的错误?
    Highcharts的自适应DOM或者DIV,JS方法实现
  • 原文地址:https://www.cnblogs.com/flytrace/p/5617014.html
Copyright © 2011-2022 走看看