Main Content

控制随机数的生成

此示例说明如何使用 rng 函数,该函数针对随机数的生成提供控制。

MATLAB® 中的(伪)随机数通过 randrandirandn 函数生成。许多其他函数调用这三个函数,但这三个函数是基础构建块。这三个函数都依赖于一个共享的随机数生成器,可以使用 rng 控制该生成器。

务必注意,MATLAB 中的“随机”数并非是完全不可预测的,而是由确定的算法生成的。该算法设计得极为复杂,这样一来,对不了解该算法的人来说,其输出似乎为独立的随机序列,并且可以通过各种随机性的统计学测试。这里介绍的函数提供了相应方式来利用确定性执行以下操作:

  • 重复进行涉及随机数的计算,并获取相同结果,或者

  • 保证在重复计算中使用不同的随机数

以及利用明显的随机性来证明不同计算的合并结果。

“从头开始”

如果在新 MATLAB 会话中查看 randrandirandn 的输出,可注意到,每次重新启动 MATLAB 时,这些函数都返回相同的数字序列。能够将随机数生成器重置到启动状态而实际上并不重新启动 MATLAB,通常很有用。例如,您可能需要重复进行涉及随机数的计算并获取相同结果。

当您第一次启动 MATLAB 会话或调用 rng("default") 时,MATLAB 使用默认算法和种子初始化随机数生成器。从 R2023b 开始,您可以在 MATLAB 预设项中设置默认算法和种子。如果您不更改这些预设项设置,则与以前的版本一样,rng 将使用种子为 0 的梅森旋转生成器的出厂值 "twister"。有关详细信息,请参阅 随机数生成器的默认设置随机数生成器的可再现性

rng("default") 提供了一种非常简单的方式来将随机数生成器重置为其默认设置。

rng("default")
rand % returns the same value as at startup
ans = 0.8147

MATLAB 启动时使用的或 rng("default") 为您提供的“默认”随机数设置是什么?在 R2023b 之前,如果调用不带任何输入的 rng,可以看到,该设置是种子为 0 的梅森旋转生成器算法。

rng
ans = struct with fields:
     Type: 'twister'
     Seed: 0
    State: [625x1 uint32]

下面详细介绍了如何使用上述输出(包括 State 字段)来控制和更改 MATLAB 生成随机数的方式。在上文中,该示例提供了一种方法来查看生成器 randrandirandn 当前使用的设置。

非重复性

每次调用 randrandirandn 时,它们都会从其共享的随机数生成器获取一个新值,后续值可被视为在统计上独立的值。但是如上所述,每次重新启动 MATLAB 时,这些函数都将会被重置并返回相同的数字序列。很明显,使用相同“随机”数进行的计算不能认为是与统计无关的。因此,如果需要合并在两个或更多个 MATLAB 会话中完成的计算(好像这些计算与统计无关的),则无法使用默认生成器设置。

避免在新的 MATLAB 会话中重复生成相同随机数的一种简单方式是,为随机数生成器选择不同的种子。rng 通过基于当前时间创建种子,为您提供了一种选择不同种子的简单方式。

rng("shuffle")
rand
ans = 0.3146

每次使用 "shuffle" 时,都将为生成器提供一个不同的种子。可以调用不带任何输入的 rng 来查看实际所使用的种子。

rng
ans = struct with fields:
     Type: 'twister'
     Seed: 736106447
    State: [625x1 uint32]

rng("shuffle") % creates a different seed each time
rng
ans = struct with fields:
     Type: 'twister'
     Seed: 736106457
    State: [625x1 uint32]

rand
ans = 0.8198

"shuffle" 是为随机数生成器重新提供种子的一种简单方式。您可能认为,使用它在 MATLAB 中实现“真正的”随机性是个好主意,甚至是必要的。但在大多数场合下,完全无需使用 "shuffle"。基于当前时间选择种子并不会改善从 randrandirandn 获取的值的统计属性,也不会在任何意义上提高这些值的随机性。虽然最好在每次启动 MATLAB 时,或者在运行某种涉及随机数的大型计算之前,都为生成器重新提供种子,但实际上在会话中过于频繁地为生成器重新提供种子并不是一种恰当之举,因为这可能会影响随机数的统计学属性。

"shuffle" 真正提供的是一种避免重复相同值序列的方法。有时它很关键,有时仅是“不错”,但通常完全无非紧要。请记住,如果使用 "shuffle",您可能需要保存 rng 创建的种子,以便在以后重复您的计算。下面说明了如何执行该操作。

进一步控制重复性和非重复性

到此为止,您已了解了如何将随机数生成器重置为其默认设置,以及使用基于当前时间创建的种子为其重新提供种子。rng 还提供了一种使用特定种子来为随机数生成器重新提供种子的方法。

您可以多次使用相同的种子来重复进行相同的计算。例如,如果运行下面的代码两次...

rng(1) % the seed is any non-negative integer < 2^32
x = randn(1,5)
x = 1×5

   -0.6490    1.1812   -0.7585   -1.1096   -0.8456

rng(1)
x = randn(1,5)
x = 1×5

   -0.6490    1.1812   -0.7585   -1.1096   -0.8456

...将得到完全一样的结果。通过执行该操作可以在清除 x 之后重新创建它,以便在依赖于 x 的后续计算中使用这些特定值来重复所要执行的操作。

另一方面,您可能想要选择其他种子来确保不重复相同的计算。例如,如果在某 MATLAB 会话中运行下面的代码...

rng(2)
x2 = sum(randn(50,1000),1); % 1000 trials of a random walk

并在另一个会话中运行下面的代码...

rng(3)
x3 = sum(randn(50,1000),1);

...可以合并两个结果,并确信它们不是重复两次的相同结果。

x = [x2 x3];

与 "shuffle" 一样,当为 MATLAB 的随机数生成器重新提供种子时将会出现警告,因为这会影响 randrandirandn 的所有后续输出。除非需要重复性或唯一性,否则通常建议不要简单通过重新为生成器提供种子来生成随机值。如果的确需要为生成器重新提供种子,通常最好在命令行或代码中不易忽略的某个点上完成。

选择生成器类型

不仅可以按如上所述为随机数生成器重新提供种子,还可以选择要使用的随机数生成器的类型。不同的生成器类型会产生不同的随机数序列,例如,可以因为其统计学属性而选择特定的类型。或者,可能需要从使用不同默认生成器类型的旧版 MATLAB 重新创建结果。

选择生成器类型的另一个常见原因是,您要编写用于生成“随机”输入数据的验证测试,并需要保证测试始终可以得到完全相同的可预测结果。如果在创建输入数据之前调用带种子的 rng,这将为随机数生成器重新提供种子。但如果由于某原因更改了生成器类型,则 randrandirandn 的输出将不会是您期望通过该种子得到的。因此,要 100% 确保可重复性,也可以指定生成器类型。

例如,

rng(0,"twister")

randrandirandn 采用种子 0 且使用梅森旋转生成器算法。

使用 "combRecursive"

rng(0,"combRecursive")

选择组合多递归生成器算法,该算法支持梅森旋转不支持的某些并行功能。

以下命令

rng(0,"v4")

选择 MATLAB 4.0 中默认的生成器算法。

当然,此命令会将随机数生成器恢复为其默认设置。

rng("default")

但是,由于不同 MATLAB 版本的默认随机数生成器设置可能发生更改,因此使用 "default" 并不能长期保证得到可预测的结果。"default" 是重置随机数生成器的一种便利方式,但要实现更好的可预测性,请指定生成器类型和种子。

另一方面,在执行交互式工作并且需要可重复性时,调用只带一个种子的 rng 不仅更简单,而且通常足够满足要求。

保存并还原随机数生成器的设置

调用不带任何输入的 rng 将返回一个标量结构体,其中的字段包含已说明的两条信息:生成器类型和上次为生成器重新提供种子所用的整数。

s = rng
s = struct with fields:
     Type: 'twister'
     Seed: 0
    State: [625x1 uint32]

第三个字段 State 包含生成器的当前状态向量的副本。此状态向量是生成器在内部维护以便在其随机数序列中生成下一个值的信息。每次调用 randrandirandn 时,它们共享的生成器都会更新其内部状态。因此,rng 返回的设置结构体中的状态向量包含从捕获状态的时间点开始重复该序列所需的信息。

虽然可以看到此输出为信息性内容,但 rng 也接受设置结构体作为“输入”,以便保存设置(包括状态向量)并在以后还原它们以重复计算。因为设置包含生成器类型,所以可以确切知道将得到的结果,这样“以后”在同一 MATLAB 会话中可能意味着从稍后的某个时刻到以后的几年(和多个 MATLAB 版本)。您可以在随机数序列中从保存生成器设置的任何点重复结果。例如

x1 = randn(10,10); % move ahead in the random number sequence
s = rng;           % save the settings at this point
x2 = randn(1,5)
x2 = 1×5

    0.8404   -0.8880    0.1001   -0.5445    0.3035

x3 = randn(5,5);   % move ahead in the random number sequence
rng(s);            % return the generator back to the saved state
x2 = randn(1,5)    % repeat the same numbers
x2 = 1×5

    0.8404   -0.8880    0.1001   -0.5445    0.3035

请注意,虽然重新提供种子仅提供粗略的重新初始化,但使用结构体保存和还原生成器状态,可以重复随机数序列的任意部分

使用设置结构体的最常见方式是还原生成器状态。但是,由于结构体不仅包含状态,还包含生成器类型和种子,因此临时切换生成器类型也是一种方便的方式。例如,如果需要使用 MATLAB 5.0 中的某个旧生成器创建值,则可以在切换为使用旧生成器的同时保存当前设置。

previousSettings = rng(0,"v5uniform")
previousSettings = struct with fields:
     Type: 'twister'
     Seed: 0
    State: [625x1 uint32]

您可以稍后还原原始设置。

rng(previousSettings)

不应修改设置结构体中的任何字段的内容。特别是,不应自行构造状态向量,也不应依赖于生成器状态的格式。

编写更简单、更灵活的代码

通过 rng,可以

  • 为随机数生成器重新提供种子,或者

  • 保存并还原随机数生成器设置

而无需了解其是什么类型。您也可以将随机数生成器恢复为其默认设置,而无需了解这些设置是什么。rng 虽然存在可能需要指定生成器类型的情况,但指定生成器类型不是必需的,因而可简化您的操作。

如果能够避免指定生成器类型,则代码将自动适应需要使用不同生成器的各种情况,并将自动受益于新的默认随机数生成器类型中的改进属性。

rngRandStream

rng 提供了一种便捷的方式来控制 MATLAB 中的随机数生成,以满足大多数常见的需求。但是,涉及多个随机数流和并行随机数生成的更复杂情况需要更复杂的工具。RandStream 类即是满足该需要的工具,它提供了最强大的方式来控制随机数生成。这两个工具相互补充,其中 rng 基于 RandStream 的灵活性而构建,可提供更简单和更精炼的语法。