• 随机数


    通常我们使用的随机函数是返回一个32位的取值范围为[0,0x7FFFFFFF) /*2147483647*/的随机数。

    因为目前我们所使用的随机数生成都是伪随机数(伪随机数是以相同的概率从一组有限的数字中选取的,所选数字并不具有完全的随机性。),所以会造成分布不均的情况。
    但是,如果生成的随机数能均匀分布的话(0~0x7FFFFFFF出现的概率一样),问题就解决了吗?

    先看下面代码:

        class Random
        {
            int index = 0;
            int []array;
            public Random(int len)
            {
                array = new int[len];
                InitArray();
            }
            void InitArray()
            {
                for(int i=0;i<array.Length;++i)
                {
                    array[i] = i;
                }
            }
            
            public int Next()
            {
                int result = array[index++];
                if(index >= array.Length)
                {
                    index = 0;
                }
                return result;
            }
        }
        
        class Program
        {
            public static void Main(string[] args)
            {
                int alen = 12;
                int []arr = new int[alen];
            
                int rlen = 16;            
                Random r = new Random(rlen);
                int cnt = rlen * 10000; // int cnt = alen * 10000;
                
                for(int i=0;i<cnt;++i)
                {
                    arr[r.Next() % alen] ++;
                }
                PrintArray(arr);
                Console.ReadKey();
            }
            
            public static void PrintArray<T>(T [] arr)
            {
                for(int i=0;i< arr.Length;++i)
                {
                    Console.WriteLine(i+":"+arr[i]);
                }
            }
        }

    代码中的Random类是自定义的“随机类”,初始化传入的参数是指该类能保证[0,value) 的均匀分布(好绕口)。例如初始化传入16,则在调用Random::Next()时保证随机出现0~15的概率是均等的。

    在Main函数中,Random r = new Random(16),但是真实测试数据只需要12个。以下是输出结果:

    0:20000
    1:20000
    2:20000
    3:20000
    4:10000
    5:10000
    6:10000
    7:10000
    8:10000
    9:10000
    10:10000
    11:10000

    “什么情况?为何0~3的次数出现了这么多?!肯定是程序有问题!”
    仔细发现Random函数的Next其实就是每次按顺序地返回,根本没有随机效果!
    好吧,我们改代码试试:

        class Util
        {
            public static void Swap<T>(ref T a, ref T b)
            {
                T t = a;
                a = b;
                b = t;
            }
            public static void Shuffle<T>(T []arr, System.Random r)
            {
                for(int i=arr.Length-1;i>0;--i)
                {
                    Swap(ref arr[i], ref arr[r.Next()%arr.Length]);
                }
            }
        }
            
        class Random
        {
            System.Random r;
            int index = 0;
            int []array;
            public Random(int len)
            {
                r = new System.Random();
                array = new int[len];
                InitArray();
            }
            
            void InitArray()
            {
                for(int i=0;i<array.Length;++i)
                {
                    array[i] = i;
                }
                Util.Shuffle(array,r);
            }
            
            public int Next()
            {
                int result = array[index++];
                if(index >= array.Length)
                {
                    index = 0;
                    Util.Shuffle(array,r);
                }
                //Console.Write(result+" ");
                return result;
            }
        }
        
        class Program
        {
            public static void Main(string[] args)
            {
                int alen = 12;
                int []arr = new int[alen];
            
                int rlen = 16;            
                Random r = new Random(rlen);
                int cnt = alen * 10000; //这里改成12,看上去"更公平点"
    //            int cnt = rlen * 10000;
                
                //System.Random rr = new System.Random();
                //Console.WriteLine(rr.Next());
                for(int i=0;i<cnt;++i)
                {    
                    arr[r.Next() % alen] ++;
                }
                PrintArray(arr);
                Console.ReadKey();
            }
            
            public static void PrintArray<T>(T [] arr)
            {
                for(int i=0;i< arr.Length;++i)
                {
                    Console.WriteLine(i+":"+arr[i]);
                }
            }
        }

    在来看看测试结果:

    0:15000
    1:15000
    2:15000
    3:15000
    4:7500
    5:7500
    6:7500
    7:7500
    8:7500
    9:7500
    10:7500
    11:7500

    可能你还会找茬说Shuffle函数中所调用的随机数也是不能均匀分布,但是我保证了自己的Random类所产生的数是均匀分布就行了。
    其实还有一个更简单的例子,就是16 与12调换,也就是说 你随机生成只能是[0,12),而[12,16)的范围永远取不到!(请自行脑部 int Random::Next() 和想要获取long.MaxValue的取值范围)。

    绕了这么久,其实我想说明的是:就算 int Random::Next()能保证生成的随机数是均匀分布,但都不代表你所需要的取值范围能均匀分布(无论取值范围比它大还是小)。

    如果想要获的自己所需的取值范围的而且又能体现随机分布的办法有两种:

    一是我上面代码的方法(Random(12)就可以了),这种也不能完美解决,因为实质上打乱数组时也是伪随机数,还有就是如果取值范围太大,根本就是浪费内存的做法。

    二是真正地实现一个无穷大的取值范围的随机数。 


    参考资料:

    Random 类: http://msdn.microsoft.com/zh-cn/library/vstudio/system.random.aspx

  • 相关阅读:
    20140322 卡迪夫城VS利物浦,拔出重剑,有惊无险
    20140316 曼联VS利物浦,罗杰斯的小九九,当4312对上4231
    『看球笔记』20140217 红军足总杯遭枪手2-1淘汰 ,胜负手在哪?
    『看球笔记』20140208利物浦VS阿森纳,十字重剑与蜜蜂飞舞
    『看球笔记』20131230切尔西vs利物浦,赛后复盘聊聊球
    Full scan vs index 执行计划的实验
    『看球笔记』利物浦0:1南安普顿,罗杰斯,你的牌技太差了
    无题
    开源啦!安卓版起床资讯闹钟,丰富大家早晨起床的android应用,评论过十就开了。
    开源啦!Android版语音天气闹钟
  • 原文地址:https://www.cnblogs.com/godzza/p/3001790.html
Copyright © 2020-2023  润新知