数据结构算法面试题精选及整理-随机数rand7生成rand10函数

本文介绍如何仅使用rand7()函数生成rand10()函数,且不使用循环或递归。文章提供了具体实现方法,包括生成rand2()和rand5()函数作为辅助,并通过测试验证了rand10()函数的均匀分布。

据说来自百度的一个面试题

问题其实很简单:

就是一个函数rand7()可以生成1~7的随机数,现在要求用这个随机数生成器来生成rand10()函数。

条件是:不能用循环或递归


点击打开链接里面有讨论。这里要感谢一下这个好学的同学。整理了一下,我就不再整理了。

不过这里可以提供我的思路

大致方法如下:

首先需要生成[1 2]上的随机函数_rand2()。

然后再生成[1 ,2 ,3, 4, 5]上的随机函数_rand5()。

然后用_rand2()函数来决定是选择1~5,还是6~10。想必你应该明白了。

这两个函数都可以通过打表法来生成。

/*生成[1, 2, 3, 4, 5]的方法可以按照打表法来进行
  1次 1 2 3 4 5 1 2    ; = (rand() + 4) % 5 + 1
  2次 3 4 5 1 2 3 4    ; = (rand() + 1) % 5 + 1
  3次 5 1 2 3 4 5 1    ; = (rand() + 3) % 5 + 1
  4次 2 3 4 5 1 2 3    ; = (rand() + 0) % 5 + 1
  5次 4 5 1 2 3 4 5    ; = (rand() + 2) % 5 + 1
  
  以后第6次照第一次来生成

  也可以用如下等价代码来生成。
  用同样的办法可以生成[1, 2]随机数
*/
static int _rand5()
{
	static int i = 0;
	int t = (_rand7() + i) % 5 + 1;
	i = (i + 1) % 5;
	return t;
}

static int _rand2()
{
	static int j = 0;
	int t = (_rand7() + j) % 2 + 1;
	j = !j;
	return t;
}


有了上面两个函数,那么生成[1 10]上的随机函数则比较容易:

首先调用_rand2(),看下是否等于1,如果等于1则直接返回1~5
否则返回6~10
int _rand10()
{
	int v = _rand2();
	if (1 == v)
	{
		return _rand5();
	}
	else return 5 + _rand5();
}


这里再加上测试代码
int main(void)
{
	int rec[11];
	memset(rec, 0, sizeof(rec));
	for (int i = 0; i < 10000000; ++i)
	{
		rec[_rand10()] ++;
	}
	for (int i = 1; i < 11; ++i)
	{
		printf("%.12f\n", (double)(rec[i])/10000000.0);
	}
	return 0;
}



结果输出:
0.100070700000
0.100000000000
0.100049700000
0.099718400000
0.099943200000
0.100126300000
0.099963900000
0.100145500000
0.099947300000
0.100035000000

完整代码给出如下:
#include<stdio.h>  
#include<stdlib.h>
#include<string.h>
static int _rand7()
{
	return rand() % 7 + 1;
}

/*生成[1, 2, 3, 4, 5]的方法可以按照打表法来进行
  1次 1 2 3 4 5 1 2    ; = (rand() + 4) % 5 + 1
  2次 3 4 5 1 2 3 4    ; = (rand() + 1) % 5 + 1
  3次 5 1 2 3 4 5 1    ; = (rand() + 3) % 5 + 1
  4次 2 3 4 5 1 2 3    ; = (rand() + 0) % 5 + 1
  5次 4 5 1 2 3 4 5    ; = (rand() + 2) % 5 + 1
  
  以后第6次照第一次来生成

  也可以用如下等价代码来生成。
  用同样的办法可以生成[1, 2]随机数
*/
static int _rand5()
{
	static int i = 0;
	int t = (_rand7() + i) % 5 + 1;
	i = (i + 1) % 5;
	return t;
}

static int _rand2()
{
	static int j = 0;
	int t = (_rand7() + j) % 2 + 1;
	j = !j;
	return t;
}

int _rand10()
{
	int v = _rand2();
	if (1 == v)
	{
		return _rand5();
	}
	else return 5 + _rand5();
}


int main(void)
{
	int rec[11];
	memset(rec, 0, sizeof(rec));
	for (int i = 0; i < 10000000; ++i)
	{
		rec[_rand10()] ++;
	}
	for (int i = 1; i < 11; ++i)
	{
		printf("%.12f\n", (double)(rec[i])/10000000.0);
	}
	return 0;
}


欢迎大家讨论~~

再给两个参考链接

1 点击打开链接
2点击打开链接


<think>我们已知在C语言中可以使用rand()函数生成随机数,但rand()生成的是0到RAND_MAX之间的整数(通常是0到32767)。要生成0到7之间的随机数,我们可以使用取模运算(%),因为取模8可以将随机数映射到0-7的范围。 但是需要注意的是,直接使用rand() % 8可能会因为随机数的均匀性而带来一些偏差(特别是当RAND_MAX不是8的整数倍时),但在实际应用中,如果RAND_MAX远大于8(32767远大于8),这种偏差可以忽略。 步骤: 1. 包含头文件:<stdlib.h>(用于rand()和srand())以及<time.h>(用于time()函数)。 2. 使用srand()初始化随机数种子,通常使用当前时间作为种子,这样每次运行程序时种子不同,产生的随机数序列也不同。 3. 调用rand()函数,然后对结果取模8(即rand() % 8)得到0到7之间的整数。 代码示例: ```c #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { // 初始化随机数种子 srand(time(0)); // 生成0到7随机数 int random_num = rand() % 8; printf("随机数: %d\n", random_num); return 0; } ``` 说明: - `rand() % 8` 的含义:rand()函数返回一个随机整数,除以8取余数,余数的范围是0到7- 使用`srand(time(0))`初始化种子,这样每次运行程序时种子不同,避免每次生成相同的随机数序列。 引用[2]中提到,如果没有调用srand(),系统会自动调用srand(1),这样每次程序运行都会生成相同的随机数序列。因此,为了每次运行得到不同的随机数,我们使用当前时间(time(0))作为种子。 另外,引用[4]中给出了使用rand()生成指定范围随机数的一般公式: - 生成[0, n-1]的随机数rand() % n - 生成[1, n]的随机数:1 + rand() % n - 生成[n, m]的随机数:n + rand() % (m - n + 1) 因此,生成0-7就是[0,7]的整数,使用rand() % 8即可。 但是,需要注意的是,如果对随机数的质量要求很高(比如要求严格的均匀分布),而RAND_MAX不是8的整数倍,那么取模运算会引入轻微的不均匀。例如,假设RAND_MAX=32767,那么0到32767中: - 0到32759之间的数共有4095个完整的0-7循环(4095*8=32760),然后剩下的8个数(32760到32767)会分别对应0到7(32760%8=0, 32761%8=1, ... 32767%8=7)。但是,0到7每个数出现的次数并不完全相等:0到3出现了4096次(因为32760到32763对应0到3,所以0到3多了一次),而4到7出现了4095次。不过,由于这个偏差非常小(4096次 vs 4095次),在大多数应用中是可以接受的。 如果要求严格的均匀分布,我们可以采用拒绝采样:当随机数落在会破坏均匀性的那部分(即大于等于32760)时,我们重新生成,直到落在0到32759之间,然后再取模。但通常对于0-7这样的小范围,且RAND_MAX较大的情况,这种改进并不必要。 因此,对于一般应用,直接使用`rand() % 8`即可。 相关问题: 1. 如何生成1到8之间的随机整数? 2. 如何生成0到1之间的小数随机数? 3. 如何生成不重复的随机数序列?</think>### 使用 `rand()` 函数生成 0-7 随机数的方法 在 C 语言中,使用 `rand()` 函数生成 0-7 范围内的随机数需要结合取模运算和种子初始化。以下是详细步骤和原理: #### 1. **核心公式** 生成 $[0,7]$ 范围内整数的公式为: ```c int random_num = rand() % 8; // 取模8得到0-7 ``` **原理**: - `rand()` 生成 $[0, RAND\_MAX]$ 的整数($RAND\_MAX$ 通常为 32767- 取模运算 `% 8` 将结果映射到 $[0,7]$ 的余数范围 - 数学表达:$ \text{random\_num} = \text{rand()} \mod 8 $ #### 2. **完整代码实现** ```c #include <stdio.h> #include <stdlib.h> #include <time.h> // 用于获取时间种子 int main() { // 初始化随机数种子(关键步骤!) srand(time(0)); // 使用当前时间作为种子[^2] // 生成0-7随机数 int random_num = rand() % 8; printf("随机数: %d\n", random_num); return 0; } ``` #### 3. **关键细节说明** 1. **种子初始化 (`srand()`)** - 必须调用 `srand(seed)` 初始化种子,否则每次程序运行会生成相同序列[^2] - 推荐使用 `time(0)` 获取系统时间(秒数)作为动态种子 - 替代方案:用未初始化内存值或硬件寄存器值(如定时器) 2. **范围调整原理** - 通用公式:生成 $[n,m]$ 范围的随机数: ```c int num = n + rand() % (m - n + 1); // [n,m] 闭区间 ``` - 例如生成 $[1,7]$: ```c int num = 1 + rand() % 7; // 1-7 ``` 3. **均匀性注意事项** - 当 $RAND\_MAX$ 不是 8 的倍数时,小范围偏差可能存在 - 优化方案(拒绝采样法): ```c int random_num; do { random_num = rand() & 0x07; // 直接取低3位 (0-7) } while (random_num > 7); // 理论上无需循环,此处确保正确性 ``` #### 4. **生成不重复序列** 若需生成不重复的 0-7 随机序列(如洗牌): ```c int arr[8] = {0,1,2,3,4,5,6,7}; for (int i = 7; i > 0; i--) { int j = rand() % (i + 1); // 随机索引[0,i] // 交换元素 int temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } // 此时arr包含0-7的随机排列[^3] ``` ### 常见问题解答 **Q1: 为什么每次运行都得到相同随机数?** A: 未调用 `srand()` 或使用固定种子。需用动态值如 `srand(time(0))`[^2]。 **Q2: 如何生成浮点随机数?** A: 生成 $[0,1)$ 范围小数: ```c float random_float = (float)rand() / RAND_MAX; // 0 ≤ random_float < 1 ``` **Q3: `rand()` 的随机性足够安全吗?** A: 不适用于密码学!`rand()` 是伪随机生成器,序列可预测。安全场景需用硬件随机数生成器。 --- ### 相关问题 1. 如何用 51 单片机的定时器生成随机数种子? 2. 在加密应用中应如何生成随机数? 3. `rand()` 和 `random()` 函数有何区别? 4. 如何测试随机数生成器的均匀性?
评论 9
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值