Linux下随机数生成的函数与常见方法

rand函数:
   头文件
       #include<stdlib.h>
   定义函数
       int rand(void)
   函数说明
       rand()会返回一随机数值,范围在0至RAND_MAX 间。在调用此函数产生随机数前,必须先利用srand()设好随机数种子,如果未设随机数种子,rand()在调用时会自动设随机数种子为1。关于随机数种子请参考srand()。
返回值
   返回0至RAND_MAX之间的随机数值,RAND_MAX定义在stdlib.h,其值为2147483647。
srand函数:
头文件
       #include<stdlib.h>
   定义函数
       void srand (unsigned int seed);
   函数说明
       srand()用来设置rand()产生随机数时的随机数种子。参数seed必须是个整数,通常可以利用geypid()或time(0)的返回值来当做seed。如果每次seed都设相同值,rand()所产生的随机数值每次就会一样。
用法:

要想每次运行得到的随机数不同,我们还要设置随机数种子。

#include <iostream>
#include <cstdlib>
#include <ctime>
using namespace std;
int r(int fanwei)
{
 srand((unsigned)time(NULL)); //用于保证是随机数
  return rand()%fanwei;  //用rand产生随机数并设定范围
}
int main()
{
 cout<<r(100)<<endl; //生成100以内的随机数,并显示
 return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
    int i;
    srand((int) time(0));
    for(i=0;i< 10;i++)
    {
        printf(" %d ", rand());
    }
    printf("n");
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char *argv[])
{
    int i, nu;
    srand((int) time(0));
    for(i=0;i< 10;i++)
    {
        nu = 0 + (int)( 26.0 *rand()/(RAND_MAX + 1.0));
        printf(" %d ", nu);
    }
    printf("n");
    return 0;
}

以下是不使用函数进行生成随机数

     众所周知,利用Linux下的rand函数可以生成范围在0到RAND_MAX(在stdlib.h中定义,值为2147483647)的数值,但是一般来讲,为了达到更好的随机效果,需要利用srand函数设置相应的随机种子(或者说随机数的起始值),种子相同,所产生的随机数也是相同的,因此,要想获得随机效果好的随机数,一定要保证每次的随机种子有差别。常见的可作为随机种子的有:当前时间、/dev/random或/dev/urandom文件内容以及uuid码。
         在阐述各种随机种子产生之前,有必要先介绍下srand函数,它是ISO C下的产生随机数的标准函数,BSD下与之相对应的函数是random。srand函数需要接收的参数是一个无符号整型数据。
一、当前时间:
         利用time函数每次获取当前的系统时间,并且也保证了每次的随机种子有差异,实验证明它是一种最为简介,同时也是使用最为广泛的方法,但是,针对某些特定环境,未必会产生有效的随机种子。在本篇日志中,我们试图将这三种产生随机种子的方法应用于这样一种特殊环境:在嵌入式环境下,试图在开发板启动之后、系统时间没有同步之前这段时间去产生随机种子,并检验所产生的随机种子的有效性,值得说明的是,此种开发板不会去保存系统时间,每次启动之后都会从一个默认时间开始计时。通过实验得出,当前时间运用在这种环境下完全不能达到产生随意随机数的目的,因为嵌入式开发板的特殊性,在每次开发板运行到产生随机数的程序时,此时的当前时间都是一样的,于是每次重新启动开发板所产生的随机数也是一样的。
二、/dev/random以及/dev/urandom:
         /dev/random以及/dev/urandom产生随机种子的原理是利用当前系统的熵池来计算出一定数量的随机比特,其中熵池是根据当前系统的“环境噪音”,它是由很多参数共同评估的,如内存的使用,文件使用量等等,环境噪音直接影响着所产生的随机种子的有效性。同样,若我们试图运用于前面所说的特殊环境,是否能够产生满足所需的随机种子呢?实验证明,能够保证每次开发板在每次启动之后能获取到不同的随机数。毕竟环境噪音所选取的评估参数众多,会导致所获取到的种子有很大的差异性。
         下面试图阐述一下/dev/random与/dev/urandom之间的区别。根据维基百科所述,urandom即”unlocked random”,,每次打开并读取/dev/urandom时,会从熵池中随机返回所需要的字节数,/dev/urandom的读取操作不会阻塞,因为它会重复使用熵池中的数据以产生随机数;反之/dev/random则是每次读之前去检查熵池是否为空,若为空,则需要阻塞并去更新熵池。从这点可以看出,前者不需要阻塞,但是随机效果弱于后者,因此常用于弱密码系统。

#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{

    FILE *fs_p = NULL;
    unsigned int seed = 0;
    
    fs_p = fopen ("/dev/urandom", "r");
    if (NULL == fs_p) 
    {
        printf("Can not open /dev/urandom\n");
        return -1;
    }
    else
    {
      fread(&seed, sizeof(int), 1, fs_p);  //obtain one unsigned int data 
      fclose(fs_p);
    }
   
    srand(seed);

    printf("Random numner is %u.\n", rand());
    return 0;
}
三、UUID码:      
uuid码的全程是通用唯一识别码,它是一个软件建构的标准,设置uuid码的目的是让分布式系统中的每一个元素都有唯一识别的信息,它是由32个16进制数据组成,并用”-”连接号分成五段,形成8-4-4-4-12形式的数据,理论上来讲,uuid的总数是2128。Linux下的uuid码是由内核提供的,存在/proc/sys/kernel/random/uuid文件里,从维基百科了解到,生成uuid的方法众多,至今出现了5个版本,MAC地址、DCE加密、MD5 hash、随机数机制以及SHA-1 hash(最新版本5,在RFC4122中有阐述)。既然我们已经获取到了唯一的uuid码,那如何转换成我们实际srand函数所需的无符号整数呢?
    可以采取的措施有:从/proc/sys/kernel/random/uuid文件的CRC校验码中抽取最多10位,并且小于2147483647的数据;直接抽取文件内容中的数值位,但要保证其最多10位,并且值小于2147483647.下面代码模拟了后者:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define FILENAME "/proc/sys/kernel/random/uuid"
#define MAX_SEED 10
#define UUID_LENGTH 35
#define UUID_DELIM "-"

void check_rand_number(char *seed, char *p)
{
  char *tmp_seed = "2147483647"; //MAX_RAND
  char *tmp = seed;

  //fill the new number to seed array
  *(seed + strlen(seed)) = *p;  
  if (strcmp(tmp_seed, tmp) >= 0)
  {
    return ;
  }
  else
  {
    *(seed + strlen(seed) - 1) = '\0'; 
  }
}

void parse_uuid_and_save(FILE *fs_p, char *seed)
{
  char uuid[UUID_LENGTH + 1] = {0};
  char *p = NULL;

  //UUID format like as "2946b341-55b0-44c6-9710-04c947d1e3e1"
  if (NULL != fgets(uuid, UUID_LENGTH, fs_p))
  {
     if ((p = strtok(uuid, UUID_DELIM)) != NULL)
     {
        while(*p != '\0')
        {
           if (*p >= '0' && *p <= '9')
           {
             *(seed + strlen(seed)) = *p;  
             p++;
           }
           else
           {
              p++;
           }
        }
     }
     else  
     {
       return;
     }
      
     while ( (p = strtok(NULL, UUID_DELIM)) != NULL)
     {
         while((*p != '\0') && (strlen(seed) < MAX_SEED))
         {
            if (*p >= '0' && *p <= '9')
            {
              //when add the tenth bit, should check the number must less than RAND_MAX(2147483647)
              if (strlen(seed) == (MAX_SEED - 1))
              {
                check_rand_number(seed, p);
                return;
              }
              else
              {
                *(seed + strlen(seed)) = *p;  
                p++;
              }                        
            }
            else
            {
               p++;
            }
         }
     }

  }
}

int main(int argc, char **argv)
{
    FILE *fs_p = NULL;
    char seed[MAX_SEED + 1] = {0};

    fs_p = fopen (FILENAME, "r");
    if (NULL == fs_p) 
    {
        printf("Can not open UUID file!\n");
        return -1;
    }
    else
    {
      parse_uuid_and_save(fs_p, seed);
      fclose(fs_p);
    }

    srand(strtol(seed, NULL, 10));
    printf("Random number is %d.\n", rand());

    return 0;
}


  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值