一、实现需求
利用C语言提供的rand()函数构造一个随机函数randLong(),能够生成0-(2^31-1)之间的随机整数。rand()生成的随机整数在0-(2^25-1)范围内。
二、利用位图的方法
起初我以为很简单:
void randLong(){ return 1.0*rand()/RAND_MAX*LONG_MAX; }
后来发现我犯了严重的错误,rand()的取值空间中只有2^25-1=32767个整数,按上面的方法只是在0-(2^31-1)上抽取了一小部分数。如果说rand()生成的是0-32767直接的随机浮点数而不是整数的话,上面的办法扩展随机数的范围是可行的。
因为一直在整位图,所以在想能不能从位入手,实现随机数的扩展。我都有点感受到位图的强大了,还真想出了一个用位图扩展的方法,这里不再拐弯抹角,直接上菜:
需要生产的随机数是[0-(2^31-1)]的闭区间内,端点值化成2进制后为:
0:0000,0000,0000,0000 ... ... ... 2^31-1:0111,1111,1111,1111
看到这个二进制后,相信你已经有想法了。我们申请一个32位长整型的位图,将位图每一位初始化为0,这时候我们需要一个rand1()生产0,1的随机数,对每个位调用一次random=rand1(),如果random=1就把该位设置为1,如果为random=0则设置为0,调用31次(除最高位为0不需要随机选择)后,这个长整型变量的值就是我们需要的随机数。
如果rand1()足够随机,即等概率(1/2)的生成0、1,那么我们按上面的方法的得到的任意一个数的概率都为(1/2)^31。来看一下实现代码:
#include <stdio.h> #include <stdlib.h> #include <math.h> /*******生成0、1的随机产生器********/ short rand01(){ return rand() % 2; } /********将bit所在位设置为1**********/ void setOne(long * p, short bit){ *p |= 1<<bit; } /********将bit所在位设置0**********/ void setZero(long *p, short bit){ *p &=~(1<<bit); } /*******生成0-2^bitNum-1之间的随机数**************/ long randLong(short bitNum){ long a = 0; long *p = &a; for(short i= 0;i < bitNum; i++){ short bit = rand01() % 2; if(bit == 0) setZero(p,i); else setOne(p,i); } return *p; }
randLong(short bitNum)可以生成0到2的任意幂次方之间的随机数(当然bitNum<=31),生成的随机数基本上等概率的。我们可以测试一下生成0-7的随机数概率:
void main(){ const int max = 8; int count[max]; for(short i=0;i<max;i++) count[i] = 0; long num=10000000; for(long i=0;i<num;i++){ long index = randLong(3);//生成0-(2^3-1)的随机数 count[index]++;//计数 } for(short i=0;i<max;i++) printf("%d:\t%lf\n",i,1.0*count[i]/num);//输出概率 }
输出的结果如下:
进行了1000万次试验,发现每个数的概率还是很接近1/8=0.125的。
三、递归实现的方法
上面有位图的方法,每次生成一个随机数需要调用多次rand01(),速度略微慢些,这里再提供一个基于递归的实现方法,直接上代码:
/**********根据rand()构造[0,max]的闭区间的随机整数**********/ long random(long max){ if(max <= RAND_MAX)//RAND_MAX是rand()的最大值,值为0x7fff return rand()% (max+1);//直接得到[0,max]的随机数 else return (random(max/(1+RAND_MAX)) * (1+RAND_MAX) + rand()) % (max+1);//递归得到[0,max]的随机数 }
当max<=RAND_MAX时,对rand()模余就得到了[0,max]的随机数;
当max>RAND_MAX时,我们先来看 (random(max/(1+RAND_MAX)) * (1+ RAND_MAX) + rand()) ,令k=max/(1+RAND_MAX),即k表示max是(1+RAND_MAX)的倍数向下取整,前面的式子变为(random(k) * (1+RAND_MAX)+ rand())。这里为了方便说明,我们假设rand()生成[0,4]的整数(即RAND_MAX=4),max=52,于是k=52/(1+4)=10,相当于把[0,52]分成10个区间,根据定义random(10)生成的是[0,10]的整数,random(10)*5相当于随机的定位到每个区间的开始位置,再加上一个rand(),即在左端点上加一个随机的偏移量构成一个新的随机数,而10依然大于4,需要用同样的方法生成random(10)。可以结合图来理解:
最后还需要将(random(10) * 5+ rand())对53模余,因为两个相加的结果可能会大于52,模余53可以保证生成的数在[0,52]范围内,注意是模余53不是52。
有了上面的方法我们可以进一步改进,生成任意区间上的随机整数,代码如下:
/*******根据c语言提供的rand()生成long型的任何区间随机整数值*******/ long random(long min,long max){ if(max <= RAND_MAX) return min + (rand()%(max - min + 1)); else return min + ((random(max/(1+RAND_MAX)) * (1+RAND_MAX) + rand()) % (max-min+1)); }
我们来测试一下区间随机数:
void testRandom(){ long min = 234567; const short num = 9; long count[num+1]; for(short i=0;i<=num;i++){ count[i]=0; } long testTimes = 10000000;//试验次数 for(long i=0;i<testTimes;i++) count[random(min,min+num)-min]++;//计数器 for(short i=0;i<=num;i++) printf("%d:\t%lf\n",i+min,1.0*count[i]/testTimes);//输出概率 }
生成了[234567,234567+9]之间的10个数,运行的结果如下:
每个数出现的概率非常接近1/10=0.1,可以认为是等概率生成。
前面我们说过,模余可以保证生成的数不超过范围内,但会造成概率不相等的情况,特别是在用大的rand()生成小的随机数时,我们测试一下,用rand7()构造rand4(),rand7()生成[0,7]的整数,rand4()生成[0,4]的整数,看如下代码:
int rand7(){//生成0到7的整数 return rand()%8; } int random4(){//生成0到4的整数 return rand7() % 5; } void main(){ const int max = 5; int count[max]; for(short i=0;i<max;i++) count[i] = 0; long num=10000000; for(long i=0;i<num;i++){ count[random4()]++;//计数 } for(short i=0;i<max;i++) printf("%d:\t%lf\n",i,1.0*count[i]/num);//输出概率 }
输出的结果如下:
我们可以看到进行1000万次试验,生成0、1、2的概率约为0.25=1/4,而3、4的概率约为0.125=1/8,这两组之间的概率相差一倍,为什么会这样?稍加分析,我们就知道了,rand7()生成的数为[0,1,2,3,4,5,6,7],且是等概率的生成这8个数,模余5后的结果为[0,1,2,3,4,0,1,2],所以rand4()中的0,1,2即有可能来自[0,1,2,3,4,5,6,7]中的0,1,2,也可能来自5,6,7,而rand4的3,4只能由rand7()的3,4而来。所以会出现0,1,2的概率是3,4概率的两倍,一般的测试实验可以不用考虑,在需要严格的随机测试实验时,应当注意这个问题。用到模余的地方都有可能造成概率不等的情况,要想写一个通用的概率相等的random(m,n)是一个数学难题。
挂羊头卖狗肉了,这篇主要讲述随机数的一些东西,也牵扯到了些位图。
相关推荐
在这个“数组应用——随机数生成器”的主题中,我们将深入探讨如何利用数组来处理随机数,并找到其中的最大值及其索引。这个任务涉及到两个主要的知识点:随机数生成和数组操作。 首先,随机数生成是计算机科学中一...
这是老师做过的一个项目,抽取了当中核心之一的内容给大家试试,有些难度。 1、实验目的 1)掌握C#命令行参数的接收; 2)掌握C#中的泛型用法; 3)掌握C#的基本流程语句; 4)掌握C#的随机数生成; 5)了解C#...
很不错的一随机数产生器,自主研发!自己做的,刚学不究,请见谅!
【第11章 抽奖——随机数与枚举】 在编程中,模拟抽奖系统往往涉及到随机数的生成和特定逻辑的实现。本章主要介绍了如何利用Java中的随机数类`Random`,以及如何运用枚举类型来创建更加规范化的抽奖程序。同时,还...
本实例008——产生随机数,旨在探讨如何在不同的编程语言中生成随机数,以及随机数生成器的基本原理和使用技巧。 首先,我们要理解随机数的定义:它是一个在一定范围内无法预测的数值。在计算机科学中,虽然数字是...
C扩展允许更直接地控制随机数生成,例如使用`srand()`和`rand()`函数。然而,如上所述,在并发环境中,如果多个进程在同一时刻调用`srand()`并传递相同的种子(如当前时间),它们会生成相同的随机序列。因此,需要...
"深入理解随机数的产生——用C语言产生随机数rand()" 随机数是一种非常重要的概念,在计算机科学中有着广泛的应用。C语言中提供了rand()函数来产生伪随机数,但这并不是真正的随机数,而是一个根据种子值递推公式...
在本文中,我们将深入探讨如何在基于uCOS-II实时操作系统上的Mini2440开发板上实现一个随机数显示的应用程序。这个应用不仅会在LCD指定区域内动态显示随机数字,还会实时监控系统性能,如任务总数、CPU使用率,并...
在ACM竞赛或算法编程中,字符串处理和随机数生成是常见的任务,因此了解这些基础知识至关重要。下面将详细解释上述提到的一些字符串处理函数和随机数的生成方法。 首先,`memset`函数用于将内存区域初始化为特定值...
随机数随机数随机数随机数随机数随机数随机数随机数
国密随机数检测工具,随机数检测
c语言随机数c语言随机数
本项目利用VHDL来实现一个简单的随机数生成器,该生成器在按下按键后,能够在0到99之间产生随机数,并通过电路中的二极管显示最后两位数字。以下将详细讲解VHDL在实现这一功能时涉及的关键知识点。 1. **VHDL语法与...
在IT行业中,随机数生成是许多算法和应用的基础,如模拟、加密、游戏开发等。在C#编程语言中,生成随机数的过程涉及到System.Random类的使用。本项目"随机数自检-扑克检测"旨在通过一个实际的扑克牌检测案例来验证...
该实验报告涉及的是数字电子技术中的随机数生成电路设计,主要使用VHDL语言进行描述。下面是关于这个实验的关键知识点: 1. **随机数生成电路**:实验要求设计一个电路,能够每2秒生成一个0到999之间的随机数,并在...
1. 设计并实现一个随机数生成电路,每2秒随机生成一个0~999之间的数字,并在数码管上显示生成的随机数。2. 为系统设置一个复位键,复位后数码管显示“000”,2秒后再开始每2秒生成并显示随机数,要求使用按键复位。
总结起来,通过MFC在VC++中实现生成随机数的功能,你需要创建一个MFC对话框应用程序,添加一个按钮控件,处理按钮点击事件,然后在事件处理函数中使用C++的标准库生成随机数。这个过程涉及了MFC的消息映射、控件交互...
对于扩展`Random`类以生成不重复随机数,我们可以使用一个集合来跟踪已生成的随机数,确保不会有重复。这里可能需要一个自定义方法,如`GetUniqueRandomNumbers(int count)`,它接受一个整数参数`count`,表示需要...