打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
一个简单有效的洗牌算法

 

我们先看一下纸牌游戏。一幅纸牌由 52 张不同的纸牌组成,发牌时必须产生不重复的纸牌,而且洗牌过程必须公平,即 52! 中纸牌顺序应该等概率出现。很明显这种随机排列所产生的随机数必须均匀分布且独立。由此代码如下:

代码示例中的 Permute<T>(T[] array) 方法产生一个随机序列。第一个循环用 1, 2, 3, …, N 初始化该序列。第二个循环完成一次随机洗牌。在该循环的每次迭代中,我们讲 array[j] 的值于数组位置在区间[0, j)之间的某个元素相交换(也可能不交换)。

然而,我们要问的是 Permute<T>(T[] array) 方法产生的所有排列等概率吗?

若根据该算法,答案为是。因为一共有 N! 种可能的排列,而 Swap<T>(array, i, random.Next(0, i)); 这一句 N-1 次调用 Next 方法出现的不同结果也是 N! 种。但是,事实上答案为否,并非所有排列都是等概率。问题就出在可爱的伪随机数数生成器(Pseudo-Random Number Generator)上。PRNG 的随机性很大程度上限制了随机序列的随机性。所以,上述代码需要一个更好的伪随机数数生成器以使实际与理论更加相符。但是好的 PRNG 往往伴随着性能的下降,比如 Mt19937 随机化算法。
using System;
using System.Diagnostics;

namespace Lucifer.CSharp.Sample
{
    class Program {
        static void Main(string[] args)
        {
            //初始化牌局 int[] array = new int[52];
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = i;
            }

            //洗牌 Permute<int>(array);

            //验证牌局 for (int i = 0; i < array.Length; i++)
            {
                var value = array[i];
                for (int j = 0; j < array.Length; j++)
                {
                    if (j == i) continue;
                    Debug.Assert(array[j] != value);
                }
            }
        }

        static void Permute<T>(T[] array)
        {
            Random random = new Random();
            for (int i = 1; i < array.Length; i++)
            {
                Swap<T>(array, i, random.Next(0, i));
            }
        }

        static void Swap<T>(T[] array, int indexA, int indexB)
        {
            T temp = array[indexA];
            array[indexA] = array[indexB];
            array[indexB] = temp;
        }
    }
}

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
算法是如何影响程序编码方式的 - 基本排序算法
PostgreSQL数据聚类——kmeans
在顺序存储模式下的三种算法
排列
《数据结构》的魅力:关于Array和List的使用
常见排序算法的实现(一)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服