代码改变世界

概率算法总结

2015-08-14 21:26  F_Code  阅读(395)  评论(0编辑  收藏  举报
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Leetcode.概率算法
{
    class Probability
    {
        static void Main(string[] args)
        {
            int[] arry = new int[100];
            int[] weight = new int[100];
            Random radom = new Random();
            for (int i = 0; i < 100; i++)
            {
                arry[i] = i;
                weight[i] = radom.Next(100);
            }

            while (true)
            {
                int result = WeightSamp(arry,weight);
                Console.WriteLine(result);
                Thread.Sleep(100);
            }
        }
        //问题1:假设一个随机数发生器rand7均匀产生1到7之间的随机整数,如何构造rand10,均匀产生1-10之间的随机整数?
        //解决方法一:1-7之间有4个奇数,3个偶数,我们扔掉一个奇数,比如7,这样剩余3个奇数,
        //3个偶数产生的概率相同——我们构造了一个0-1整数的均匀产生器,用它产生4个bit,对应表示整数0..15, 保留1..10就可以了
        public static int Arand10()
        {
            Random radom = new Random();
            int result = 0;
            int i = 0;//循环计数使用的
            int cur = radom.Next(8);
            while (cur != 7)
            {
                if (cur % 2 == 0)
                {
                    result = (result << 1) | 0;
                }
                else
                {
                    result = (result << 1) | 1;
                }
                cur = radom.Next(8);
                i++;
                if (i == 4)
                {
                    if (result <= 10)//result<10丢掉
                        break;
                    else
                    {
                        result = 0;
                        i = 0;
                    }
                }
            }
            return result;
        }

        //解决方法二:我们把1-7减去1,变为0-6。产生一个两位的七进制数,对应0-48,
        //我们把40-48扔掉(因为这只有9个数),其余按照个位数字分类,0-9对应我们要的1-10
        public static int Brand10()
        {
            Random radom = new Random();
            int cur1 = radom.Next(8);
            int cur2 = radom.Next(8);
            int result = cur1 * 7 + cur2;
            while (result > 40)
            {
                cur1 = radom.Next(8);
                cur2 = radom.Next(8);
                result = cur1 * 7 + cur2;
            }
            result = result % 10 + 1;
            return result;
        }

        //问题二:不均匀随机数发生器构造均匀
        //一个随机数发生器,不均匀,以概率p产生0,以(1-p)产生1, (0<p<1),构造一个均匀的随机数发生器 
        //分析:产生两次,(0,1)的概率与(1,0)的概率相同都是p * (1 – p)。
        //现在想要构造1-10之间的随机数,则可以使用4位,当随机数产生(1,0)的时候产生1,当随机数产生(0,1)的时候产生0即可


        //问题三:水库(Reservoir)采样
        //题目描述:流入若干个对象(整数),事先不知道个数。如何随机取出k个 (k小于总数)?
        //解决方法:用一个数组a保存k个数 a[0..k -1]
        //对于第i个元素(i = 1,2,…),如果i <= k: 则a[i -1]存放这个元素
        //否则:产生随机数x = rand() % i
        //若x < k,则用a[x]存放这个元素(扔掉之前的元素)
        //简单证明:假设目前已经流入n > k个元素
        //第i( i <= k)个元素被选中的可能性,1 * k / (k + 1) * (k + 1) / (k + 2) *…*(n -1) / n = k / n(第一次被选中的概率*以后不被换掉的概率)
        //第i (i > k)个元素被选中的可能性,k / i * i / (i + 1) * (i + 1) / (i + 2) *…* (n – 1) / n = k / n(第一次选中的概率*以后不被换掉的概率)
        public static int[] Reservoir(int k)
        {
            int[] arry = new int[k];
            Flow flow = new Flow();
            int i = 0;
            while (!flow.IsEnd())
            {
                int cur = flow.GetNext();
                if (i < k)
                {
                    arry[i] = cur;
                    i++;
                }
                else
                {
                    int random = (new Random().Next()) % i;
                    if (random < k)
                    {
                        arry[random] = cur;
                        i++;
                    }
                }
            }
            return arry;
        }

        //问题四:用数组a[0..n -1]随机产生一个全排列,算法导论上的题目,上面有证明
        public static int[] RndomShuffle(int n)
        {
            int[] arry = new int[n];
            for (int i = 0; i < n; i++)
            {
                arry[i] = i;
            }

            for (int i = 0; i < n; i++)
            {
                int index = new Random().Next() % (n - i) + i;
                Swap(arry, index, i);
            }
            return arry;
        }

        public static void Swap(int[] arry, int index1, int index2)
        {
            int temp = arry[index1];
            arry[index1] = arry[index2];
            arry[index2] = temp;
        }


        //问题5:带权采样问题
        //给定n种元素,再给定n个权值,按权值比例随机抽样一个元素。为了方便我们可以假设权值全是整数
        //比较好的解法:先按1/m的概率随机选择一种元素
        //再产生随机数根据权值决定能否选择这种元素,如果能则选取它并结束,否则返回(1)
        //Pi1 = Wi / Wtot  或Pi2 =Wi  / Wmax 无关紧要(正比于Wi)
        //证明:
        public static int WeightSamp(int[] arry, int[] weight)
        {
            int result = 0;
            int length = arry.Length;
            int max = weight.Max();//找出最大的权重
            Random radom = new Random();
            while (true)
            {
                int cur1 = radom.Next(length);//随机选取一个数
                int cur2 = radom.Next(max + 1);
                if (weight[cur1] <= cur2)
                {
                    result = cur1;
                    break;
                }
            }
            return result;
        }
    }

    //模拟数据流
    public class Flow
    {
        private int[] arry;
        private int cur = -1;

        public Flow()
        {
            arry = new int[100];
            for (int i = 0; i < 100; i++)
            {
                arry[i] = i;
            }
        }

        public int GetNext()
        {
            cur++;
            return arry[cur];
        }

        public bool IsEnd()
        {
            if (cur < 99)
                return false;
            else
                return true;
        }
    }
}

最后一个问题的证明如图: