【问题标题】:Why is it necessary to call rng() twice in Matlab为什么要在 Matlab 中调用 rng() 两次
【发布时间】:2015-01-06 15:40:22
【问题描述】:

这对我来说似乎是一个错误。似乎有必要在 Matlab 中调用 rng() 两次才能获得所需的种子。考虑以下实验:

>> sd = rng(3) % THIS DOES NOT WORK

sd = 

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

>> sd = rng(3) % BUT NOW IT DOES

sd = 

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

>> sd = rng(3) % AND AGAIN, TO CONFIRM

sd = 

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

>> sd = rng('shuffle') % BUT THIS FAILS

sd = 

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

>> sd = rng('shuffle') % BUT ON THE SECOND GO IT WORKS

sd = 

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

>> sd = rng('shuffle') % AND ON THE THIRD

sd = 

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

>> sd = rng(4) % BUT AGAIN THIS FAILS

sd = 

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

>> sd = rng(4) % BUT ON THE SECOND GO IT WORKS AGAIN

sd = 

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

>> sd = rng(4) % AND SO ON

sd = 

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

【问题讨论】:

    标签: matlab random


    【解决方案1】:

    简答: 根据文档,rng 的返回值为 previous 状态:

    sprev = rng(...) 返回之前的随机数设置 rand、randi 和 randn 在更改设置之前使用的生成器。

    所以,答案是:不,这不是错误。随机数生成器在您第一次调用时已正确初始化。

    但是,在我看来,这是一个非常出乎意料的行为。


    更长一点的答案: 我建议改用RandStream 对象,这对于具有面向对象编程基础知识的任何人来说都更容易理解。例如:

    s1 = RandStream.create('mrg32k3a');
    r1 = rand(s1,100000,1);
    

    我强烈建议避免设置全局流,因为它具有全局变量的所有缺点。

    %Not recommended! (Due to global variable)
    s = RandStream('mt19937ar','Seed',1);
    RandStream.setGlobalStream(s);
    

    编辑 (1) 我想解释一下我对为什么设置全局随机数生成器不是一个好习惯的看法。基本上,任何好的软件的目的都是为了缩小任何变量的范围,以便reduce coupling and increase cohesion。全局变量具有最高可能的耦合(任何例程都可以使用它)和最低可能的内聚。全局随机数生成器甚至比普通变量风险更大,因为它有更多机会被其他人使用。

    重新设置全局随机数生成器可能会导致一些奇怪的错误。考虑以下示例 - 您正在编写一个在 for 循环中运行并生成随机数的程序。

     for i=1:N
         k = randn(1,1); 
         %... Do something
     end
    

    一切似乎都很完美。现在你想在你的循环中间添加一个第三方函数Foo 来做一些事情。代码设计者决定将全局号码生成器重新播种到1

     for i=1:N
         k = randn(1,1); 
         %... Do something
         Foo();
     end
    
     function Foo()
         %Do some stuff
         rng(1);
     end
    

    惊喜!现在,您的程序会生成一个完全非随机的数字序列,即每次循环调用中的数字完全相同。

    更多阅读数据,如果您仍然不相信 - Hereherehere

    【讨论】:

    • 呃,你是对的。此外,这是非常出乎意料的。例如,考虑这种行为。这里sd.Seed 是 3:rng(3); sd=rng()。但在这里,sd.Seed 将是以前的样子:sd = rng(3)
    • @Andrey,我可以听到开发人员在阅读您的答案时哭泣。 rng 命令是作为RandStream 功能的简化 引入的,它被故意设计得有点神秘(什么是“mrg32k3a”?),以阻止用户干预内部结构随机数流,除非他们确切地知道自己在做什么。你在这里,说rng 令人困惑,但RandStream 很容易理解。没有批评你 - 但你的期望和设计之间的差距是具有讽刺意味的。
    • 什么?这是对 globalstream 的一个奇怪的反对意见。如果不想使用它,也不是因为通常反对全局变量。另外,OP的问题是关于rng,默认是基于globalstream的。
    • @SamRoberts,horchler 感谢您的评论!没错,它是作为简化引入的。问题是很多没有软件工程或 OOP 知识的用户正在使用 Matlab,对他们来说,对象的概念很难理解。关于全局随机数生成器,我将尝试扩展我的答案来解释我的意思。
    • rng 返回旧状态的目的是让人们能够使用rng 设置状态,然后使用如下模式安全地恢复它:oldState = rng(desiredState); x = onCleanup(@()rng(oldState)); doStuffWithRngInDesiredState();
    猜你喜欢
    • 2011-10-04
    • 2022-01-13
    • 2019-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-04-03
    • 2012-05-15
    • 2016-10-07
    相关资源
    最近更新 更多