实际中常用的一个随机数产生器
2010-04-21 13:41
302 查看
这是刚做完的一小段代码,经测试已经无误,呵呵,这里share给大家看看,有朋友觉得有用,可以拿去用。
这个程序解决的问题如下:
已知一件事物有几种状态,每种状态出现的概率不一样,要求做一个随机数产生器,返回状态值,要求状态值出现的规律,符合输入的概率。
这是小弟上午问我的问题,我们正在做一个工业测试模型,实际的例子是,根据实际情况,某种设备返回的状态概率符合下表:
要求写段代码,模拟设备的上述行为。
我下午上班想了一下,花了半个小时为他写了一个随机数产生器,经测试,0bug,呵呵。他现在正在用。
Code:
#define CTonyRandomArea_TOKEN_MAX 100 //最大类型数
#define CTonyRandomArea_TOKEN_AREA_MAX 10000 //类型数组单元数,精确到小数点后两位
//输入最大100个元素的数组,每个数组表示每类占有的百分比,内部自带百分比调整。
//即如果外部输入的数字之和不是整数100,内部会根据百分比,自动调整其比例,使总和=100.0
//然后内部建立10000个单元的类型数组,根据传入的每种类型的比例,在类型数组中批量填充对应的类型值
//总之,类型数组中每种类型的数量,占据的比例正好是输入的百分比
//最后,在0~10000中取随机,然后在对应的类型数组单元中取类型值,即为返回的类型
class CTonyRandomArea
{
public:
CTonyRandomArea(double* pTokenPercentArray,char cTokenCount)
{
m_nTokenCount=cTokenCount;
if(CTonyRandomArea_TOKEN_MAX<m_nTokenCount)
m_nTokenCount=CTonyRandomArea_TOKEN_MAX;
int i=0;
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]=*(pTokenPercentArray+i);
}
//动态调整内部的值
//有时候试验人员,测得几个状态出现的数字,可能懒得再计算成百分比
//程序帮忙自动计算
double dNumberCount=0;
for(i=0;i<m_nTokenCount;i++)
{
dNumberCount+=m_dTokenPercentArray[i];
}
if(100.0!=dNumberCount)
{
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]/=dNumberCount;
m_dTokenPercentArray[i]*=100;
}
}
//以小数点后两位精度,开始计算在10000个总单元中,每种类型对应的数量。
for(i=0;i<m_nTokenCount;i++)
{
m_sTokenPercentArray[i]=(short)(m_dTokenPercentArray[i]*100);
}
//按比例填充类型数组
int j=0;
int nFillMin=0;
int nFillMax=0;
for(i=0;i<m_nTokenCount;i++)
{
m_cTokenPercentArrayAreaUp[i]=-1;
}
for(i=0;i<m_nTokenCount;i++)
{
nFillMax=nFillMin+m_sTokenPercentArray[i];
for(j=nFillMin;j<nFillMax;j++)
{
m_cTokenPercentArrayAreaUp[j]=i;
}
nFillMin=nFillMax;
}
// m_cTokenPercentArrayAreaUp[CTonyRandomArea_TOKEN_AREA_MAX-1]=i-1;
}
~CTonyRandomArea(){}
void PrintfInfo(void)
{
int i=0;
double dNumberCount=0;
for(i=0;i<m_nTokenCount;i++)
{
dNumberCount+=m_dTokenPercentArray[i];
printf("%d = %f/n",i,m_dTokenPercentArray[i]);
}
printf("All = %f/n",dNumberCount);
//打印10000个单元的类型分布,看看排得对不对
//这段打印起来太长,一般隐掉,需要了再打印
// int nOutMax=400;
// int nInMax=25; //二者相乘为10000
// int j=0;
// for(i=0;i<nOutMax;i++)
// {
// printf("%05d - ",i*nInMax);
// for(j=0;j<nInMax;j++)
// {
// printf("%d ",m_cTokenPercentArrayAreaUp[i*nInMax+j]);
// }
// printf("/n");
// }
}
//取类型数组对应单元的值
char GetType(int nTypeIndex) //输入参数0~10000
{
if(10000<=nTypeIndex) nTypeIndex=9999;
if(0>nTypeIndex) nTypeIndex=0;
return m_cTokenPercentArrayAreaUp[nTypeIndex];
}
//真实的工作函数,利用输入的概率来产生随机type
char GetRandomType(void)
{
return GetType(GetRandomBetween(0,10000));
}
private:
double m_dTokenPercentArray[CTonyRandomArea_TOKEN_MAX]; //比例数组
short m_sTokenPercentArray[CTonyRandomArea_TOKEN_MAX]; //比例数组,短整型,1~10000,相当于精确到小数点后两位
char m_nTokenCount;
char m_cTokenPercentArrayAreaUp[CTonyRandomArea_TOKEN_AREA_MAX]; //类型数组
};
//这是测试代码
void TestCTonyRandomArea(void)
{
double dTokenPercentArray[100];
dTokenPercentArray[0]=12.0;
dTokenPercentArray[1]=40.0;
dTokenPercentArray[2]=40.0;
dTokenPercentArray[3]=7.0;
dTokenPercentArray[4]=1.0;
CTonyRandomArea Area1(dTokenPercentArray,5);
Area1.PrintfInfo();
int i=0;
for(i=0;i<20;i++)
{
printf("RandType = %d/n",Area1.GetRandomType());
}
}
其实这个原理很简单:
1、我先从外部导入一个比例列表,在100以内的数组单元,每个单元里面放置一个double值,相当于对应类别的比例。这样,我预设最大有100种状态,具体本次试验有多少种状态,即100个状态比例数组多少个单元是有效的,看构造函数的第二个参数,就是这个参数输入的。
2、这里面我做了点人性化考虑,因为很多时候,我们测试的设备状态是直接的采样值,即每种状态出现了多少次,懒得计算成百分比,因此,我内部自动帮他重新计算一遍百分比。这样用起来很方便。
3、我根据各种类型的比例,内部准备一个10000个单元的大数组,我根据每种状态的比例,在这个数组中填充足够的状态数,这样,构建了一个比例分配表。这实际上是把计算精度放大到小数点后两位,即99.99%这种精度
4、我真正提供随机数的函数,是在0~10000中取值,即随机命中比例分配表的某个单元,这个单元取出来是哪种状态,就返回哪种状态。由于比例分配表决定了各种状态被命中的比例,因此,我返回值是符合出现比例的。
5、最后我给了一个测试函数TestCTonyRandomArea,这是我团队的规矩,任何人写一个模块,必须同时提供相应的白盒测试函数,并将测试结果展示给使用者看,作为验收标准,即“你必须自己证明自己的工作是有效的,并接受检验”,我这个leader也不能例外。
6、PrintfInfo函数也是我团队的规矩,位于底层的类,有责任提供一个PrintInfo函数,供调用者随时查阅你的内部数据,“把你的数据暴露给大家看,想出来混江湖,就不怕裸奔被人看!”,呵呵《0bug-C/C++商用工程之道》里面很多类都有这个函数的。
嗯,中间有个GetRandomBetween这个函数,就是《0bug-C/C++商用工程之道》这本书P199页的源代码,这里我也给一份Copy,另外,其工作原理,有兴趣的读者可以看看书中的描述。
Code:
inline int _GetNot0(void)
{
int nRet=rand();
if(!nRet) nRet++;
return nRet;
}
inline int GetRandomBetween(int nBegin,int nEnd)
{
int n=_GetNot0();
int nBetween=0;
if(0>nBegin) nBegin=-nBegin;
if(0>nEnd) nEnd=-nEnd;
if(nBegin>nEnd)
{
nBetween=nEnd;
nEnd=nBegin;
nBegin=nBetween;
}
else if(nBegin==nEnd)
nEnd=nBegin+10;
nBetween=nEnd-nBegin;
n=n%nBetween;
n+=nBegin;
return n;
}
上述代码是我匆忙写的,属于测试用代码,不完全符合0bug一书里面的C/C++无错化程序设计原则,各位读者请注意哈。
不过,虽然是测试代码,但是带了很多工程型代码的影子,大家有兴趣可以看看。
另外,上述代码没有做锁封装,但是,仍然是多线程安全的。大家有注意到没有?
因为其工作原理是查表法,所有的表构造时一次生成,以后仅仅是纯读,请《0bug-C/C++商用工程之道》的读者注意2.3.6节,P50页的论述,“用锁的最高境界--不用”,这里符合第1条特例,“针对一个资源的所有操作都是读的时候,可以不加锁”。我这段代码可以算作实例了。各位读者可以参考一下。
好吧,就这么多,大家有兴趣可以看看。
嗯,有人可能说,这里的随机数产生器没有使用srand初始化,记住,我在用我自己的工程库,也就是《0bug-C/C++商用工程之道》的工程库,工程库的init动作里面已经做过这种动作了。
代码是VS2008下测试的,不过,我的理解,应该是跨平台的。
上述代码在很多游戏开发中可以投入实用的。
比如说,某个NPC哨兵,他可能在某个时刻,看前后左右,或者抽烟,或者睡觉,或者和另一个哨兵聊天,这时候,可以用这个随机数产生器,根据预设的每种动作的概率,权重,随时求出他的行为种类,并予以展示。
再或者,暗黑里面,我们使用暗金的装备,每次攻击,有百分之多少的概率出现压碎性打击,有多少概率出现冰冻属性,等等,也可以用这个随机数产生器来求。
大家慢慢想吧,呵呵。
嗯,这里网友发现一处bug,我已经修改了,请昨天看过的朋友注意:
Code:
if(100.0!=dNumberCount)
{
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]/=dNumberCount;
m_dTokenPercentArray[i]*=100; //这里少乘了个100,百分比动态调整失效,因此,我加上了这一句。
}
}
这段代码出来后,一些网友表示看不懂我的原意,我们在CSDN博客有一些问答,我觉得对大家理解本程序的设计思路有帮助,因此,整理了一下,摘录在这里:
网友问:if(100.0!=dNumberCount) 浮点数直接用等于作比较是不正确的
我答:通常的做法是if((100.0-dNumberCount)<0.00000001),我知道的,不过,我为什么这么写,你看得懂吗?
网友问:不懂,老师教教吧,谢谢
我答:这里主要的目的是否定,是为了验证所有输入的double数加起来不是100.0,然后内部重新计算一次。由于外部人员输入,通常都不是正好100.0,因此,这里使用否定的严厉校验,即只要不是绝对==100.0,内部就重新计算。看好了,我是否定严厉,不是肯定严厉,因此不用教科书做法。
网友问:同意这样直接比较在此处也不会产生错误,我还是有如下观点: 1. 这样严厉的否定可能会拒绝一些本可以接受的输入,当然概率比较小,而且即使拒绝了也顶多是多计算一下,不会有bug 2. 即使是通过代码中的“自动调整其比例“的计算以后,仍然有可能会出发您的”否定严厉“ 所以我认为还是不应该用直接比较。 3. (100.0-dNumberCount)<0.00000001 这样的比较还是不合适的,一是要用绝对值,当然这里可能是您忘记写了;二是0.00000001的取定要推敲,用float.h中提供的常数宏更好
我答:嗯,你说的有理,我下回注意,呵呵。不过,你说的计算后仍然有否定严厉误差的问题,看我61行,我写那行代码的目的就是为了弥补这个误差的。不过后来看了没有误差,所以就隐掉了。
网友问:不是我挑错,但我总觉的你的代码显的很长。好多没必要。比如 GetRandomBetween函数,其实很简单。 GetRandomBetween(int nBegin,int nEnd) { int n = abs(nBegin); int nBetween= abs(nEnd) - n; if(nBetween < 0) { n = abs(nEnd); } if(nBetween == 0) nBetween = 10; n += _GetNot0(); return n; } 这样不是更简洁点么?完成的功能是完全一样的。
我答:把每句话尽量简化,简化到大家看起来一目了然的时候,程序就不容易出错。你的方案,一句话里面有多个计算,很绕。不是每个项目成员都有你的水平的。
网友问:再比如,你的 GetType和GetRandomType这2个函数,完全可以结合成一个嘛。 char GetType(int nTypeIndex) //输入参数0~10000 { return m_cTokenPercentArrayAreaUp[GetRandomBetween(0,10000)]; } 注意,这里的GetRandomBetween(0,10000)返回范围,就是0-9999. 这样不是简单多了?
我答:看下面,是故意拆分的,留两个api,给别人一个中间查表的切入点。
网友问:哦。原来是故意拆分的。
网友问:还有一点,为什么程序中,有好多char和short来替代int?这样有什么好处?是为了节约空间么?我认为,char和short在做参数传进传出,或者与int比较时,每次都要扩展为int,还不如直接用int好。在32位系统中,用int最快了。 只是自己的一些看法,有说的不对的,我们互相学习。
我答:嗯,看在你说出互相学习这句话,我回答你的问题:这段代码之所以写得像你说的这么繁琐,是为了尽可能提供api给使用者用,就是我小弟,他觉得用得方便。因为他是用户。我必须站在用户的需求角度设计api,方便调用。因此,很多稍微复杂一点的api函数,我会尽量拆细,每一步都提供一个函数接口给用户用。用不用在他,但是我尽量给全。
api接口设计,应该站在用户使用方便来设计的。反而是我的构造函数很复杂,是因为这些是我内部动作,我要屏蔽,无须通报外部,这体现高内聚,低耦合的原则。
char和short确实是为了节约空间考虑,因为里面有个10000个单元的大数组,用char是10k,用int是40k。
网友问:这么考虑的话也可以。我感觉在这种情况的话,用unsigned char会不会更好?
我答:我预设100个类型,<127,char的正数范围足以。
网友问:我倒是感觉这次的需求这么简单,没必要给更多的中间接口。设计以需求为目标,不是程序员觉的客户怎么方便怎么设计,有很多接口,客户也许根本用不到。反而造成不必要的设计,程序复杂度上升。
我答:这个算我个人习惯吧,基础模块的公有接口我习惯留得越多越好,越简单越好,最好每个接口一句话。这样,哪天有新需求,省的我改接口。因为这类基础模块的使用者,通常就是我团队成员,大家这么做也习惯了。不过,对外的接口,还是应该越少越好,这是原则,比如功能层向业务层输出的接口,和其他小组的接口,暴露越少越好。不同的需求导致不同的设计。
最后再补充一点,你有想过这个程序的效率没有?它用查表法,你可以和普通计算法比较一下,每个都跑个1000万次,你就看出时间差别了。而且,它不用锁,并行环境和串行环境效率一样高。
网友问:查表法肯定比每次都计算省时间。但第一次构造要花时间,而且牺牲一部分空间。就你这次的实现来看,用查表法是对的。
我答:实话跟你讲吧,这段代码是有前提的,我们要做5000万条记录,中间有20万个设备的记录,每个设备的采样频率不一样,我要并发模拟,你再想想我写这么复杂有道理没?
最后,还有网友反映,构造函数太复杂,看不懂,我这里也解释一下。当时情况比较急,小弟赶着用,我也没时间精雕细琢这个代码,所以,构造函数写得就很复杂,基本上想到哪写到哪。
这个函数的设计,并不符合《0bug-C/C++商用工程之道》第三章的“C/C++无错化设计原则”,所以看起来就难懂。看见没,只要不符合这个原则,只要一个函数内有多个循环主体,即多个逻辑意思,大家看起来就混乱。 希望大家以后开发引以为戒,尽量还是写简单的程序。
=======================================================
在线底价购买《0bug-C/C++商用工程之道》
(直接点击下面链接或拷贝到浏览器地址栏)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0
肖舸
如果您对我的文章感兴趣,请点这里添加我为好友:http://student.csdn.net/invite.php?u=39028&c=42fcd4a519102d74
博客主站:http://blog.csdn.net/tonyxiaohome
CSDN学生大本营个人主页:http://student.csdn.net/?39028
举报
科大校友(C/C++)
罗长友(3G/移动开发)
郭伟(C/C++)
武汉科技大学 赵孜泷(Java学生)
交大附中 顾佳炜(Net爱好者)
姚小根(数据库)
崔备(Java)
一毛(C/C++)
bill(Java爱好者)
颜镇江(Net学生)
小许(C/C++学生)
天津城市建设学院 谭鹏飞(Java爱好者)
王晓威(C/C++学生)
CSDN学生大本营辅导员 张媛(职业指导爱好者)
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:17
先Up再看
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:21
顺便说下,刚刚用老大的书解决了一个2个小时的问题,头文件包含另一个头文件。。 后来突然想到书上有个方法。 然后解决了 一个子 爽
回复 举报
肖舸 2010-04-16 15:24
专注于linux编程&分词领域 付吉祥(C/C++学生): 顺便说下,刚刚用老大的书解决了一个2个小时的问题,头文件包含另一个头文件。。 后来突然想到书上有个方法。 然后解决了 一个子 爽
嗯,这本来就不复杂。
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:26
TPRI 肖舸(C/C++老师)<img src='image/group/teacher.gif' border=0>: 嗯,这本来就不复杂。
恩 纯属经验问题 工程文件比较多,出现很多错误,开始以为什么问题,原来是头文件包含问题
回复 举报
王辉(C/C++) 2010-04-16 20:00
慢慢研究下
回复 举报
**(数据库学生) 2010-04-16 20:06
收了。
回复 举报
王晓威(C/C++学生) 2010-04-16 21:02
用VS2008的人不少
回复 举报
四川省南充市南部一中 周彬(C/C++学生) 2010-04-16 21:04
学习了
回复 举报
江西财经 彭文忠(C/C++学生) 2010-04-17 12:48
收藏下慢慢研究去
回复 举报
slsl7979 2010-04-17 17:21
你好,肖老师,请问下上面这个程序有一处 if(100.0!=dNumberCount),浮点数的比较
这么比是可以的吗,
回复 举报
重庆邮电大学 段君临(Java) 2010-04-17 22:26
我想问下肖老师 在实际编程中 有哪里可以实现随机机制的 方法。 简单的给我说下思想吧
有劳。。
回复 举报
bill(Java爱好者) 2010-04-17 23:59
1. 第一次看到,把构造函数写这么复杂的
2. 感觉关于两个浮点数判等的回复有些狡辩,如果正如你回复的,想严厉校验话,不论用户输入的数加起来是不是100.0,你都重新分配一下就得了,或者对你浮点数判等的地方特地写注释说明一下,还反问人家看懂没有!
3. GetRandomBetween(int nBegin,int nEnd) 函数在逻辑上有问题,你的CTonyRandomArea 得到合理结果的前提是GetRandomBetween得到均匀分布的随机数据,实际上,该函数得不到均 匀分布的随机数。
回复 举报
肖舸 2010-04-18 09:04
重庆邮电大学 段君临(Java): 我想问下肖老师 在实际编程中 有哪里可以实现随机机制的 方法。 简单的给我说下思想吧
有劳。。
这个方法就太多了,rand算一个。野指针的值也算一个。从某个变化量比较大的设备,比如时钟振荡器读状态,再经过一些哈希计算什么的,都可以。
回复 举报
肖舸 2010-04-18 09:05
bill(Java爱好者): 1. 第一次看到,把构造函数写这么复杂的
2. 感觉关于两个浮点数判等的回复有些狡辩,如果正如你回复的,想严厉校验话,不论用户输入的数加起来是不是100.0,你都重新分配
无所谓,你认为我程序写的不好,不看就是了,呵呵。
回复 举报
肖舸 2010-04-18 09:17
slsl7979: 你好,肖老师,请问下上面这个程序有一处 if(100.0!=dNumberCount),浮点数的比较
这么比是可以的吗,
看新增部分内容。
回复 举报
bill(Java爱好者) 2010-04-18 11:01
我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不过一段时间再看程序,恐怕自己也要想想当时为啥这样写
GetRandomBetween函数你用的是用区间长取模加起点的方法 “rand() % (b-a) + a”,你的程序写得很工整,我想说的是这个方法本身有缺陷:
rand()函数得到的是(0,RAND_MAX)之间均匀分布的伪随机数,用区间长取模法会改变它的均匀分布性,在C++中RAND_MAX=32768,
就用你文中转成(0,10000)间的随机数为例来说,在生成大量随机数时,(0,2768)区段的随机数比重要比(2768,10000)区段的高25%左右。
我觉着把你函数中倒数第三句换成:n = (int)(nBetwwen * (n / (double)(RAND_MAX)); 是不是要好一些,这样得到的随机序列保持了均匀性
inline int GetRandomBetween(int nBegin,int nEnd)
{
int n=_GetNot0();
int nBetween=0;
if(0>nBegin) nBegin=-nBegin;
if(0>nEnd) nEnd=-nEnd;
if(nBegin>nEnd)
{
nBetween=nEnd;
nEnd=nBegin;
nBegin=nBetween;
}
else if(nBegin==nEnd)
nEnd=nBegin+10;
nBetween=nEnd-nBegin;
// n=n%nBetween;
n = (int)(nBetwwen * (n / (double)(RAND_MAX));
n+=nBegin;
return n;
}
原来是一次取模,现在是一次除法和一次乘法,都是简单类型,我认为性能上不会有多大差别,且保证了转换后随机序列的均匀性。
以上只是就这个问题和你讨论下
回复 举报
肖舸 2010-04-18 11:28
bill(Java爱好者): 我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不
呵呵,建议不讨论了。
这个函数是我工程库长期使用的一个函数,过去长期的使用结果表明,它满足我使用需求,并且没有bug。
你要有兴趣写个更好的,当然没有问题,不过,建议自己开一篇博文来论述好了。
至少目前,在没有新的不满足需求到来前,我不会修改这个函数的内容的。以维持工程库的稳定性。
回复 举报
肖舸 2010-04-18 11:35
bill(Java爱好者): 我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不
至于工程库内模块的来龙去脉,有兴趣研究的话,建议看看《0bug》这本书,看了,里面很多设计思想的由来就都清楚了。
回复 举报
交大附中 顾佳炜(Net爱好者) 2010-04-18 15:14
不错
回复 举报
吴小伟(C/C++学生) 2010-04-18 16:26
程序写的如何,不评论,至少数学功底较弱
回复 举报
北京交通大学 牛俊力(C/C++学生) 2010-04-18 23:49
我感觉真没必要写这么复杂。既然是百分比。那么直接[1-100]随机。如果得到1-12返回状态1。13-42返回状态2。43-92返回状态3。93返回状态4。94-100返回状态5。直接用if-else结构在函数调用时每次都要判断多次,效率稍低。但我觉得也能接受。如果想快的话。建个规模100的int数组。按前面的比例存1-5这五个状态。(也就是12个1,40个2、、、最后一个5)。然后直接通过数组索引定位状态。牺牲一点空间换时间,都行。但是弄这么复杂我实在看不出道理。
回复 举报
肖舸 2010-04-19 05:50
北京交通大学 牛俊力(C/C++学生): 我感觉真没必要写这么复杂。既然是百分比。那么直接[1-100]随机。如果得到1-12返回状态1。13-42返回状态2。43-92返回状态3。93返回状态4。94-100返回状态5。直接
如果下次不是5个状态,而是6个呢?
如果是5个状态,但是比例不一样呢?
重写代码?
另外,这么多if,如果跑1000万次呢?效率如何?
回复 举报
北京交通大学 牛俊力(C/C++学生) 2010-04-19 10:26
TPRI 肖舸(C/C++老师)<img src='image/group/teacher.gif' border=0>: 如果下次不是5个状态,而是6个呢?
如果是5个状态,但是比例不一样呢?
重写代码?
另外,这么多if,如果跑1000万次呢?效率如何?
是哦。我只是针对这个问题考虑的。如果状态增多。还是要改很多的。不过多if的问题已经用打表解决了。在那段问题后面我有说。
这个程序解决的问题如下:
已知一件事物有几种状态,每种状态出现的概率不一样,要求做一个随机数产生器,返回状态值,要求状态值出现的规律,符合输入的概率。
这是小弟上午问我的问题,我们正在做一个工业测试模型,实际的例子是,根据实际情况,某种设备返回的状态概率符合下表:
设备状态 | 百分比 |
1 | 12% |
2 | 40% |
3 | 40% |
4 | 7% |
5 | 1% |
我下午上班想了一下,花了半个小时为他写了一个随机数产生器,经测试,0bug,呵呵。他现在正在用。
Code:
#define CTonyRandomArea_TOKEN_MAX 100 //最大类型数
#define CTonyRandomArea_TOKEN_AREA_MAX 10000 //类型数组单元数,精确到小数点后两位
//输入最大100个元素的数组,每个数组表示每类占有的百分比,内部自带百分比调整。
//即如果外部输入的数字之和不是整数100,内部会根据百分比,自动调整其比例,使总和=100.0
//然后内部建立10000个单元的类型数组,根据传入的每种类型的比例,在类型数组中批量填充对应的类型值
//总之,类型数组中每种类型的数量,占据的比例正好是输入的百分比
//最后,在0~10000中取随机,然后在对应的类型数组单元中取类型值,即为返回的类型
class CTonyRandomArea
{
public:
CTonyRandomArea(double* pTokenPercentArray,char cTokenCount)
{
m_nTokenCount=cTokenCount;
if(CTonyRandomArea_TOKEN_MAX<m_nTokenCount)
m_nTokenCount=CTonyRandomArea_TOKEN_MAX;
int i=0;
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]=*(pTokenPercentArray+i);
}
//动态调整内部的值
//有时候试验人员,测得几个状态出现的数字,可能懒得再计算成百分比
//程序帮忙自动计算
double dNumberCount=0;
for(i=0;i<m_nTokenCount;i++)
{
dNumberCount+=m_dTokenPercentArray[i];
}
if(100.0!=dNumberCount)
{
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]/=dNumberCount;
m_dTokenPercentArray[i]*=100;
}
}
//以小数点后两位精度,开始计算在10000个总单元中,每种类型对应的数量。
for(i=0;i<m_nTokenCount;i++)
{
m_sTokenPercentArray[i]=(short)(m_dTokenPercentArray[i]*100);
}
//按比例填充类型数组
int j=0;
int nFillMin=0;
int nFillMax=0;
for(i=0;i<m_nTokenCount;i++)
{
m_cTokenPercentArrayAreaUp[i]=-1;
}
for(i=0;i<m_nTokenCount;i++)
{
nFillMax=nFillMin+m_sTokenPercentArray[i];
for(j=nFillMin;j<nFillMax;j++)
{
m_cTokenPercentArrayAreaUp[j]=i;
}
nFillMin=nFillMax;
}
// m_cTokenPercentArrayAreaUp[CTonyRandomArea_TOKEN_AREA_MAX-1]=i-1;
}
~CTonyRandomArea(){}
void PrintfInfo(void)
{
int i=0;
double dNumberCount=0;
for(i=0;i<m_nTokenCount;i++)
{
dNumberCount+=m_dTokenPercentArray[i];
printf("%d = %f/n",i,m_dTokenPercentArray[i]);
}
printf("All = %f/n",dNumberCount);
//打印10000个单元的类型分布,看看排得对不对
//这段打印起来太长,一般隐掉,需要了再打印
// int nOutMax=400;
// int nInMax=25; //二者相乘为10000
// int j=0;
// for(i=0;i<nOutMax;i++)
// {
// printf("%05d - ",i*nInMax);
// for(j=0;j<nInMax;j++)
// {
// printf("%d ",m_cTokenPercentArrayAreaUp[i*nInMax+j]);
// }
// printf("/n");
// }
}
//取类型数组对应单元的值
char GetType(int nTypeIndex) //输入参数0~10000
{
if(10000<=nTypeIndex) nTypeIndex=9999;
if(0>nTypeIndex) nTypeIndex=0;
return m_cTokenPercentArrayAreaUp[nTypeIndex];
}
//真实的工作函数,利用输入的概率来产生随机type
char GetRandomType(void)
{
return GetType(GetRandomBetween(0,10000));
}
private:
double m_dTokenPercentArray[CTonyRandomArea_TOKEN_MAX]; //比例数组
short m_sTokenPercentArray[CTonyRandomArea_TOKEN_MAX]; //比例数组,短整型,1~10000,相当于精确到小数点后两位
char m_nTokenCount;
char m_cTokenPercentArrayAreaUp[CTonyRandomArea_TOKEN_AREA_MAX]; //类型数组
};
//这是测试代码
void TestCTonyRandomArea(void)
{
double dTokenPercentArray[100];
dTokenPercentArray[0]=12.0;
dTokenPercentArray[1]=40.0;
dTokenPercentArray[2]=40.0;
dTokenPercentArray[3]=7.0;
dTokenPercentArray[4]=1.0;
CTonyRandomArea Area1(dTokenPercentArray,5);
Area1.PrintfInfo();
int i=0;
for(i=0;i<20;i++)
{
printf("RandType = %d/n",Area1.GetRandomType());
}
}
其实这个原理很简单:
1、我先从外部导入一个比例列表,在100以内的数组单元,每个单元里面放置一个double值,相当于对应类别的比例。这样,我预设最大有100种状态,具体本次试验有多少种状态,即100个状态比例数组多少个单元是有效的,看构造函数的第二个参数,就是这个参数输入的。
2、这里面我做了点人性化考虑,因为很多时候,我们测试的设备状态是直接的采样值,即每种状态出现了多少次,懒得计算成百分比,因此,我内部自动帮他重新计算一遍百分比。这样用起来很方便。
3、我根据各种类型的比例,内部准备一个10000个单元的大数组,我根据每种状态的比例,在这个数组中填充足够的状态数,这样,构建了一个比例分配表。这实际上是把计算精度放大到小数点后两位,即99.99%这种精度
4、我真正提供随机数的函数,是在0~10000中取值,即随机命中比例分配表的某个单元,这个单元取出来是哪种状态,就返回哪种状态。由于比例分配表决定了各种状态被命中的比例,因此,我返回值是符合出现比例的。
5、最后我给了一个测试函数TestCTonyRandomArea,这是我团队的规矩,任何人写一个模块,必须同时提供相应的白盒测试函数,并将测试结果展示给使用者看,作为验收标准,即“你必须自己证明自己的工作是有效的,并接受检验”,我这个leader也不能例外。
6、PrintfInfo函数也是我团队的规矩,位于底层的类,有责任提供一个PrintInfo函数,供调用者随时查阅你的内部数据,“把你的数据暴露给大家看,想出来混江湖,就不怕裸奔被人看!”,呵呵《0bug-C/C++商用工程之道》里面很多类都有这个函数的。
嗯,中间有个GetRandomBetween这个函数,就是《0bug-C/C++商用工程之道》这本书P199页的源代码,这里我也给一份Copy,另外,其工作原理,有兴趣的读者可以看看书中的描述。
Code:
inline int _GetNot0(void)
{
int nRet=rand();
if(!nRet) nRet++;
return nRet;
}
inline int GetRandomBetween(int nBegin,int nEnd)
{
int n=_GetNot0();
int nBetween=0;
if(0>nBegin) nBegin=-nBegin;
if(0>nEnd) nEnd=-nEnd;
if(nBegin>nEnd)
{
nBetween=nEnd;
nEnd=nBegin;
nBegin=nBetween;
}
else if(nBegin==nEnd)
nEnd=nBegin+10;
nBetween=nEnd-nBegin;
n=n%nBetween;
n+=nBegin;
return n;
}
上述代码是我匆忙写的,属于测试用代码,不完全符合0bug一书里面的C/C++无错化程序设计原则,各位读者请注意哈。
不过,虽然是测试代码,但是带了很多工程型代码的影子,大家有兴趣可以看看。
另外,上述代码没有做锁封装,但是,仍然是多线程安全的。大家有注意到没有?
因为其工作原理是查表法,所有的表构造时一次生成,以后仅仅是纯读,请《0bug-C/C++商用工程之道》的读者注意2.3.6节,P50页的论述,“用锁的最高境界--不用”,这里符合第1条特例,“针对一个资源的所有操作都是读的时候,可以不加锁”。我这段代码可以算作实例了。各位读者可以参考一下。
好吧,就这么多,大家有兴趣可以看看。
嗯,有人可能说,这里的随机数产生器没有使用srand初始化,记住,我在用我自己的工程库,也就是《0bug-C/C++商用工程之道》的工程库,工程库的init动作里面已经做过这种动作了。
代码是VS2008下测试的,不过,我的理解,应该是跨平台的。
上述代码在很多游戏开发中可以投入实用的。
比如说,某个NPC哨兵,他可能在某个时刻,看前后左右,或者抽烟,或者睡觉,或者和另一个哨兵聊天,这时候,可以用这个随机数产生器,根据预设的每种动作的概率,权重,随时求出他的行为种类,并予以展示。
再或者,暗黑里面,我们使用暗金的装备,每次攻击,有百分之多少的概率出现压碎性打击,有多少概率出现冰冻属性,等等,也可以用这个随机数产生器来求。
大家慢慢想吧,呵呵。
嗯,这里网友发现一处bug,我已经修改了,请昨天看过的朋友注意:
Code:
if(100.0!=dNumberCount)
{
for(i=0;i<m_nTokenCount;i++)
{
m_dTokenPercentArray[i]/=dNumberCount;
m_dTokenPercentArray[i]*=100; //这里少乘了个100,百分比动态调整失效,因此,我加上了这一句。
}
}
这段代码出来后,一些网友表示看不懂我的原意,我们在CSDN博客有一些问答,我觉得对大家理解本程序的设计思路有帮助,因此,整理了一下,摘录在这里:
网友问:if(100.0!=dNumberCount) 浮点数直接用等于作比较是不正确的
我答:通常的做法是if((100.0-dNumberCount)<0.00000001),我知道的,不过,我为什么这么写,你看得懂吗?
网友问:不懂,老师教教吧,谢谢
我答:这里主要的目的是否定,是为了验证所有输入的double数加起来不是100.0,然后内部重新计算一次。由于外部人员输入,通常都不是正好100.0,因此,这里使用否定的严厉校验,即只要不是绝对==100.0,内部就重新计算。看好了,我是否定严厉,不是肯定严厉,因此不用教科书做法。
网友问:同意这样直接比较在此处也不会产生错误,我还是有如下观点: 1. 这样严厉的否定可能会拒绝一些本可以接受的输入,当然概率比较小,而且即使拒绝了也顶多是多计算一下,不会有bug 2. 即使是通过代码中的“自动调整其比例“的计算以后,仍然有可能会出发您的”否定严厉“ 所以我认为还是不应该用直接比较。 3. (100.0-dNumberCount)<0.00000001 这样的比较还是不合适的,一是要用绝对值,当然这里可能是您忘记写了;二是0.00000001的取定要推敲,用float.h中提供的常数宏更好
我答:嗯,你说的有理,我下回注意,呵呵。不过,你说的计算后仍然有否定严厉误差的问题,看我61行,我写那行代码的目的就是为了弥补这个误差的。不过后来看了没有误差,所以就隐掉了。
网友问:不是我挑错,但我总觉的你的代码显的很长。好多没必要。比如 GetRandomBetween函数,其实很简单。 GetRandomBetween(int nBegin,int nEnd) { int n = abs(nBegin); int nBetween= abs(nEnd) - n; if(nBetween < 0) { n = abs(nEnd); } if(nBetween == 0) nBetween = 10; n += _GetNot0(); return n; } 这样不是更简洁点么?完成的功能是完全一样的。
我答:把每句话尽量简化,简化到大家看起来一目了然的时候,程序就不容易出错。你的方案,一句话里面有多个计算,很绕。不是每个项目成员都有你的水平的。
网友问:再比如,你的 GetType和GetRandomType这2个函数,完全可以结合成一个嘛。 char GetType(int nTypeIndex) //输入参数0~10000 { return m_cTokenPercentArrayAreaUp[GetRandomBetween(0,10000)]; } 注意,这里的GetRandomBetween(0,10000)返回范围,就是0-9999. 这样不是简单多了?
我答:看下面,是故意拆分的,留两个api,给别人一个中间查表的切入点。
网友问:哦。原来是故意拆分的。
网友问:还有一点,为什么程序中,有好多char和short来替代int?这样有什么好处?是为了节约空间么?我认为,char和short在做参数传进传出,或者与int比较时,每次都要扩展为int,还不如直接用int好。在32位系统中,用int最快了。 只是自己的一些看法,有说的不对的,我们互相学习。
我答:嗯,看在你说出互相学习这句话,我回答你的问题:这段代码之所以写得像你说的这么繁琐,是为了尽可能提供api给使用者用,就是我小弟,他觉得用得方便。因为他是用户。我必须站在用户的需求角度设计api,方便调用。因此,很多稍微复杂一点的api函数,我会尽量拆细,每一步都提供一个函数接口给用户用。用不用在他,但是我尽量给全。
api接口设计,应该站在用户使用方便来设计的。反而是我的构造函数很复杂,是因为这些是我内部动作,我要屏蔽,无须通报外部,这体现高内聚,低耦合的原则。
char和short确实是为了节约空间考虑,因为里面有个10000个单元的大数组,用char是10k,用int是40k。
网友问:这么考虑的话也可以。我感觉在这种情况的话,用unsigned char会不会更好?
我答:我预设100个类型,<127,char的正数范围足以。
网友问:我倒是感觉这次的需求这么简单,没必要给更多的中间接口。设计以需求为目标,不是程序员觉的客户怎么方便怎么设计,有很多接口,客户也许根本用不到。反而造成不必要的设计,程序复杂度上升。
我答:这个算我个人习惯吧,基础模块的公有接口我习惯留得越多越好,越简单越好,最好每个接口一句话。这样,哪天有新需求,省的我改接口。因为这类基础模块的使用者,通常就是我团队成员,大家这么做也习惯了。不过,对外的接口,还是应该越少越好,这是原则,比如功能层向业务层输出的接口,和其他小组的接口,暴露越少越好。不同的需求导致不同的设计。
最后再补充一点,你有想过这个程序的效率没有?它用查表法,你可以和普通计算法比较一下,每个都跑个1000万次,你就看出时间差别了。而且,它不用锁,并行环境和串行环境效率一样高。
网友问:查表法肯定比每次都计算省时间。但第一次构造要花时间,而且牺牲一部分空间。就你这次的实现来看,用查表法是对的。
我答:实话跟你讲吧,这段代码是有前提的,我们要做5000万条记录,中间有20万个设备的记录,每个设备的采样频率不一样,我要并发模拟,你再想想我写这么复杂有道理没?
最后,还有网友反映,构造函数太复杂,看不懂,我这里也解释一下。当时情况比较急,小弟赶着用,我也没时间精雕细琢这个代码,所以,构造函数写得就很复杂,基本上想到哪写到哪。
这个函数的设计,并不符合《0bug-C/C++商用工程之道》第三章的“C/C++无错化设计原则”,所以看起来就难懂。看见没,只要不符合这个原则,只要一个函数内有多个循环主体,即多个逻辑意思,大家看起来就混乱。 希望大家以后开发引以为戒,尽量还是写简单的程序。
=======================================================
在线底价购买《0bug-C/C++商用工程之道》
(直接点击下面链接或拷贝到浏览器地址栏)
http://s.click.taobao.com/t_3?&p=mm_13866629_0_0&n=23&l=http%3A%2F%2Fsearch8.taobao.com%2Fbrowse%2F0%2Fn-g%2Corvv64tborsvwmjvgawdkmbqgboq---g%2Cgaqge5lhebbs6qzlfmqmttgtyo42jm6m22xllqa-------------1%2C2%2C3%2C4%2C5%2C6%2C7%2C8%2C9%2C10%2C11%2C12%2C13%2C14%2C15%2C16%2C17%2C18%2C19%2C20---40--coefp-0-all-0.htm%3Fpid%3Dmm_13866629_0_0
肖舸
如果您对我的文章感兴趣,请点这里添加我为好友:http://student.csdn.net/invite.php?u=39028&c=42fcd4a519102d74
博客主站:http://blog.csdn.net/tonyxiaohome
CSDN学生大本营个人主页:http://student.csdn.net/?39028
举报
路过 | 鸡蛋 | 12 鲜花 | 握手 | 2 雷人 |
刚表态过的朋友 (14 人)
科大校友(C/C++)
罗长友(3G/移动开发)
郭伟(C/C++)
武汉科技大学 赵孜泷(Java学生)
交大附中 顾佳炜(Net爱好者)
姚小根(数据库)
崔备(Java)
一毛(C/C++)
bill(Java爱好者)
颜镇江(Net学生)
小许(C/C++学生)
天津城市建设学院 谭鹏飞(Java爱好者)
王晓威(C/C++学生)
CSDN学生大本营辅导员 张媛(职业指导爱好者)
发表评论 评论 (23 个评论)
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:17
先Up再看
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:21
顺便说下,刚刚用老大的书解决了一个2个小时的问题,头文件包含另一个头文件。。 后来突然想到书上有个方法。 然后解决了 一个子 爽
回复 举报
肖舸 2010-04-16 15:24
专注于linux编程&分词领域 付吉祥(C/C++学生): 顺便说下,刚刚用老大的书解决了一个2个小时的问题,头文件包含另一个头文件。。 后来突然想到书上有个方法。 然后解决了 一个子 爽
嗯,这本来就不复杂。
回复 举报
专注于linux编程&分词领域 付吉祥(C/C++学生) 2010-04-16 15:26
TPRI 肖舸(C/C++老师)<img src='image/group/teacher.gif' border=0>: 嗯,这本来就不复杂。
恩 纯属经验问题 工程文件比较多,出现很多错误,开始以为什么问题,原来是头文件包含问题
回复 举报
王辉(C/C++) 2010-04-16 20:00
慢慢研究下
回复 举报
**(数据库学生) 2010-04-16 20:06
收了。
回复 举报
王晓威(C/C++学生) 2010-04-16 21:02
用VS2008的人不少
回复 举报
四川省南充市南部一中 周彬(C/C++学生) 2010-04-16 21:04
学习了
回复 举报
江西财经 彭文忠(C/C++学生) 2010-04-17 12:48
收藏下慢慢研究去
回复 举报
slsl7979 2010-04-17 17:21
你好,肖老师,请问下上面这个程序有一处 if(100.0!=dNumberCount),浮点数的比较
这么比是可以的吗,
回复 举报
重庆邮电大学 段君临(Java) 2010-04-17 22:26
我想问下肖老师 在实际编程中 有哪里可以实现随机机制的 方法。 简单的给我说下思想吧
有劳。。
回复 举报
bill(Java爱好者) 2010-04-17 23:59
1. 第一次看到,把构造函数写这么复杂的
2. 感觉关于两个浮点数判等的回复有些狡辩,如果正如你回复的,想严厉校验话,不论用户输入的数加起来是不是100.0,你都重新分配一下就得了,或者对你浮点数判等的地方特地写注释说明一下,还反问人家看懂没有!
3. GetRandomBetween(int nBegin,int nEnd) 函数在逻辑上有问题,你的CTonyRandomArea 得到合理结果的前提是GetRandomBetween得到均匀分布的随机数据,实际上,该函数得不到均 匀分布的随机数。
回复 举报
肖舸 2010-04-18 09:04
重庆邮电大学 段君临(Java): 我想问下肖老师 在实际编程中 有哪里可以实现随机机制的 方法。 简单的给我说下思想吧
有劳。。
这个方法就太多了,rand算一个。野指针的值也算一个。从某个变化量比较大的设备,比如时钟振荡器读状态,再经过一些哈希计算什么的,都可以。
回复 举报
肖舸 2010-04-18 09:05
bill(Java爱好者): 1. 第一次看到,把构造函数写这么复杂的
2. 感觉关于两个浮点数判等的回复有些狡辩,如果正如你回复的,想严厉校验话,不论用户输入的数加起来是不是100.0,你都重新分配
无所谓,你认为我程序写的不好,不看就是了,呵呵。
回复 举报
肖舸 2010-04-18 09:17
slsl7979: 你好,肖老师,请问下上面这个程序有一处 if(100.0!=dNumberCount),浮点数的比较
这么比是可以的吗,
看新增部分内容。
回复 举报
bill(Java爱好者) 2010-04-18 11:01
我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不过一段时间再看程序,恐怕自己也要想想当时为啥这样写
GetRandomBetween函数你用的是用区间长取模加起点的方法 “rand() % (b-a) + a”,你的程序写得很工整,我想说的是这个方法本身有缺陷:
rand()函数得到的是(0,RAND_MAX)之间均匀分布的伪随机数,用区间长取模法会改变它的均匀分布性,在C++中RAND_MAX=32768,
就用你文中转成(0,10000)间的随机数为例来说,在生成大量随机数时,(0,2768)区段的随机数比重要比(2768,10000)区段的高25%左右。
我觉着把你函数中倒数第三句换成:n = (int)(nBetwwen * (n / (double)(RAND_MAX)); 是不是要好一些,这样得到的随机序列保持了均匀性
inline int GetRandomBetween(int nBegin,int nEnd)
{
int n=_GetNot0();
int nBetween=0;
if(0>nBegin) nBegin=-nBegin;
if(0>nEnd) nEnd=-nEnd;
if(nBegin>nEnd)
{
nBetween=nEnd;
nEnd=nBegin;
nBegin=nBetween;
}
else if(nBegin==nEnd)
nEnd=nBegin+10;
nBetween=nEnd-nBegin;
// n=n%nBetween;
n = (int)(nBetwwen * (n / (double)(RAND_MAX));
n+=nBegin;
return n;
}
原来是一次取模,现在是一次除法和一次乘法,都是简单类型,我认为性能上不会有多大差别,且保证了转换后随机序列的均匀性。
以上只是就这个问题和你讨论下
回复 举报
肖舸 2010-04-18 11:28
bill(Java爱好者): 我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不
呵呵,建议不讨论了。
这个函数是我工程库长期使用的一个函数,过去长期的使用结果表明,它满足我使用需求,并且没有bug。
你要有兴趣写个更好的,当然没有问题,不过,建议自己开一篇博文来论述好了。
至少目前,在没有新的不满足需求到来前,我不会修改这个函数的内容的。以维持工程库的稳定性。
回复 举报
肖舸 2010-04-18 11:35
bill(Java爱好者): 我没有说你程序写得不好的意思
关于构造函数写得复杂,你前文中也说了原因
浮点数判等的问题你也说明了,我的意思是最好在代码注释一下那样写的原因不是更好些吗,要不
至于工程库内模块的来龙去脉,有兴趣研究的话,建议看看《0bug》这本书,看了,里面很多设计思想的由来就都清楚了。
回复 举报
交大附中 顾佳炜(Net爱好者) 2010-04-18 15:14
不错
回复 举报
吴小伟(C/C++学生) 2010-04-18 16:26
程序写的如何,不评论,至少数学功底较弱
回复 举报
北京交通大学 牛俊力(C/C++学生) 2010-04-18 23:49
我感觉真没必要写这么复杂。既然是百分比。那么直接[1-100]随机。如果得到1-12返回状态1。13-42返回状态2。43-92返回状态3。93返回状态4。94-100返回状态5。直接用if-else结构在函数调用时每次都要判断多次,效率稍低。但我觉得也能接受。如果想快的话。建个规模100的int数组。按前面的比例存1-5这五个状态。(也就是12个1,40个2、、、最后一个5)。然后直接通过数组索引定位状态。牺牲一点空间换时间,都行。但是弄这么复杂我实在看不出道理。
回复 举报
肖舸 2010-04-19 05:50
北京交通大学 牛俊力(C/C++学生): 我感觉真没必要写这么复杂。既然是百分比。那么直接[1-100]随机。如果得到1-12返回状态1。13-42返回状态2。43-92返回状态3。93返回状态4。94-100返回状态5。直接
如果下次不是5个状态,而是6个呢?
如果是5个状态,但是比例不一样呢?
重写代码?
另外,这么多if,如果跑1000万次呢?效率如何?
回复 举报
北京交通大学 牛俊力(C/C++学生) 2010-04-19 10:26
TPRI 肖舸(C/C++老师)<img src='image/group/teacher.gif' border=0>: 如果下次不是5个状态,而是6个呢?
如果是5个状态,但是比例不一样呢?
重写代码?
另外,这么多if,如果跑1000万次呢?效率如何?
是哦。我只是针对这个问题考虑的。如果状态增多。还是要改很多的。不过多if的问题已经用打表解决了。在那段问题后面我有说。
相关文章推荐
- 实际中常用的一个随机数产生器(分类别概率随机) 推荐
- 实际中常用的一个随机数产生器
- 实际中常用的一个随机数产生器(分类别概率随机)
- 实际中常用的一个随机数产生器(分类别概率随机)
- JAVA的一个随机数产生模块
- 如何产生一个从x到y的k个随机数 Random(int x,int y ,int k)
- ORACLE 如何产生一个随机数
- 编写一个产生随机数的库
- Javascript和JQuery中常用的随机数产生函数
- 产生一个随机数 将随机数保留两位小数
- 猜数游戏,产生一个随机数
- 产生一个int数组,随机向数组中插入1-100的随机数且不能重复
- “舒尔特表”训练法:产生1-25随机数,并打印到一个表格中
- ORACLE 如何产生一个随机数:DBMS_RANDOM
- 一个例子和我做的微秒级随机数产生器~~
- 日期转换工具以及常用的字符,产生随机数,将java流中的编码转换为utf-8----java
- 产生一个随机数
- Mysql产生一个随机数时,报Recursive stored functions and triggers are not allowed.
- 产生一个1000以内的随机数,并猜测
- C语言产生一个[M, N]区间上的随机数