各种编程语言返回的随机数(确切地说是伪随机数)实际上都是根据递推公式计算的一组数值,当序列足够长,这组数值近似满足均匀分布。

C的标准函数库提供一随机数生成器rand(定义在stdlib.h),能返回0~RAND_MAX之间均匀分布的伪随机整数(RAND_MAX至少为32767,一般都默认为32767)。

用rand()随机生成在[x,y]内的整数

int k;

k=x+rand()%(y-x+1),k即为所求范围内随机生成的数,rand()%a的结果最大为a-1.

rand( )%20的意思的生成20以内的随机数。

例如:

#include
#include
void main()
{
for(int i=0;i<10;i++)
printf("%dn",rand());
}

如果我们是第一次运行,而且对其不太清楚,那么它生成的基本上算是0-RAND_MAX之间的等概率随机数列了。但是如果你第二次运行的时候会发现输出结果仍和第一次一样。

原来rand()生成伪随机数时需要一个种子(计算伪随机序列的初始数值)才行,如果种子相同就会得到相同的序列结果(这就是函数的好处T-T)。这个“优点”被有的软件利用于加密和解密。加密时,可以用某个种子数生成一个伪随机序列并对数据进行处理;解密时,再利用种子数生成一个伪随机序列并对加密数据进行还原。这样,对于不知道种子数的人要想解密就需要多费些事了。当然,这种完全相同的序列对于你来说是非常糟糕的。要解决这个问题,需要在每次产生随机序列前,先指定不同的种子,这样计算出来的随机序列就不会完全相同了。

srand()用来设置rand()产生随机数时的随机数种子。在调用rand()函数产生随机数前,必须先利用srand()设好随机数种子seed,如果未设随机数种子,rand()在调用时会自动设随机数种子为1(有人说默认是0,困惑中)。上面的两个例子就是因为没有设置随机数种子,每次随机数种子都自动设成相同值1,进而导致rand()所产生的随机数值都一样。(可能有人知道C语言中的随机函数random,可是random函数并不是ANSIC标准,所以说,random函数不能在gcc,vc等编译器下编译通过。我们可以自己编一个^0^)我们需要使程序每一次使用的种子都不一样,现在主要问题是种子srand的选择是不是接近随机(不存在完全随机),你也可以人为指定种子数。

Windows9x/NT的游戏FreeCell就允许用户指定种子数,这样用户如果一次游戏没有成功,下次还可以以同样的发牌结果再玩一次。但我们还是喜欢系统自动生成……最简单的方法就是利用系统时间了(几乎所有的人都这么做),因为时间的数值随时间变化而变化,运行两次,一般不会出现前一次和后一次相同的局面,time(NULL)会返回一个表示当前系统时间的整数(它在time.h中,据说time()函数求出来的是自1970年1月1日到现在的秒数,有的说是unix元年,不知道这两个是不是一天……:(,另外有的嫌麻烦会写作time(0))。我们这么来写:

#include
#include
#include
void main()
{
srand((int)time(0));
for(int x=0;x<10;x++)
printf("%dn",(rand());
}

据说如果软件一直开两天,种子会有1/(60*60*24)个可能会重复,虽说这对于一般人已经绝对足够了,但纵然人不舒服,于是我在网上开到有这么写的:

#include
#include
#include
#include
void main(void)
{
int i;
unsigned int seedVal;
struct timeb timeBuf;
ftime(&timeBuf);
seedVal=((((unsigned int)timeBuf.time&0xFFFF)+
(unsigned int)timeBuf.millitm)^
(unsigned int)timeBuf.millitm);
srand((unsigned int)seedVal);
for(i=0;i<10;++i)
printf("%6dn",rand());
}

“上面的程序先是调用_ftime()来检查当前时间,并把它的值存入结构成员timeBuf.time中,当前时间的值从1970年1月1日开始以秒计算。在调用了_ftime()之后,在结构timeBuf的成员millitm中还存入了当前那一秒已经度过的毫秒数,但在DOS中这个数字实际上是以百分之一秒来计算的。然后,把毫秒数和秒数相加,再和毫秒数进行异或运算。当然也可以对这两个结构成员进行更多的计算,以控制seedVal的取值范围,并进一步加强它的随机性,但上例用的逻辑运算就已经足够了。”

看下面代码:

void main()
{
for(int i=0;i<100000;i++)
{
srand( (unsigned)time( NULL ) );
printf("%dn",rand());
}
}

为什么总是生成一个数???因为此程序产生一个随机数之前,都调用一次srand,而由于计算机运行很快,所以每用time得到的时间都是一样的(time的时间精度较低,只有55ms)。这样相当于使用同一个种子产生随机序列,所以产生的随机数总是相同的。把srand放在循环外看看:

srand( (unsigned)time( NULL ) );

for(int i=0;i<100000;i++)

问题解决了:)

既然生成的是

0-RAND_MAX之间均匀分布的随机整数(我们姑且把以上方法生成的是理想的随机数吧),那么要生成0-x之间(包括0不包括x)的随机数就把rand()改成

rand()/(double)RAND_MAX*x ,要生成x-y之间(包括x不包括y)的就是

rand()/(double)RAND_MAX*(y-x)+x 了。但是如果要生0-10之间的整数很多人会这么写:

#include
#include
#include
void main()
{
srand((int)time(0));
for(int x=0;x<10;x++)
printf("%dn",(rand()%10);
}

x-y的就是 rand()%(y-x)+x

???懂一点概率的就知道这样写,会使得到的数列分布不均的,但是大家确实都喜欢这么写……因为在x的值比较小,RAND_MAX相对较大,而生成的数列有不算太大,又是用来解决精确度要求不高的问题(如一些游戏目标,传说游戏中踩地雷式的遇敌就是用rand()实现的.

当主角在地图上走的时候动不动冒出三两小兵挑衅兼找死.它的实现方式是设主角所立位置为0,主角每走一步,变量加1,当变量==随机取得的数时,小兵出现),这样写足够了……

下面再讨论生成m个小于n的不重复随机数的算法

本人认为算法结构很重要,但语句结构也很重要,所以我喜欢一个算法给出多个语句结构……

最容易想到的傻瓜算法,逐个产生这些随机数,每产生一个,都跟前面的随机数比较,如果重复,就重新产生:

算法一(1)

srand((unsigned)time(NULL));
for(j = 0; j < m; j++) {
a:a[j] = rand() % n;
for(i=0;i

很早的时候学编程都喜欢用goto语句,因为过去算法是用流程图表示的,用goto可以直接转译,而且循环语句发展不完善,仅仅是作为条件分支的一个补充,如果条件分支箭头向上就是一个循环,而且goto可以实现过去循环所不能实现的结构,形成很巧妙的循环交叉。所以过去一般认为有两种结构,顺序和分支,循环是分支的特殊情况。但是break和continue语句使这些结构实现成为可能,后来发现goto的使用会造成维护和调试的许多麻烦,所以循环逐渐代替了goto,使程序更有层次。现在编程不建议用goto了,如果用goto还会被认为编程修养不好……言归正题,把它的goto去掉:

算法一(2)

srand((unsigned)time(NULL));
j=0;
while (j
{
a[j] = rand() % n ;
for(i=0;i
if(i
j++
}

但是后来都建议用for循环,这样可以使循环条件跟紧凑:

算法一(3)

srand((unsigned)time(NULL));
for(j=0;j<m;j++)
{a[j]=rand()%n;
for(i=0;i<j;i++)
{if(a[i]==a[j])
{i=-1;a[j]=rand()%n;}
}
}

但这是个很笨的方法,且比较次数呈线性增长,越往后次数越多……另一个想法是生成一个就把它从中去掉,就可以实现不重复了:

算法二(1)

for (i=0;i<n;i++)
a[i]=i+1;
for(i=0;i<m;i++)
{j=rand()%(n-i);
b[i]=a[j];
for (k=j;k<n-i-2;k++)
a[k]=a[k+1];
}

b[]是生成的随机数列

这样做也太麻烦了,生成的直接做个标记不就行了?

算法三(1-1)

i=rand()%m;
a[i]=1;b[0]=i;
for(j=1;j<m;j++)
{for (i=rand()%m;a[i]==1;i=rand()%m);
b[j]=i;a[i]=1;
}

写紧凑一些吧,直接:

算法三(1-2)

for(j=0;j<m;j++)
{for (i=rand()%m;a[i]==1;i=rand()%m);
b[j]=i;a[i]=1;
}

但是我看到了更简洁的:

int n=某值;
int a[n]={0};
for (i=1;i<=n;i++)
{while(a[x=rand()%n]);
a[x]=i;
}

这个无循环体的while保证a[m]是初始化的0状态。这生成了n个,我们只取m个,这太浪费了,结合一下:

int n=某值,m=某值;
int a[n]={0},b[m]
for (i=1;i<=n;i++)
{while(a[x=rand()%n]);
b[i]=x;a[x]=1;
}

但是总叫人不舒服,对这种算法谁有更好的写法呢?

这种算法越到后面,遇到已使用过的元素的可能性越高,重复次数就越多,但是当m较小时还是很好的:)

这都不太让人满意么?看看下面这个:

算法四(1)

for (i=0;i<n;i++)
a[i]=i+1;
for (i=n-1;i>0;i--)
{w=rand()%i;
t=a[i];
a[i]=a[w];
a[w]=t;
}

这个算法很不错,有人会怀疑其随机性,但个人认为是没问题的,首先第二行按顺序用0到n填满整个数组;第三行,是随机产生从0到n-2个数组下标,把这个下标的元素值跟n-1下标的元素值交换,一直进行到下标为1的元素。因此它只需要遍历一次就能产生全部的随机数。

但这样会生成n个小于n的随机数,我们只要m个,再加两句:

for(i=0;i<m;i++)

b[i]=a[i];

b[]是生成的随机数列

如果m和n很接近的话或者相等是最好,但可能不是……那改一下:

算法四(2)

for (i=0;i<n;i++)
a[i]=i+1;
for (i=0;i<m;i++)
{w=rand()%n;
t=a[i];
a[i]=a[w];
a[w]=t;
}

但是条件反过来了,如果m远小于n还行,如果接近,其随机性就存在问题了(为什么?),再改一下:

算法四(3)

for (i=0;i<n;i++)
a[i]=i+1;
for (i=0;i<m;i++)
{w=rand()%(n-i)+i;
t=a[i];
a[i]=a[w];
a[w]=t;
}

这样可以万无一失了……

希望对大家有帮助!

自学C/C++编程难度很大,不妨和一些志同道合的小伙伴一起学习成长!

C语言C++编程学习交流圈子,【点击进入】微信公众号:C语言编程学习基地

有一些源码和资料分享,欢迎转行也学习编程的伙伴,和大家一起交流成长会比自己琢磨更快哦!

学习C/C++编程知识,提升C/C++编程能力,欢迎关注笔者学习专栏:

C/C++学习笔记​zhuanlan.zhihu.com

c语言求阶乘和的流程图_C/C++编程笔记:C语言 rand() 随机函数,深入解析程序随机数!...相关推荐

  1. c++语言表白超炫图形_C/C++编程笔记:浪漫流星雨表白装b程序

    #include #include//图形库:easyX201905 #include #include#define MAXSTAR 1314 #define MAXMETEOR 520 //结构体 ...

  2. C语言求阶乘案例教程

    思路分析: 1.我们先搞清楚阶乘是什么,怎么用数学符号表示出来. 我们看百度百科对阶乘的介绍. "一个正整数的阶乘是所有小于及等于该数的正整数的积,并且0的阶乘为1.自然数n的阶乘写作n!& ...

  3. C语言-求阶乘和的两种方法

    目录 方法一:递归法 方法二:循环法 fun.c文件 fun.h文件 main.c文件 方法一:递归法 /** 递归法 求阶乘和**/ long Factorial_sum_way1(int m){i ...

  4. .net调用c++方法时如何释放c++中分配的内存_C/C++编程笔记:C语言编程知识要点总结!大一C语言知识点(全)...

    一.C语言程序的构成 与C++.Java相比,C语言其实很简单,但却非常重要.因为它是C++.Java的基础.不把C语言基础打扎实,很难成为程序员高手. 1.C语言的结构 先通过一个简单的例子,把C语 ...

  5. status c语言_C/C++编程笔记:C语言编程风格个人总结,初学小白可借鉴

    总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的.首先声明,我下面提到的编程规范,是自己给自己定的,不是c语言里面规定的. 一件事情,做成和做好中间可能隔了 ...

  6. c++ 动态分配数组_C/C++编程笔记:「C语言指针」民间解读版本

    相信很多同学在初学C语言时候,都会遇到遇到一个问题:指针,永远的神! 好消息,这个痛点将会在这篇文章得到解决,妈妈再也不用担心我的学习了! 一说指针,第一反应,指针是一种数据类型. c语言学习,重点就 ...

  7. java语言与c 相比 有哪些优点_Java编程和C语言分别有哪些优势

    原标题:Java编程和C语言分别有哪些优势 在IT业和互联网的飞速发展时代,企业对程序员的需求也大量增加,但是因为国内IT人才贮备的不足.培训体系的不健全以及国内IT技术人员对其价值认识不够,造成各类 ...

  8. C语言求阶乘(附完整源码)

    求数的阶乘 求正整数的阶乘完整源码(main函数测试) 求正整数的阶乘完整源码(main函数测试) #include <stdio.h> int main() {int a[200], n ...

  9. c++for循环求最大公约数_C/C++编程笔记:C语言 for 循环精讲!实例讲解带你吃透...

    在循环入口处定义循环三要素,循环条件为真时执行循环体,先判断再循环. 语法 C++中 for 循环的语法为: for (init; condition; increment) { statement( ...

最新文章

  1. BASIS--如何删除开发用户的开发Key
  2. 委托C# 生活 实例
  3. python recv
  4. rem和em学习笔记及CSS预处理
  5. 大剑无锋之内存泄漏是什么?【面试推荐】
  6. 第29届IEEE IV 征稿启示
  7. Zookeeper C API 基本常量和结构体介绍
  8. 我五年来都没来过 我的意志力飞涨。
  9. php远程读取几行文件,PHP读取远程文件的三种方法
  10. mysql必会语法_sql语法:sql必读必会(二)mysql中的函数大全
  11. win10添加网络打印机_Mac可以访问Windows共享文件,添加网络打印机
  12. 诡异的json包含bom头
  13. 1.2.4 List.contains方法——判断列表中是否包含指定元素
  14. mysql中orderby用法_MySQL教程:OrderBy用法_MySQL
  15. Windows如何安装mysql
  16. 2012 CSP-S 初赛 答案解析
  17. 技术接受模型(TAM,Technology Acceptance Model)
  18. modelsim仿真加速注意点
  19. WiFi_Display_Spec
  20. ubuntu下tree命令的使用

热门文章

  1. 强大的Vivado IP工具——自定义IP的使用
  2. APACHE 安装出错 configure: error: Cannot use an external APR with the bundled APR-util
  3. template模板的使用方法
  4. 硬件开源需求迫切?开源笔电 Nevona 筹款金额达预设目标3倍
  5. 管好统计信息,开启SQL优化之门
  6. springmvc整合mybatis框架源码 bootstrap html5 mysql oracle maven SSM
  7. vs 2012自带打包工具进行部署安装
  8. CPLEX在Linux上的安装与配置
  9. COBOL入门到精通
  10. word2vec损失函数的数学推导和近似训练以及doc2vec的实战掉包应用