既然在前2篇文章中我们已经实现了RandN小于等于49的随机数,上一篇文章(http://blog.csdn.net/xzjxylophone/article/details/6853727)留下的问题:可否用任意Rand7实现任意一个数的随机数
这篇文章就来讨论该如何实现。
注意到我们计算RandN的时候用到34=7*4 + 6,那么计算其他的数,如101,可以想到101=2 * 49 + 3 = 2*pow(7,2) + 0 * pow(7, 1) + 3 * pow(7,0)
这个 让我们想到进制(二进制,十六进制等等)了,基于这个思路我们实现如下的代码:
首先NumAnalysze类:该类的作用是把一个数表示成一个基于我们给定的进制,用一个链表保存其结果
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; } } }
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%
或许在测试函数,应该计算方差而不是简单差概率。在此这一点先不管了。
到现在为止,我们一直是以Rand7为例子的,在这种算法中,是用到类似于进制的去解决的。我想应该能写一个更基本的算法,抛弃Rand7,可以用任意一个rand函数,如Rand10,rand20,rand16,rand2等等。