一道面試題引發的有關隨機數的思考(3)

在上一篇文章(http://blog.csdn.net/xzjxylophone/article/details/6835332)中,我們主要完成了這個C#工程的重構和Rand10,Rand12的另一種實現。

這次我們來實現如果用Rand7來產生一個Rand11.

按照以前的思路計算Rand10的時候是分成2個集合(1,3,5,7,9)(2,4,6,810)

那麼我們是否可以把11也分成2個集合(1,3,5,7,9,11)和(2,4,6,8,10);

按照10的思路,在Rand10.Next()函數中應該把代碼:

if (n > 4)
	num *= 2;
else 
	num = num * 2 - 1;

在Rand11.Next()修改成:

if (n > 4)
	num = 2*num-1;//1,3,5,7,9,11
else 
	num = 2 * Rand5;//此處的Rand5表示隨即產生一個1-5的一個數

爲了完成Rand11,那必須先實現Rand5.

Rand5

public class Rand5 : Rand7Base
{
	private Rand5()
	{
		_maxNum = 5;
	}
	public static Rand5 GetInstance()
	{
		if (rand == null || !(rand is Rand5))
		{
			rand = new Rand5();
		}
		return (Rand5)rand;
	}
	//獲得隨機數   
	override public int Next()
	{
		int num;
		//均勻產生1、 2 、3、4、5   
		while (true)
		{
			num = _rand7.Next();
			if (num <= 5)
				return num;
		}
	}  
}
按照前述的思路完成Rand11:

public class Rand11 : Rand7Base
{
	private Rand11()
	{
		_maxNum = 11;
	}
	public static Rand11 GetInstance()
	{
		if (rand == null || !(rand is Rand11))
		{
			rand = new Rand11();
		}
		return (Rand11)rand;
	}
	//獲得隨機數   
	override public int Next()
	{
		
		int num;
		
		label:
		while (true) //代碼塊1
		{
			num = _rand7.Next();
			if (num <= 6)
				break;
		}
		//1<=num<=6
		//1.3.5.7.9.11  num*2-1
		//2.4.6.8.10    num*2

		while (true)
		{
			int n = _rand7.Next();
			if (n == 1)
				continue;
			if (n > 4)
				num = num * 2 - 1; // 代碼塊2
			else
			{
				num = 2 * Rand5.GetInstance().Next();//隨即獲取1-5,保證num爲(2,4,6,8,10)  //代碼塊3
			} 
			break;
		}
		return num;
	}   
}

添加測試函數:

static void TestRand5()
{
	Rand5 rand = Rand5.GetInstance();
	TestRand(rand);
}
static void TestRand11()
{
	Rand11 rand = Rand11.GetInstance();
	TestRand(rand);
}
測試Rand5和Rand11得到的測試結果:

計算Rand5的概率如下
產生1的概率是:0.1998812
產生2的概率是:0.2001405
產生3的概率是:0.1998788
產生4的概率是:0.2001452
產生5的概率是:0.1999543
耗時: 1.35574716655683 秒
Average:0.2000000;Max:0.2001452;Min:0.1998788
Max:0.2001452, Max-Average:0.0001452, Bits:0.0726000%
Min:0.1998788, Min-Average:-0.0001212, Bits:-0.0606000%
計算Rand11的概率如下
產生1的概率是:0.0833986
產生2的概率是:0.0998455
產生3的概率是:0.0832477
產生4的概率是:0.1000255
產生5的概率是:0.0832922
產生6的概率是:0.1000058
產生7的概率是:0.0833212
產生8的概率是:0.0999634
產生9的概率是:0.0834429
產生10的概率是:0.1001360
產生11的概率是:0.0833212
耗時: 2.95999916630916 秒
Average:0.0909091;Max:0.1001360;Min:0.0832477
Max:0.1001360, Max-Average:0.0092269, Bits:10.1496000%
Min:0.0832477, Min-Average:-0.0076614, Bits:-8.4275300%

Rand5是正確的。Rand11的誤差範圍居然在-8%----10%這裏,說明Rand11是有問題。

重新觀察Rand10和Rand11.在Rand10中雖然分配了2個集合,但是每個集合的元素都是相同的,但是在Rand11中2個集合,一個集合是6個元素,一個集合是5個元素,問題肯定就出現在這裏。

注意到程序運行到代碼塊2和代碼塊3的概率都是一樣的都爲1/2

只不過概率1/2被6個元素給分了(1,3,5,7,9,11),而剩下的1/2被剩餘的5個元素分了(2,4,6,8,10)

所以在輸出的結果中可以看到P(1),P(3),P(5),P(7),P(9),P(11)的概率都約等於1/12而其他數的概率約等於1/10。


現在可以這樣來考慮,爲了讓1-11這11個數都是等概率的得到,可以把概率1看成一塊蛋糕分給11個人,這個該如何分了?

可以這樣來分:先平均分成12份,每個人拿一份,最後剩下一份,然後把剩下的那一份再平均分12份,依次類推。

現在我們用數學知識來計算一個人(A)到底分配了多少蛋糕:

步驟1:第一次分12份,分給11人後,A得到了1/12,還剩下1/12

步驟2:把步驟1剩下的一份在分成12份,分給11人後,  A得到了1/12   *   1/12,剩餘1/12 * 1/12

步驟3:把步驟2剩餘的一份在分成12份,分給11人後, A得到 1/12 * pow(1/12,  2)                    //pow(a,b)表示a的b次方

依次類推

可的A得到的蛋糕:P(A)=1/12 + 1/12 * 1/ 12 + 1/12 * pow(1/12,2) + ..... + 1/ 12 * pow(1/12, n)= (1/12) * (1- pow(1/12, 2)) / (1 - 1/12)  //等比數列計算公式 = (1/12 ) / (11/12) = 1 / 11

結果爲1/11是我們想要的結果,那麼可以根據依然的想法來編寫程序:

Rand11_2

public class Rand11_2 : Rand7Base
{
	private Rand11_2()
	{
		_maxNum = 11;
	}
	public static Rand11_2 GetInstance()
	{
		if (rand == null || !(rand is Rand11_2))
		{
			rand = new Rand11_2();
		}
		return (Rand11_2)rand;
	}
	//獲得隨機數   
	override public int Next()
	{
		int num;
		while (true)
		{
			num = _rand7.Next();
			if (num <= 6)
				break;
		}
		while (true)
		{
			int n = _rand7.Next();
			if (n == 1)
				continue;
			if (n > 4)
				num = num * 2 - 1;//1,3,5,7,9,11
			else
			{
				//當num = 6的時候表示需要繼續的劃分蛋糕
				if (num == 6)
				{
					num = Next();
				}
				else
				{
					num = 2 * num;
				}
				
			}
			break;
		}
		return num;
	}   
}

按照以前的方法添加測試代碼並測試

此次測試Rand11和Rand11_2,這樣可以進行比較:

計算Rand11的概率如下
產生1的概率是:0.0831913
產生2的概率是:0.1001187
產生3的概率是:0.0833342
產生4的概率是:0.0999278
產生5的概率是:0.0834714
產生6的概率是:0.1000888
產生7的概率是:0.0833103
產生8的概率是:0.1001032
產生9的概率是:0.0832276
產生10的概率是:0.1000561
產生11的概率是:0.0831706
耗時: 3.28849131413559 秒
Average:0.0909091;Max:0.1001187;Min:0.0831706
Max:0.1001187, Max-Average:0.0092096, Bits:10.1305700%
Min:0.0831706, Min-Average:-0.0077385, Bits:-8.5123400%
計算Rand11_2的概率如下
產生1的概率是:0.0908736
產生2的概率是:0.0908268
產生3的概率是:0.0908969
產生4的概率是:0.0909283
產生5的概率是:0.0909252
產生6的概率是:0.0908479
產生7的概率是:0.0908451
產生8的概率是:0.0909375
產生9的概率是:0.0909681
產生10的概率是:0.0910002
產生11的概率是:0.0909504
耗時: 2.48036752188162 秒
Average:0.0909091;Max:0.0910002;Min:0.0908268
Max:0.0910002, Max-Average:0.0000911, Bits:0.1002200%
Min:0.0908268, Min-Average:-0.0000823, Bits:-0.0905200%

這樣就出現我們所希望的結果了,也就是說我們的假設是正確的了。

既然Rand11_2可以用這樣的方法去解決了,那麼Rand10或者Rand12能不能用類似的思路去解決這個問題了?

下一篇我們來嘗試用這種思路實現Rand10.


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章