在上一篇文章(http://blog.csdn.net/xzjxylophone/article/details/6853589)的最后我们提出如何用Rand7实现比较大的数字,例如Rand35,Rand34等等
这篇文章中我们来尝试新建一个类:RandN表示是任意小于49的一个随机数
首先看Rand10_3,我们可以这样思考:10=7*1 + 3,因此我们再第二步使用了如下的语句:
//num=4,5,6,7的时候重新计算 if(num > 3) { result = Next(); }
因此我们可以这样去计算RandN(以34为例子):
34=4*7 + 6,
先得到一个Rand7的数字:int remain = rand7.Next()
然后在得到数字:int ceiling = _rand7.Next();//ceiling
如果ceiling 大于5,即ceiling =6,7
重新计算ceiling = _rand7.Next();//ceiling
否则
result=remain + 7 * ceiling;
在这里还要判断条件,就是remain这个参数:
如果remain大于6的话,那么就重新计算。
以下是RandN的实现:
//支持2-49,包括2和49 public class RandN : Rand7Base { protected int _remain; //剩余数,范围是0-6 protected int _ceiling; //大于的最小数 private RandN(int maxNum) { UpdateMaxNum(maxNum); } public void UpdateMaxNum(int maxNum) { _maxNum = maxNum; _remain = _maxNum % 7; decimal chushu = (decimal)_maxNum / 7; _ceiling = (int)Math.Ceiling(chushu); } public static RandN GetInstance(int maxNum) { if (rand == null || !(rand is RandN)) { rand = new RandN(maxNum); } //最大随机数不一样 else if(rand is RandN && rand.MaxNum != maxNum) { ((RandN)rand).UpdateMaxNum(maxNum); } return (RandN)rand; } override public int Next() { int result = 0; int remain = _rand7.Next(); bool loop = false; int ceiling = 0; while (!loop) { ceiling = _rand7.Next();//ceiling //重新计算 if (ceiling > _ceiling) continue; //n-1保证result是有可能小于7 result = remain + (ceiling - 1) * 7; //以下的代码可以被替代: if (ceiling == _ceiling && _remain != 0)//考虑整除的情况 { if (remain > _remain)//剩余数超过最大的剩余数就重新计算 { result = Next(); } } //可以被替代成: //if(result > _maxNum) //{ // result = Next(); //} loop = true; } return result; } }
添加测试代码:
static void TestRandN() { RandN rand = RandN.GetInstance(34); TestRand(rand); rand = RandN.GetInstance(35); TestRand(rand); rand = RandN.GetInstance(6); TestRand(rand); }测试结果:
计算Rand34的概率如下 产生1的概率是:0.0294017 产生2的概率是:0.0294027 产生3的概率是:0.0293998 产生4的概率是:0.0295281 产生5的概率是:0.0294838 产生6的概率是:0.0294388 产生7的概率是:0.0293086 产生8的概率是:0.0294455 产生9的概率是:0.0293082 产生10的概率是:0.0294202 产生11的概率是:0.0294208 产生12的概率是:0.0293530 产生13的概率是:0.0294430 产生14的概率是:0.0293526 产生15的概率是:0.0294120 产生16的概率是:0.0293657 产生17的概率是:0.0294057 产生18的概率是:0.0293856 产生19的概率是:0.0293732 产生20的概率是:0.0294238 产生21的概率是:0.0293502 产生22的概率是:0.0294515 产生23的概率是:0.0294136 产生24的概率是:0.0294762 产生25的概率是:0.0294245 产生26的概率是:0.0294585 产生27的概率是:0.0294599 产生28的概率是:0.0293607 产生29的概率是:0.0294267 产生30的概率是:0.0294665 产生31的概率是:0.0293348 产生32的概率是:0.0294346 产生33的概率是:0.0293828 产生34的概率是:0.0294867 耗时: 2.78373975842868 秒 Average:0.0294118;Max:0.0295281;Min:0.0293082 Max:0.0295281, Max-Average:0.0001163, Bits:0.3955400% Min:0.0293082, Min-Average:-0.0001036, Bits:-0.3521200% 计算Rand35的概率如下 产生1的概率是:0.0286397 产生2的概率是:0.0285668 产生3的概率是:0.0286318 产生4的概率是:0.0285731 产生5的概率是:0.0285246 产生6的概率是:0.0285689 产生7的概率是:0.0284451 产生8的概率是:0.0287106 产生9的概率是:0.0285398 产生10的概率是:0.0284319 产生11的概率是:0.0285941 产生12的概率是:0.0285509 产生13的概率是:0.0286407 产生14的概率是:0.0286336 产生15的概率是:0.0285854 产生16的概率是:0.0286022 产生17的概率是:0.0286192 产生18的概率是:0.0285978 产生19的概率是:0.0286203 产生20的概率是:0.0286015 产生21的概率是:0.0285558 产生22的概率是:0.0285200 产生23的概率是:0.0285130 产生24的概率是:0.0285547 产生25的概率是:0.0285327 产生26的概率是:0.0285055 产生27的概率是:0.0285340 产生28的概率是:0.0285735 产生29的概率是:0.0285664 产生30的概率是:0.0285509 产生31的概率是:0.0286140 产生32的概率是:0.0285983 产生33的概率是:0.0284317 产生34的概率是:0.0286959 产生35的概率是:0.0285756 耗时: 3.08980588360172 秒 Average:0.0285714;Max:0.0287106;Min:0.0284317 Max:0.0287106, Max-Average:0.0001392, Bits:0.4871000% Min:0.0284317, Min-Average:-0.0001397, Bits:-0.4890500% 计算Rand6的概率如下 产生1的概率是:0.1666733 产生2的概率是:0.1664507 产生3的概率是:0.1666722 产生4的概率是:0.1667893 产生5的概率是:0.1667764 产生6的概率是:0.1666381 耗时: 9.59583967306935 秒 Average:0.1666667;Max:0.1667893;Min:0.1664507 Max:0.1667893, Max-Average:0.0001226, Bits:0.0735800% Min:0.1664507, Min-Average:-0.0002160, Bits:-0.1295800%
观察代码可以发现当maxNum为6的时候,第二次ceiling=rand7.Next() 此时必须是1,否则是一直重新计算。所以是否可以用一个办法来优化是何时来重新计算ceiling。
在C#中可以用委托,或者C++中的函数指针。下一篇文章我们来优化这个算法。