在编辑器中打开 rngdemo.m
在命令窗口中运行

控制随机数生成

MATLAB 中的(伪)随机数通过 randrandirandn 函数生成。 许多其他函数调用这三个函数,这三个函数是基础构建块。 这三个函数都依赖于一个共享随机数生成函数。 以下演示介绍了 rng 函数,该函数提供对该生成函数的控制。

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

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

目录

“从头开始”

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

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

rng default
rand % returns the same value as at startup
ans =

    0.8147

MATLAB 启动时的“默认”随机数设置是什么或 rng 默认为您提供的随机数是什么? 如果调用不带任何输入的 rng,可以看到,该设置为 Mersenne Twister 生成函数算法,种子为 0。

rng
ans = 

     Type: 'twister'
     Seed: 0
    State: [625x1 uint32]

下面详细介绍了如何使用上述输出(包括 State 字段)来控制并更改 MATLAB 生成随机数的方式。 现在,仅使用该示例来查看生成函数 randrandirandn 当前使用的设置。

非重复性

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

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

rng shuffle
rand
ans =

    0.8733

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

rng
ans = 

     Type: 'twister'
     Seed: 1988908430
    State: [625x1 uint32]

rng shuffle % creates a different seed each time
rng
ans = 

     Type: 'twister'
     Seed: 1988908433
    State: [625x1 uint32]

rand
ans =

    0.4194

'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 =

   -0.6490    1.1812   -0.7585   -1.1096   -0.8456

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

   -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')

将 0 作为种子提供给 randrandirandn 之后,使这些函数使用 Mersenne Twister 生成函数算法。

使用 'combRecursive'

rng(0,'combRecursive')

选择 Combined Multiplicative Recursive 生成函数算法,该算法支持 Mersenne Twister 不支持的某些并行功能。

以下命令

rng(0,'v4')

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

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

rng default

但是,由于默认随机数生成函数设置在不同 MATLAB 版本之间会有所变化,因此使用 'default' 并不能长期保证得到可预测的结果。 'default' 是重置随机数生成函数的一种方便方式,但要实现更好的可预测性,请指定生成函数类型和种子。

另一方面,当交互式工作并且需要重复性时,调用带有种子的 rng 的更简单,通常也更高效。

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

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

s = rng
s = 

     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 =

    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 =

    0.8404   -0.8880    0.1001   -0.5445    0.3035

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

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

previousSettings = rng(0,'v5uniform')
previousSettings = 

     Type: 'twister'
     Seed: 0
    State: [625x1 uint32]

... 然后在以后还原原始设置。

rng(previousSettings)

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

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

通过 rng,可以

无需了解其是什么类型 您也可以将随机数生成函数恢复为其默认设置,无需了解这些设置是什么。 虽然存在可能需要指定生成函数类型的情况,但 rng 为您提供了无需指定该类型的简单性。

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

旧模式和 rng

在低于 MATLAB 7.7 的版本中,通过直接调用带有 'seed'、'state' 或 'twister' 输入的 randrandn 来控制随机数生成函数的内部状态。 例如,

rand('state',1234)

建议不要使用该语法,并将 MATLAB 切换到“旧随机数模式”,该情况下 randrandn 使用单独和过期的生成函数,就像低于 MATLAB 7.7 的版本中的行为一样。 如果可能,应将任何使用旧语法的现有代码更新为改用 rng。 要执行该操作,可能需要考虑一下确定使用旧代码的真实意图;有关建议和示例,请参阅更新随机数生成函数语法

如果您或所运行的某些代码执行了将 MATLAB 置于旧模式的命令(如 rand('state',1234)),则可以使用

rng default

来转义旧模式并恢复为默认启动生成函数。 如果存在您无法修改或不允许您修改的代码,则可以使用以下命令保护该旧代码:

s = rng; % save current settings of the generator

% call code using legacy random number generator syntaxes

rng(s) % restore previous settings of the generator

以确保没有其他代码使用旧随机函数生成函数。

rngRandStream

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