热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C/C++编程笔记:C语言rand()随机函数,深入解析程序随机数!

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

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

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#includevoid main(){for(int i&#61;0;i<10;i&#43;&#43;)printf("%d\n",rand());}

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

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

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

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

#include#include#includevoid main(){srand((int)time(0));for(int x&#61;0;x<10;x&#43;&#43;)printf("%d\n",(rand());}

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

#include#include#include#includevoid main(void){int i;unsigned int seedVal;struct timeb timeBuf;ftime(&timeBuf);seedVal&#61;((((unsigned int)timeBuf.time&0xFFFF)&#43;(unsigned int)timeBuf.millitm)^(unsigned int)timeBuf.millitm);srand((unsigned int)seedVal);for(i&#61;0;i<10;&#43;&#43;i)printf("%6d\n",rand());}

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

看下面代码&#xff1a;

void main(){for(int i&#61;0;i<100000;i&#43;&#43;){srand( (unsigned)time( NULL ) );printf&#xff08;"%d\n",rand());}}

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

srand( (unsigned)time( NULL ) );

for(int i&#61;0;i<100000;i&#43;&#43;)

问题解决了&#xff1a;&#xff09;

既然生成的是

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

rand()/(double)RAND_MAX*x &#xff0c;要生成x-y之间&#xff08;包括x不包括y&#xff09;的就是 

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

#include#include#includevoid main(){srand((int)time(0));for(int x&#61;0;x<10;x&#43;&#43;)printf("%d\n",(rand()%10);}

x-y的就是 rand()%(y-x)&#43;x

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

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

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

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

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

算法一&#xff08;1&#xff09;

srand((unsigned)time(NULL));for(j &#61; 0; j

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

算法一&#xff08;2&#xff09;

srand((unsigned)time(NULL));j&#61;0;while (j{a[j] &#61; rand() % n ;for(i&#61;0;iif(ij&#43;&#43;}

 但是后来都建议用for循环&#xff0c;这样可以使循环条件跟紧凑&#xff1a;

算法一&#xff08;3&#xff09;

srand((unsigned)time(NULL));for(j&#61;0;j

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

算法二&#xff08;1&#xff09;

for (i&#61;0;i

b[]是生成的随机数列

 这样做也太麻烦了&#xff0c;生成的直接做个标记不就行了&#xff1f;

算法三&#xff08;1-1&#xff09;

i&#61;rand()%m;a[i]&#61;1;b[0]&#61;i;for(j&#61;1;j

写紧凑一些吧&#xff0c;直接&#xff1a;

算法三&#xff08;1-2&#xff09;

for(j&#61;0;j

但是我看到了更简洁的&#xff1a;

int n&#61;某值&#xff1b;int a[n]&#61;{0}&#xff1b;for (i&#61;1;i<&#61;n;i&#43;&#43;){while(a[x&#61;rand()%n]);a[x]&#61;i;}

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

int n&#61;某值&#xff0c;m&#61;某值&#xff1b;int a[n]&#61;{0},b[m]for (i&#61;1;i<&#61;n;i&#43;&#43;){while(a[x&#61;rand()%n]);b[i]&#61;x;a[x]&#61;1;}

但是总叫人不舒服&#xff0c;对这种算法谁有更好的写法呢&#xff1f;

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

这都不太让人满意么&#xff1f;看看下面这个&#xff1a;

算法四&#xff08;1&#xff09;

for (i&#61;0;i0;i--){w&#61;rand()%i;t&#61;a[i];a[i]&#61;a[w];a[w]&#61;t;}

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

但这样会生成n个小于n的随机数&#xff0c;我们只要m个&#xff0c;再加两句&#xff1a;

for(i&#61;0;i

 b[i]&#61;a[i];

b[]是生成的随机数列

 如果m和n很接近的话或者相等是最好&#xff0c;但可能不是……那改一下&#xff1a;

算法四&#xff08;2&#xff09;

for (i&#61;0;i

但是条件反过来了&#xff0c;如果m远小于n还行&#xff0c;如果接近&#xff0c;其随机性就存在问题了&#xff08;为什么&#xff1f;&#xff09;&#xff0c;再改一下&#xff1a;

算法四&#xff08;3&#xff09;

for (i&#61;0;i

这样可以万无一失了……

希望对大家有帮助&#xff01;

自学C/C&#43;&#43;编程难度很大&#xff0c;不妨和一些志同道合的小伙伴一起学习成长&#xff01;

C语言C&#43;&#43;编程学习交流圈子&#xff0c;【点击进入】微信公众号&#xff1a;C语言编程学习基地

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


推荐阅读
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 第四讲ApacheLAMP服务器基本配置Apache的编译安装从Apache的官方网站下载源码包:http:httpd.apache.orgdownload.cgi今 ... [详细]
  • 原文地址http://balau82.wordpress.com/2010/02/28/hello-world-for-bare-metal-arm-using-qemu/最开始时 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • 本文介绍了在多平台下进行条件编译的必要性,以及具体的实现方法。通过示例代码展示了如何使用条件编译来实现不同平台的功能。最后总结了只要接口相同,不同平台下的编译运行结果也会相同。 ... [详细]
  • 3.223.28周学习总结中的贪心作业收获及困惑
    本文是对3.223.28周学习总结中的贪心作业进行总结,作者在解题过程中参考了他人的代码,但前提是要先理解题目并有解题思路。作者分享了自己在贪心作业中的收获,同时提到了一道让他困惑的题目,即input details部分引发的疑惑。 ... [详细]
  • 本文介绍了前端人员必须知道的三个问题,即前端都做哪些事、前端都需要哪些技术,以及前端的发展阶段。初级阶段包括HTML、CSS、JavaScript和jQuery的基础知识。进阶阶段涵盖了面向对象编程、响应式设计、Ajax、HTML5等新兴技术。高级阶段包括架构基础、模块化开发、预编译和前沿规范等内容。此外,还介绍了一些后端服务,如Node.js。 ... [详细]
  • Java SE从入门到放弃(三)的逻辑运算符详解
    本文详细介绍了Java SE中的逻辑运算符,包括逻辑运算符的操作和运算结果,以及与运算符的不同之处。通过代码演示,展示了逻辑运算符的使用方法和注意事项。文章以Java SE从入门到放弃(三)为背景,对逻辑运算符进行了深入的解析。 ... [详细]
  • 颜色迁移(reinhard VS welsh)
    不要谈什么天分,运气,你需要的是一个截稿日,以及一个不交稿就能打爆你狗头的人,然后你就会被自己的才华吓到。------ ... [详细]
  • Linux下C语言的几道经典面试题小结(分享)
    本篇文章整理了几道Linux下C语言的经典面试题,相信对大家更好的理解Linux下的C语言会有很大的帮助,欢迎大家探讨指正。1、如果在Linux下使用GCC ... [详细]
  • 一、死锁现象与递归锁进程也是有死锁的所谓死锁:是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作 ... [详细]
  • linux 字符串数组初始化,C++字符数组初始化方法的分析
    发现了一个字符数组初始化的误区,而这个往往能导致比较严重的性能问题,分析介绍如下:往往我们在初始化一个字符数组,大概有如下几 ... [详细]
  • c语言翁恺28讲,翁恺的c语言视频怎么样
    本文目录一览:1、翁凯的c语言视频多长时间2、 ... [详细]
  • golang源码分析调度概述
    golang源码分析-调度过程概述本文主要概述一下golang的调度器的大概工作的流程,众所周知golang是基于用户态的协程的调度来完成多任务的执行。在Linux ... [详细]
author-avatar
快乐皇妃800
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有