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;
}
}
}
最后一个问题的证明如图: