1つの面接問題が引き起こした乱数に関する思考(7)
3720 ワード
前の2つの記事でRandNが49以下の乱数を実現した以上,前の記事(http://blog.csdn.net/xzjxylophone/article/details/6853727)残された問題:任意のRand 7で任意の数の乱数を実現できるかどうか
この文章はどのように実現すべきかを議論します.
我々がRandNを計算する際に34=7*4+6を用いることに注意すると,101のように他の数を計算すると,101=2*49+3=2*pow(7,2)+0*pow(7,1)+3*pow(7,0)と考えられる.
これは進数(バイナリ、16進数など)を考えさせ、この考え方に基づいて次のコードを実現します.
まずNumAnalyzeクラス:このクラスの役割は、1つの数を私たちが与えた進数に基づいて表し、チェーンテーブルで結果を保存することです.
もしかするとテスト関数では、単純差確率ではなく分散を計算すべきかもしれません.この点はともかくとして.
これまでRand 7を例に挙げてきたが,このアルゴリズムでは進数のような解を用いている.もっと基本的なアルゴリズムを書くことができると思います.Rand 7を捨てて、任意のrand関数、例えばRand 10、rand 20、rand 16、rand 2などを使うことができます.
この文章はどのように実現すべきかを議論します.
我々がRandNを計算する際に34=7*4+6を用いることに注意すると,101のように他の数を計算すると,101=2*49+3=2*pow(7,2)+0*pow(7,1)+3*pow(7,0)と考えられる.
これは進数(バイナリ、16進数など)を考えさせ、この考え方に基づいて次のコードを実現します.
まずNumAnalyzeクラス:このクラスの役割は、1つの数を私たちが与えた進数に基づいて表し、チェーンテーブルで結果を保存することです.
public class NumAnalyse
{
private int _baseNum;//7:
private List<int> _list;//203
//
/// <summary>
///
/// </summary>
/// <param name="baseNum"> </param>
/// <param name="reaNum"> </param>
public NumAnalyse(int baseNum, int reaNum)
{
// , _rand7.Next() - 1;
// ,
reaNum--;//
_baseNum = baseNum;
ResetList();
//
while (reaNum >= _baseNum)
{
int remain = reaNum % baseNum;
reaNum = reaNum / baseNum;
_list.Add(remain);
}
if (reaNum != 0)
{
_list.Add(reaNum);
}
//
_list.Reverse();
}
public NumAnalyse(int baseNum)
{
_baseNum = baseNum;
ResetList();
_list.Add(0);
}
//
private void ResetList()
{
if(_list != null)
{
_list.Clear();
}
else
{
_list = new List<int>();
}
}
public int GetReaNum
{
get
{
int result = 0;
int i = 0;
for(i = 0; i < _list.Count; i++)
{
result += (_list[i]) * (int)Math.Pow(_baseNum, _list.Count - i - 1);
}
//+1 ,
return result + 1;
}
}
public List<int> NumList
{
get
{
return _list;
}
}
}
RandSNクラスの実装を見ています.public class RandSN : Rand7Base
{
private NumAnalyse na;//maxNum
private RandSN(int maxNum)
{
UpdateMaxNum(maxNum);
}
public void UpdateMaxNum(int maxNum)
{
_maxNum = maxNum;
na = new NumAnalyse(7, maxNum);
}
public static RandSN GetInstance(int maxNum)
{
if (rand == null || !(rand is RandSN))
{
rand = new RandSN(maxNum);
}
else if (rand is RandSN && rand.MaxNum != maxNum)
{
((RandSN)rand).UpdateMaxNum(maxNum);
}
return (RandSN)rand;
}
override public int Next()
{
int result = 0;
NumAnalyse tempNa = new NumAnalyse(7);
//
int num = _rand7.Next() - 1;
//
bool preEqual = true;
for (int i = 0; i < na.NumList.Count; i++)
{
// && preEqual
// 2
if (num > na.NumList[i] && preEqual)
{
result = Next();
break;
}
else
{
tempNa.NumList.Add(num);
// , ,
if(preEqual)
{
preEqual = (num == na.NumList[i]);
}
num = _rand7.Next() - 1;
}
}
// result
return result != 0 ? result : tempNa.GetReaNum;
}
}
テスト関数を追加するには、次の手順に従います.static void TestRandSN()
{
RandSN rand = RandSN.GetInstance(101);
TestRand(rand);
rand = RandSN.GetInstance(572);
TestRand(rand);
}
テスト結果: Rand101
: 24.0733554924083
Average:0.0099010;Max:0.0099738;Min:0.0098305
Max:0.0099738, Max-Average:0.0000728, Bits:0.7353800%
Min:0.0098305, Min-Average:-0.0000705, Bits:-0.7119500%
Rand572
: 30.9447089193863
Average:0.0017483;Max:0.0017927;Min:0.0017121
Max:0.0017927, Max-Average:0.0000444, Bits:2.5424400%
Min:0.0017121, Min-Average:-0.0000362, Bits:-2.0678800%
Rand 572についての偏差は2.54%程度だと思いますが、主な原因は、Rand 572にとってテストの総回数が小さすぎて、興味のある方はN倍拡大して試してみることができるからだと思いますもしかするとテスト関数では、単純差確率ではなく分散を計算すべきかもしれません.この点はともかくとして.
これまでRand 7を例に挙げてきたが,このアルゴリズムでは進数のような解を用いている.もっと基本的なアルゴリズムを書くことができると思います.Rand 7を捨てて、任意のrand関数、例えばRand 10、rand 20、rand 16、rand 2などを使うことができます.