C语言——rand函数

2024-05-05 06:52
文章标签 语言 rand 函数

本文主要是介绍C语言——rand函数,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、rand函数

这是一个在 C 标准库 <stdlib.h> 中定义的函数,用于生成伪随机数,默认情况下,它生成从 0 到 RAND_MAX 的伪随机数,其中 RAND_MAX 是一个常数,通常是 32767

1、函数原型:

2、函数返回值:

返回产生的从 0 到 RAND_MAX 的伪随机数。

3、使用示例:

在使用 rand 函数时要配合 srand 函数设置 rand 函数的随机种子。

1)产生[0,n]的整数(n<RAND_MAX):

	rand() % (n + 1);

例如产生[0,100]的整数:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main()
{srand((unsigned int)time(NULL));int n = 100;int random_number = rand() % (n + 1); // 生成 [0, n] 范围内的随机数printf("Random Number between 0 and %d: %d\n", n, random_number);return 0;
}

2)产生[n,m]的整数(m<RAND_MAX,n>0):

    int random_number = rand() % (n - m + 1) + m; // 生成 [m, n] 范围内的随机数
  1. 模运算rand() % (n - m + 1) 的作用是确定 rand() 生成的随机数范围在 [0, n-m] 内。

  2. m:最后通过加上 m,将 [0, n-m] 的范围调整到 [m, n]。这样做的目的是将原来的范围平移,使得最小值从 0 变为 m

例如产生[5,10]的整数:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main()
{srand((unsigned int)time(NULL));int m = 5;int n = 10;int random_number = rand() % (n - m + 1) + m; // 生成 [m, n] 范围内的随机数printf("Random Number between %d and %d: %d\n", m, n, random_number);return 0;
}

3)产生[n.m,a.b]的浮点数(n.m>0,a.b<RAND_MAX):

		printf("%.1lf ",((double)rand() / RAND_MAX) * (a_b - n_m) + n_m);
  1. (double)将rand产生的随机数强制转换为浮点数,这样在除以RAND_MAX时可以是浮点数除法,而不是整数除法。
  2. ((double)rand() / RAND_MAX)可以产生[0,1]的浮点数,然后乘以 (a_b - n_m) 来确定步长。
  3. + n_m 来确定范围。
  4. 打印时可以决定是几位浮点数。

例如产生[1.0,10.0]的浮点数:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main()
{srand((unsigned int)time(NULL));double n_m = 1.0;double a_b = 10.0;double random_number = ((double)rand() / RAND_MAX) * (a_b - n_m) + n_m;printf("Random float number between %.1lf and %.1lf is: %.1lf\n", n_m, a_b, random_number);return 0;
}

4、srand的设置

1)srand函数使用注意

srand的函数原型:

如果你尝试多次运行下面的代码:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main()
{printf("%d ", rand() % 100);return 0;
}

你会发现每次运行的结果都是一样的,这是因为什么呢?

实际上每次调用 rand() 函数之前,如果没有显式调用 srand(seed) 来设置种子值,系统会自动将种子值设为 1作为默认的初始种子。实际上作为全局静态变量,seed实际上在一般的实现上实在声明时就默认初始化为了1。

所以每次运行程序时,种子都会重置为1,所以每次重新运行这个程序产生的值是同一个。

这里的种子设置为一样的,产生的就是一样的,又是为什么呢?

主要是这里的rand的内部实现是使用了线性同余法产生随机数。

线性同余生成器(Linear Congruential Generator, LCG)是一种产生伪随机数的算法,其数学公式通常表示为:

X_{n+1} = (aX_n + c) \mod m

  • X 是序列中的数值。
  • a 是乘数(multiplier)。
  • c 是增量(increment)。
  • m 是模数(modulus)。
  • X_{n+1} 是下一个产生的随机数。
  • X_n 是当前的随机数(或种子)。

在这个公式中,种子 X_0 (( n = 0 ) 时的 ( X ))是用来启动随机数序列的初始值,而 srand 函数设置的就是这个种子值。

当你在 C 语言中调用 srand 函数并传递一个种子值时,你设置了序列的起始点 X_0。这个种子值会影响到随机数生成的整个序列,因为它是计算下一个随机值时用到的当前随机数 X_n。也就是说当前产生的随机数 X_n 是作为下一个随机数 X_{n+1} 产生的种子的。

在上面的例子中,种子在程序不断重新运行时不断被重置为1,而且程序只产生一个随机数,所以产生的这个唯一的随机数的种子一直是1,又因为程序中的乘数、增量和模数都是设置好的,所以这个随机数就只受种子的影响了,种子一直是同一个,所以产生的这个随机数也一直是同一个。

下面的程序与这个原理也很相似:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>int main()
{int n = 1000;while (n--){srand((unsigned int)time(NULL));printf("%d ", rand() % 100);}return 0;
}

你会发现产生的1000个随机数全部都是一样的,这也是因为每次循环迭代时都调用 srand((unsigned int)time(NULL)) 来重新设置随机数生成器的种子。由于 time(NULL) 在很短的时间内(例如,一秒内,当然这里程序的运行是毫秒级的)返回的值是相同的,因此在这样一个快速的循环中,srand 一直被设置为相同的种子值。这会导致 rand() 在每次迭代中产生相同的随机数序列。

因此,正确的做法是在程序的开始处(或者在生成随机数之前)只调用一次 srand,使用一个种子(如当前时间),这样可以在整个程序的生命周期内生成不同的随机数。

2)线性同余法优缺点:

线性同余法的优点是简单易实现,计算效率高。但是,它也存在一些缺点。首先,生成的随机数序列可能不够随机,会显示出一些模式,尤其是在低维度的测试中可能会被检测出来。其次,如果参数选择不当,可能会导致随机数的周期过短,使得生成的随机数序列在较短的时间内重复。因此,在实际应用中,需要仔细选择参数,或者使用更复杂的随机数生成算法来获得更好的随机性和周期性。

二、rand函数和srand函数的具体实现

#include <stdio.h>
#include <stdlib.h>static unsigned long next = 1;/* RAND_MAX assumed to be 32767 */
int myrand(void) {next = next * 1103515245 + 12345;return (unsigned int)(next/65536) % 32768;
}void mysrand(unsigned int seed) {next = seed;
}int main(void) {mysrand(1234);printf("Random number: %d\n", myrand());return 0;
}

这里的这一语句:

static unsigned long next = 1;

印证了我们之前的话,如果没有显式调用srand函数,那么种子就默认是1,可以发现这里的srand函数的功能就是将我们设置的随机种子赋值给这个全局变量next,然而如果我们没有调用srand函数,next变量从最开始就是1,也就不会改变。

实际上这里的这个next,或者说每次调用srand函数中的next就相当于上面公式里的X_0

    next = next * 1103515245 + 12345;

这里的两个数字(1103515245)是乘数和(12345)是增量,不同编译器的具体数据可能不一样,

    return (unsigned int)(next/65536) % 32768;

这里的模数是 32768 。

对于srand的实现就一个功能:

void mysrand(unsigned int seed) {next = seed;
}

将我们传入的种子参数作为产生下一个随机数的种子。

这篇关于C语言——rand函数的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/961054

相关文章

Python中help()和dir()函数的使用

《Python中help()和dir()函数的使用》我们经常需要查看某个对象(如模块、类、函数等)的属性和方法,Python提供了两个内置函数help()和dir(),它们可以帮助我们快速了解代... 目录1. 引言2. help() 函数2.1 作用2.2 使用方法2.3 示例(1) 查看内置函数的帮助(

C++ 函数 strftime 和时间格式示例详解

《C++函数strftime和时间格式示例详解》strftime是C/C++标准库中用于格式化日期和时间的函数,定义在ctime头文件中,它将tm结构体中的时间信息转换为指定格式的字符串,是处理... 目录C++ 函数 strftipythonme 详解一、函数原型二、功能描述三、格式字符串说明四、返回值五

Go语言中泄漏缓冲区的问题解决

《Go语言中泄漏缓冲区的问题解决》缓冲区是一种常见的数据结构,常被用于在不同的并发单元之间传递数据,然而,若缓冲区使用不当,就可能引发泄漏缓冲区问题,本文就来介绍一下问题的解决,感兴趣的可以了解一下... 目录引言泄漏缓冲区的基本概念代码示例:泄漏缓冲区的产生项目场景:Web 服务器中的请求缓冲场景描述代码

Go语言如何判断两张图片的相似度

《Go语言如何判断两张图片的相似度》这篇文章主要为大家详细介绍了Go语言如何中实现判断两张图片的相似度的两种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 在介绍技术细节前,我们先来看看图片对比在哪些场景下可以用得到:图片去重:自动删除重复图片,为存储空间"瘦身"。想象你是一个

Go语言中Recover机制的使用

《Go语言中Recover机制的使用》Go语言的recover机制通过defer函数捕获panic,实现异常恢复与程序稳定性,具有一定的参考价值,感兴趣的可以了解一下... 目录引言Recover 的基本概念基本代码示例简单的 Recover 示例嵌套函数中的 Recover项目场景中的应用Web 服务器中

Python中bisect_left 函数实现高效插入与有序列表管理

《Python中bisect_left函数实现高效插入与有序列表管理》Python的bisect_left函数通过二分查找高效定位有序列表插入位置,与bisect_right的区别在于处理重复元素时... 目录一、bisect_left 基本介绍1.1 函数定义1.2 核心功能二、bisect_left 与

java中BigDecimal里面的subtract函数介绍及实现方法

《java中BigDecimal里面的subtract函数介绍及实现方法》在Java中实现减法操作需要根据数据类型选择不同方法,主要分为数值型减法和字符串减法两种场景,本文给大家介绍java中BigD... 目录Java中BigDecimal里面的subtract函数的意思?一、数值型减法(高精度计算)1.

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

Python函数返回多个值的多种方法小结

《Python函数返回多个值的多种方法小结》在Python中,函数通常用于封装一段代码,使其可以重复调用,有时,我们希望一个函数能够返回多个值,Python提供了几种不同的方法来实现这一点,需要的朋友... 目录一、使用元组(Tuple):二、使用列表(list)三、使用字典(Dictionary)四、 使