c语言随机数怎么生成(通俗易懂,附实例)

在 C 语言里,所谓随机数其实是“伪随机数”。计算机根据一个种子值,通过固定公式推导出看起来杂乱的数字序列。只要种子相同,每次运行得到的序列完全一致。这种确定性对调试有利,却让初学者误以为结果真随机。

理解“伪随机”本质后,才能正确运用随机数函数。

伪随机数的可重现性演示:

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

int main(void) {
    /* 不设置种子,默认种子为 1 */
    for (int i = 0; i < 5; ++i)
        printf("%d ", rand());  /* 多次运行结果完全相同 */
    return 0;
}

运行结果(任意机器):

41 18467 6334 26500 19169

rand()与srand()的基本用法

stdlib.h 提供两个核心函数:rand() 负责生成 0~RAND_MAX 之间的整数;srand(unsigned seed) 负责设置种子。若希望每次运行结果不同,必须把“时刻在变的值”设为种子,常用 time(NULL) 返回的秒数。注意 srand() 在整个程序中只需调用一次,重复调用反而可能削弱随机性。

设置时间种子并生成 10 个随机数:

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

int main(void) {
    srand((unsigned)time(NULL)); /* 只需调用一次 */
    for (int i = 0; i < 10; ++i)
        printf("%d ", rand());
    return 0;
}
/* 某次运行结果:
31529 15850 13549 21351 29358 30434 28276 21666 25608 26947 */

把随机数映射到任意区间

rand() 返回范围是 0~RAND_MAX,通常需要转换到更实用的区间 [min, max]。通用公式:rand() % (max - min + 1) + min。取模运算简单直观,但低位随机性可能不足;若对质量要求高,可用 (int)((double)rand() / RAND_MAX * (max - min + 1)) + min 来利用高位。

生成 [10, 99] 区间的随机两位数:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand((unsigned)time(NULL));
    for (int i = 0; i < 8; ++i) {
        int n = rand() % 90 + 10; /* 90 = 99-10+1 */
        printf("%d ", n);
    }
    return 0;
}
/* 某次运行结果:
63 57 18 90 44 81 76 35 */

生成浮点随机数

游戏或仿真常需要 0.0~1.0 之间的浮点随机数。最简单写法:double r = (double)rand() / RAND_MAX; 若需区间 [a, b],再线性变换:a + r * (b - a)。注意 RAND_MAX 通常是整数上限,强制转 double 可避免整型除法精度损失。

输出 5 个 [0, 1) 的浮点随机数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srand((unsigned)time(NULL));
    for (int i = 0; i < 5; ++i) {
        double r = (double)rand() / RAND_MAX;
        printf("%.6f ", r);
    }
    return 0;
}
/* 某次运行结果:
0.534711 0.902348 0.198074 0.956483 0.003011 */

随机数质量与可移植性

rand() 的实现依赖编译器,RAND_MAX 最小值仅 32767,周期也较短。若需加密或高质量仿真,应改用 POSIX 的 random() 函数或 C11 的 <stdlib.h> 新接口 rand_s()(Windows)/ arc4random()(BSD)。

另外,C++ 的 <random> 提供梅森旋转算法,跨平台且周期长,是现代 C 程序可以借鉴的方案。

使用 random() 提升随机质量(GNU/Linux):
#define _DEFAULT_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    srandom(time(NULL));
    for (int i = 0; i < 5; ++i)
        printf("%ld ", random());
    return 0;
}
/* 某次运行结果:
1234567890 987654321 555555555 111111111 202312345 */

常见误区与调试技巧

1. 每次 rand() 前都 srand(time(NULL))——时间精度为秒,导致连续调用得到相同种子。

2. 用 rand() % 10000 生成验证码却忽略 RAND_MAX 只有 32767,造成某些数字概率偏高。

3. 在多线程中共享 rand() 状态出现竞争。

解决:主线程只调用一次 srand();或改用线程安全的 erand48();或给每个线程独立种子。

【线程安全示例】独立种子:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <time.h>

#define THREADS 2

void* work(void* arg) {
    unsigned seed = time(NULL) + *(int*)arg; /* 各线程种子不同 */
    for (int i = 0; i < 3; ++i)
        printf("T%d: %d\n", *(int*)arg, rand_r(&seed));
    return NULL;
}

int main(void) {
    pthread_t t[THREADS];
    int id[THREADS] = {1, 2};
    for (int i = 0; i < THREADS; ++i) pthread_create(&t[i], NULL, work, &id[i]);
    for (int i = 0; i < THREADS; ++i) pthread_join(t[i], NULL);
    return 0;
}
/* 某次运行结果:
T1: 1481765933
T1: 1020355381
T1: 1457077689
T2: 1805894523
T2: 1209059285
T2: 957064183 */

完整示例:猜数字小游戏

综合本节知识,写一段“猜 1~100 整数”的极简游戏。程序自动产生答案,用户键盘输入猜测,程序提示大了或小了,直到猜中。代码展示随机数生成、区间映射、输入输出与循环控制,适合初学者一次看懂。

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

int main(void) {
    srand((unsigned)time(NULL));
    int goal = rand() % 100 + 1; /* 1~100 */
    int guess, cnt = 0;
    printf("Guess the number (1-100):\n");
    do {
        printf("Your guess: ");
        scanf("%d", &guess);
        ++cnt;
        if (guess < goal) puts("Too small!");
        else if (guess > goal) puts("Too big!");
        else printf("Bingo! %d attempts.\n", cnt);
    } while (guess != goal);
    return 0;
}
/* 运行交互示例:
Guess the number (1-100):
Your guess: 50
Too big!
Your guess: 25
Too small!
Your guess: 37
Bingo! 3 attempts. */
posted @ 2025-11-24 18:04  像风一样的博主  阅读(8)  评论(0)    收藏  举报