【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析(二)

简介: 【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析

【C/C++ 随机函数行为】深入探索C++中的随机数:std::random_device与rand的行为分析(一)https://developer.aliyun.com/article/1467606


4.3 rand的局限性与替代方案

正如Donald Knuth在他的经典著作《计算机程序设计艺术》中所说:“伪随机数生成器不应该被用于生成真正的随机数。”这意味着,尽管rand()函数在许多应用中都很有用,但它并不适合所有场景。

例如,当我们需要生成加密安全的随机数时,rand()就不再适用。在这种情况下,我们需要使用C++标准库中的std::random_device或其他加密安全的随机数生成器。

4.3.1 为什么rand()不是最佳选择

  1. 可预测性:如前所述,rand()是一个伪随机数生成器。这意味着,给定相同的种子,它总是返回相同的序列。
  2. 范围限制rand()返回的随机数范围受到RAND_MAX的限制,这可能不适合所有应用。

4.3.2 替代方案

C++标准库提供了多种随机数引擎,如std::mt19937(Mersenne Twister引擎)和std::random_device。这些引擎提供了更高的随机性和更广的范围,使它们成为rand()的理想替代方案。

std::random_device rd;  // 真随机数生成器
std::mt19937 gen(rd()); // 以真随机数为种子的Mersenne Twister引擎
std::uniform_int_distribution<> dis(1, 6); // 均匀分布
for (int n=0; n<10; ++n)
    std::cout << dis(gen) << ' '; // 生成1到6之间的随机数

在上面的示例中,我们使用std::random_device作为种子来初始化Mersenne Twister引擎,并使用均匀分布来生成1到6之间的随机数。

总之,当我们需要生成随机数时,应该考虑使用C++标准库提供的随机数引擎,而不是依赖rand()函数。

5. C++标准库中的随机数引擎与分布

在我们的日常生活中,随机性无处不在,从天气预报到股票市场,再到我们决定吃什么晚餐。同样,在编程中,随机性也是一个不可或缺的部分。但是,真正的随机性是很难实现的,这就是为什么我们需要随机数引擎和分布来帮助我们。

5.1 常见的随机数引擎概览

C++标准库提供了多种随机数引擎,每种引擎都有其特定的用途和特点。随机数引擎(Random Number Engine)是一个可以产生一系列随机数的对象。这些引擎通常基于某种数学算法,可以产生伪随机数序列。

以下是C++中常见的随机数引擎:

引擎名称 特点 适用场景
default_random_engine 默认引擎,可能因平台而异 快速生成随机数
mt19937 基于Mersenne Twister算法 需要高质量随机数
mt19937_64 64位版本的Mersenne Twister算法 需要高质量的64位随机数
ranlux24_base RANLUX算法的基础版本 需要中等质量的随机数
ranlux48_base RANLUX算法的48位版本 需要高质量的随机数

从心理学的角度来看,人类对随机性的感知是有偏见的。我们往往认为,一个真正的随机序列应该看起来是均匀的,没有任何模式。但实际上,真正的随机序列可能包含看似不随机的模式。这就是为什么我们需要随机数引擎来帮助我们生成看起来更“随机”的序列。

“随机性不是不按任何规则,而是按照我们不理解的规则。” - 艾伯特·爱因斯坦

5.2 分布函数与随机数生成

在生成随机数时,我们通常不仅仅是想得到一个随机的数字,而是想得到一个符合某种分布的数字。例如,当我们掷一个公正的骰子时,我们期望每个数字出现的概率都是1/6。这就是均匀分布。

C++标准库提供了多种分布函数,如:

  • uniform_int_distribution:均匀分布的整数
  • uniform_real_distribution:均匀分布的实数
  • normal_distribution:正态分布
  • exponential_distribution:指数分布
  • …等等

每种分布都有其特定的参数和用途。例如,正态分布需要两个参数:均值和标准差。

std::default_random_engine generator;
std::normal_distribution<double> distribution(5.0,2.0); // 均值为5,标准差为2
for (int i=0; i<10; ++i) {
    std::cout << distribution(generator) << std::endl;
}

在这个示例中,我们生成了10个符合正态分布的随机数。

从心理学的角度来看,人们对正态分布有很强的直觉。许多自然现象,如人的身高、考试成绩等,都大致遵循正态分布。这也是为什么正态分布在统计学中如此重要。

“大自然喜欢隐藏自己。” - 赫拉克利特

5.3 如何选择合适的随机数引擎与分布

选择合适的随机数引擎和分布取决于你的具体需求。以下是一些建议:

  1. 如果你只是需要快速生成随机数,而不太关心其质量,那么default_random_engineuniform_int_distributionuniform_real_distribution可能是个好选择。
  2. 如果你需要高质量的随机数,例如在加密或模拟中,那么mt19937ranlux48_base可能更合适。
  3. 如果你需要生成符合特定分布的随机数,例如正态分布或指数分布,那么你应该选择相应的分布函数。

最后,记住测试你的随机数生成器。确保它满足你的需求,并且在你的应用中表现良好。

从心理学的角度来看,人们往往对随机数生成器有过高的期望。我们期望它既快速又高质量,但这在实际中往往是不可能的。因此,理解你的需求,并选择合适的工具是关键。

“知己知彼,百战不殆。” - 孙子《孙子兵法》

6. 随机算法与应用

在编程中,随机性通常与不确定性相伴随。但是,当我们深入了解其背后的心理学原理时,我们会发现随机性实际上是一种强大的工具,可以帮助我们更好地理解和解决问题。

6.1 随机打乱容器中的元素

在C++中,我们经常需要打乱容器中的元素。例如,当我们想要随机化一个数组或向量的元素顺序时。这种随机化的需求可以追溯到心理学中的一种观点:人们往往对随机事件的预测存在偏见。通过打乱元素,我们可以确保数据的随机性,从而消除这种偏见。

#include <algorithm> // for std::shuffle
#include <vector>
#include <random>
std::vector<int> numbers = {1, 2, 3, 4, 5};
std::random_device rd; // 随机数设备 (Random device)
std::mt19937 g(rd()); // 使用Mersenne Twister算法生成随机数
std::shuffle(numbers.begin(), numbers.end(), g);

在上面的示例中,我们使用了std::shuffle函数来打乱numbers向量中的元素。这是一个简单而有效的方法,可以确保元素的随机性。

6.2 生成随机子集

生成随机子集是另一个常见的需求。例如,当我们想要从大数据集中随机选择一些样本进行测试时。这与心理学中的抽样理论有关,即从总体中随机选择一部分样本,以便对总体进行估计。

#include <set>
#include <random>
std::set<int> generateRandomSubset(const std::set<int>& data, size_t size) {
    std::vector<int> vec(data.begin(), data.end());
    std::random_device rd;
    std::mt19937 g(rd());
    std::shuffle(vec.begin(), vec.end(), g);
    return std::set<int>(vec.begin(), vec.begin() + size);
}

这个函数从给定的数据集中随机选择一个子集。这是一个非常实用的工具,可以帮助我们在实际应用中更好地理解和使用随机性。

6.3 随机数在模拟与统计中的应用

随机数在模拟和统计中有广泛的应用。例如,当我们想要模拟现实世界中的某种情况,或者当我们想要估计某种现象的概率时。这与心理学中的模拟理论有关,即通过模拟来理解和预测现实世界中的事件。

#include <iostream>
#include <random>
double simulatePi(size_t iterations) {
    std::random_device rd;
    std::mt19937 g(rd());
    std::uniform_real_distribution<double> dist(0.0, 1.0);
    size_t insideCircle = 0;
    for (size_t i = 0; i < iterations; ++i) {
        double x = dist(g);
        double y = dist(g);
        if (x * x + y * y <= 1.0) {
            ++insideCircle;
        }
    }
    return 4.0 * insideCircle / iterations;
}
int main() {
    std::cout << "Estimated value of Pi: " << simulatePi(1000000) << std::endl;
}

在上面的示例中,我们使用了蒙特卡洛方法来估计π的值。这是一个简单而有效的方法,可以帮助我们更好地理解和使用随机性。

6.3.1 蒙特卡洛方法的心理学解读

蒙特卡洛方法是一种使用随机数来解决问题的方法。从心理学的角度来看,这种方法利用了人们对随机事件的直觉和经验。通过大量的随机试验,我们可以得到一个问题的近似解。这种方法的有效性建立在大数定律的基础上,即随着试验次数的增加,平均结果趋近于真实值。

名言引用“我经常说,只要你给我足够的数据,我可以用统计方法证明任何事情。” - Darrell Huff,《如何撒谎、伪造数据及统计数字》

6.3.2 随机数在统计中的应用

在统计学中,随机数被用来进行抽样、模拟和其他各种计算。这与心理学中的随机性概念有关,即人们对随机事件的预测和解释。

名言引用“统计学是从数据中得出结论的科学,尤其是在存在变异和不确定性的情况下。” - Ronald A. Fisher

方法 优点 缺点
蒙特卡洛模拟 可以处理复杂的问题 需要大量的计算
抽样 可以从大数据集中得到代表性的信息 可能存在偏见

7. Qt与C++随机数

在C++编程中,随机数是一个非常重要的部分,尤其是在涉及到模拟、游戏或安全性的应用中。Qt,作为一个跨平台的应用程序开发框架,也提供了一套随机数工具。但是,它与C++标准库中的随机数工具有何不同?在这一章中,我们将深入探讨Qt中的随机数工具,并从心理学的角度来帮助读者更好地理解它们。

7.1 Qt中的随机数工具

Qt提供了一个名为QRandomGenerator的类,它是Qt 5.10引入的新特性。与C++标准库中的std::random_devicestd::mt19937等随机数引擎相比,QRandomGenerator提供了一个更简单、更高效的随机数生成方法。

7.1.1 QRandomGenerator的基本使用

#include <QRandomGenerator>
int main() {
    // 使用全局的随机数生成器
    quint32 value = QRandomGenerator::global()->generate();
    qDebug() << "Random Value:" << value;
}

在上面的示例中,我们使用了QRandomGenerator的全局实例来生成一个随机的quint32值。这与使用std::random_devicestd::mt19937生成随机数的方法相比,显得更为简洁。

但是,为什么Qt要引入一个新的随机数生成器,而不是直接使用C++标准库中的工具呢?这是因为,从心理学的角度来看,人们更喜欢简单直接的方法。当面对复杂的任务时,我们的大脑会自动寻找简化问题的方法。这就是为什么Qt提供了一个简单易用的随机数生成器,以满足大多数开发者的需求。

7.1.2 QRandomGenerator与C++标准库的比较

特性/工具 QRandomGenerator std::random_device std::mt19937
速度
随机性
使用简易性

从上表中,我们可以看到QRandomGenerator在速度和随机性上都有很好的表现,而且使用起来非常简单。这再次证明了Qt的设计哲学——使复杂的任务变得简单。

7.2 如何在Qt项目中有效利用C++标准库的随机数功能

尽管Qt提供了自己的随机数工具,但在某些情况下,我们可能仍然需要使用C++标准库中的随机数功能。例如,当我们需要更复杂的随机数分布或更高级的随机数引擎时。

7.2.1 使用std::random_device与Qt结合

#include <random>
#include <QRandomGenerator>
int main() {
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dis(1, 6);
    int randomValue = dis(gen);
    qDebug() << "Random Value using std::random_device:" << randomValue;
    quint32 qtValue = QRandomGenerator::global()->bounded(1, 6);
    qDebug() << "Random Value using QRandomGenerator:" << qtValue;
}

在上面的示例中,我们首先使用std::random_devicestd::mt19937生成了一个1到6之间的随机整数,然后再使用QRandomGenerator生成了一个同样范围的随机整数。这样,我们可以在同一个项目中灵活地使用两种不同的随机数工具。

从心理学的角度来看,人们在面对选择时,往往会选择自己熟悉的工具。但是,为了提高编程效率和代码的可读性,我们应该根据实际需求选择最合适的工具。

7.2.2 深入源码:QRandomGenerator的工作原理

QRandomGenerator的工作原理基于Xorshift128+算法。这是一个高效的伪随机数生成算法,它的随机性和速度都非常出色。从心理学的角度来看,深入了解工具的工作原理可以帮助我们更好地理解和使用它。

“知其然,知其所以然。” —— 《论语》

这句古老的名言告诉我们,只有深入了解事物的本质,我们才能真正掌握它。同样,在编程中,深入了解工具的工作原理可以帮助我们写出更高效、更稳定的代码。

结语

在我们的编程学习之旅中,理解是我们迈向更高层次的重要一步。然而,掌握新技能、新理念,始终需要时间和坚持。从心理学的角度看,学习往往伴随着不断的试错和调整,这就像是我们的大脑在逐渐优化其解决问题的“算法”。

这就是为什么当我们遇到错误,我们应该将其视为学习和进步的机会,而不仅仅是困扰。通过理解和解决这些问题,我们不仅可以修复当前的代码,更可以提升我们的编程能力,防止在未来的项目中犯相同的错误。

我鼓励大家积极参与进来,不断提升自己的编程技术。无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力。

目录
相关文章
|
5月前
|
程序员 编译器 C++
【C++核心】C++内存分区模型分析
这篇文章详细解释了C++程序执行时内存的四个区域:代码区、全局区、栈区和堆区,以及如何在这些区域中分配和释放内存。
73 2
|
2月前
|
存储 算法 安全
基于红黑树的局域网上网行为控制C++ 算法解析
在当今网络环境中,局域网上网行为控制对企业和学校至关重要。本文探讨了一种基于红黑树数据结构的高效算法,用于管理用户的上网行为,如IP地址、上网时长、访问网站类别和流量使用情况。通过红黑树的自平衡特性,确保了高效的查找、插入和删除操作。文中提供了C++代码示例,展示了如何实现该算法,并强调其在网络管理中的应用价值。
|
1月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
8月前
|
存储 前端开发 安全
C++一分钟之-未来与承诺:std::future与std::promise
【6月更文挑战第27天】`std::future`和`std::promise`是C++异步编程的关键工具,用于处理未完成任务的结果。`future`代表异步任务的结果容器,可阻塞等待或检查结果是否就绪;`promise`用于设置`future`的值,允许多线程间通信。常见问题包括异常安全、多重获取、线程同步和未检查状态。解决办法涉及智能指针管理、明确获取时机、确保线程安全以及检查未来状态。示例展示了使用`std::async`和`future`执行异步任务并获取结果。
196 2
|
2月前
|
存储 对象存储 C++
C++ 中 std::array<int, array_size> 与 std::vector<int> 的深入对比
本文深入对比了 C++ 标准库中的 `std::array` 和 `std::vector`,从内存管理、性能、功能特性、使用场景等方面详细分析了两者的差异。`std::array` 适合固定大小的数据和高性能需求,而 `std::vector` 则提供了动态调整大小的灵活性,适用于数据量不确定或需要频繁操作的场景。选择合适的容器可以提高代码的效率和可靠性。
101 0
|
3月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【11月更文挑战第6天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
149 5
|
4月前
|
存储 算法 搜索推荐
对二叉堆的简单分析,c和c++的简单实现
这篇文章提供了对二叉堆数据结构的简单分析,并展示了如何在C和C++中实现最小堆,包括初始化、插入元素、删除最小元素和打印堆的函数,以及一个示例程序来演示这些操作。
58 19
|
4月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
【10月更文挑战第8天】在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。
|
5月前
|
Ubuntu Linux Shell
C++ 之 perf+火焰图分析与调试
简介 在遇到一些内存异常的时候,经常这部分的代码是很难去进行分析的,最近了解到Perf这个神器,这里也展开介绍一下如何使用Perf以及如何去画火焰图。 1. Perf 基础 1.1 Perf 简介 perf是Linux下的一款性能分析工具,能够进行函数级与指令级的热点查找。利用perf剖析程序性能时,需要指定当前测试的性能时间。性能事件是指在处理器或操作系统中发生的,可能影响到程序性能的硬件事件或软件事件 1.2 Perf的安装 ubuntu 18.04: sudo apt install linux-tools-common linux-tools-4.15.0-106-gen
114 2
|
5月前
|
安全 C++
C++: std::once_flag 和 std::call_once
`std::once_flag` 和 `std::call_once` 是 C++11 引入的同步原语,确保某个函数在多线程环境中仅执行一次。