一 说起随机函数,恐怕又有人说这是老生长谈了……一般很多人都形成了自己的固定格式,因为随机数用处比较大,用的时候比较多,拿过来就用了。但是新手不这么 干,他们总是抱有疑惑,我就是一个新手,而且较菜……为了让跟我一样的菜鸟看明白,我会尽量的说得让高手们不屑一顾(:由于可能内容太多可能会分篇,大家 见谅^

计算机的好处是精确,所以它不擅长模拟信号,但它的缺点也是 如此。于是在一些模拟问题上计算机遇到麻烦了……比如所随机数,因为函数嘛,总会是确定的,确定的算法就会生成确定的结果。各种编程语言返回的随机数(确 切地说是伪随机数)实际上都是根据递推公式计算的一组数值,当序列足够长,这组数值近似满足均匀分布。c的标准函数库提供一随机数生成器rand(定义在 stdlib.h),能返回0-RAND_MAX之间均匀分布的伪随机整数(RAND_MAX至少为32767,一般都默认为32767)。

例如:

#include<stdio.h>
#include<stdlib.h>
void main()
{
       for(int i=0;i<10;i+)
             printf("%d\n",rand());
}


果我们是第一次运行,而且对其不太清楚,那么它生成的基本上算是0-RAND_MAX之间的等概率随机数列了。但是如果你第二次运行的时候会发现输出结果
仍和第一次一样。:(原来rand()生成伪随机数时需要一个种子(计算伪随机序列的初始数值)才行,如果种子相同就会得到相同的序列结果(这就是函数的
好处T-T)。这个“优点”被有的软件利用于加密和解密。加密时,可以用某个种子数生成一个伪随机序列并对数据进行处理;解密时,再利用种子数生成一个伪
随机序列并对加密数据进行还原。这样,对于不知道种子数的人要想解密就需要多费些事了。当然,这种完全相同的序列对于你来说是非常糟糕的。要解决这个问
题,需要在每次产生随机序列前,先指定不同的种子,这样计算出来的随机序列就不会完全相同了。

srand()
用来设置rand()产生随机数时的随机数种子。在调用rand()函数产生随机数前,必须先利用srand()设好随机数种子(seed),
如果未设随机数种子,
rand()在调用时会自动设随机数种子为1(有人说默认是0,困惑中)。上面的两个例子就是因为没有设置随机数种子,每次随机数种子都自动设成相同值1
,进而导致rand()所产生的随机数值都一样。(可能有人知道C语言中的随机函数random,可是random函数并不是ANSI
C标准,所以说,random函数不能在gcc,vc等编译器下编译通过。我们可以自己编一个^0^)我们需要使程序每一次使用的种子都不一样,现在主要
问题是种子srand的选择是不是接近随机(不存在完全随机),你也可以人为指定种子数。Windows
9x/NT的游戏FreeCell就允许用户指定种子数,这样用户如果一次游戏没有成功,下次还可以以同样的发牌结果再玩一次。但我们还是喜欢系统自动生
成……最简单的方法就是利用系统时间了(几乎所有的人都这么做),因为时间的数值随时间变化而变化,运行两次,一般不会出现前一次和后一次相同的局
面,time(NULL)会返回一个表示当前系统时间的整数(它在time.h中,据说time()函数求出来的是自1970年1月1日到现在的秒数,有
的说是unix元年,不知道这两个是不是一天……:( ,另外有的嫌麻烦会写作time(0))。我们这么来写:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

void main()
{

srand((int)time(0));
     for(int x=0;x<10;x++)
           printf("%d\n",(rand());
}

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

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/timeb.h>

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("%6d\n",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("%d\n",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<stdio.h>
#include<stdlib.h>
#include<time.h>

void main()
{

srand((int)time(0));
     for(int x=0;x<10;x++)
           printf("%d\n",(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<j;i++){if(a[i]==a[j])goto a;}


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

算法一(2)

srand((unsigned)time(NULL));

j=0; 
while (j<m) 

a[j] = rand() % n ; 
for(i=0;i<j;i++){if(a[i]==a[j]) break; } 
if(i<j)continue;

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语言生成不重复的随机数的更多相关文章

  1. C#产生不重复的随机数并生成随机文件名

    本文转自:http://blog.ciznx.com/post/csharprandomnumberandrandomfilename.aspx 在项目中会遇到需要批量生成文件的时候,比如 asp.n ...

  2. JavaScript 不重复的随机数

    在 JavaScript 中,一般产生的随机数会重复,但是有时我们需要不重复的随机数,如何实现?本文给于解决方法,需要的朋友可以参考下     在 JavaScript 中,一般产生的随机数会重复,但 ...

  3. [转载][记录]javascript生成不重复的随机数

    参考链接:javascript生成不重复的随机数 项目播放视频,是无序的,有上下两个按钮,所以需要生成1,8不重复的随机数数组,如: ,,,,,,, 然后再split一次,就是数组了. 拿来主义了

  4. 生成N个不重复的随机数(转)

    有25幅作品拿去投票,一次投票需要选16幅,单个作品一次投票只能选择一次.前面有个程序员捅了漏子,忘了把投票入库,有200个用户产生的投票序列为空.那么你会如何填补这个漏子? 当然向上级反映情况.但是 ...

  5. 论 Java 中获取一组不重复的随机数之性能问题

    今天在做一个Java项目, 用到了使用一组不重复的随机数问题, 不管怎么做随机数里面总有几个是重复的. 于是上网去找资料, 在网上找到的资料中大部分都是一个思路: 网上的思路一:(性能不理想) 先生成 ...

  6. js生成1-100不重复的随机数及生成10个1-100不重复的随机数

    //生成1-100不重复的随机数 var count=100; var a=new Array(); for(var i=0;i<100;i++){ a[i]=i+1; } a.sort(fun ...

  7. 递归函数获得n个不重复的随机数

    // 递归函数获取不重复的随机数 var arr_end; // 用于保存最后得到的数组 function suiji(n) { var arr = [] // 产生n个随机数加入数组 for(var ...

  8. java,url长链接生成短链接,短链接生成器,自定义字符串,对字符串md5混合KEY加密,根据短链接获得key值,不重复的随机数,不重复的随机字符串

    java,url长链接生成短链接,短链接生成器,自定义字符串,对字符串md5混合KEY加密,根据短链接获得key值,不重复的随机数,不重复的随机字符串 package com.zdz.test; im ...

  9. WPF产生不重复的随机数

    WPF产生不重复的随机数 在给定的区间范围(比如[50,99]),产生给定数量的随机数(不如10个),要求产生的随机数不重复首先给定变量min=50;max=99+1;//习惯与程序规定的编码不同所致 ...

随机推荐

  1. [置顶] SOLR 4.4 部署

    SOLR 4.4 部署 前言:近期研究下solr4.4的部署,一下是部署步骤,与大家分享下. 下载solr4.4.0.zip 地址        http://mirror.esocc.com/apa ...

  2. HDU 1498 50 years, 50 colors

    题目大意:给你一个 n*n 的矩阵,每个格子上对应着相应颜色的气球,每次你可以选择一行或一列的同种颜色的气球进行踩破,问你在K次这样的操作后,哪些颜色的气球是不可能被踩破完的. 题解:对于每一种颜色建 ...

  3. 【opengl】OpenGL中三维物体显示在二维屏幕上显示的变换过程

    转自:http://blog.sina.com.cn/s/blog_957b9fdb0100zesv.html 为了说明在三维物体到二维图象之间,需要经过什么样的变换,我们引入了相机(Camera)模 ...

  4. android 之Fragment(官网资料翻译)

    原文地址: http://blog.csdn.net/lilu_leo/article/details/7671533 ***************************  正文分割线 ***** ...

  5. pyrailgun 0.24 : Python Package Index

    pyrailgun 0.24 : Python Package Index pyrailgun 0.24 Download pyrailgun-0.24.zip Fast Crawler For Py ...

  6. Fractal(递归,好题)

    Fractal Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 8341   Accepted: 3965 Descripti ...

  7. SQL Server(SSIS package) call .net DLL

    There are two method to call .net DLL in SQLSERVER. The first one is to use the sql clr but it has a ...

  8. BCS--设置BDC元数据存储权限--访问被业务数据拒绝

    设置元数据存储权限 http://blog.163.com/liangshan_wei@126/blog/static/8297850320139126930290/

  9. xocde7下导入libsqlite3.tbd编译报错的解决办法

    在xocde7下没有libsqlite3.dylib,只有libsqlite3.tbd,然后我导入了tbd.编译报错error: /Applications/Xcode.app/Contents/De ...

  10. 使用autoCompleteTextView以及MultiAutoCompleteTextView实现自动匹配输入内容

    一:autoCompleteTextView 1:控件属性设置: 注意添加:android:completionThreshold="1"来设置输入几个字符的时候开始显示匹配的内容 ...