【问题标题】:FORTRAN: A random number generator must be called many times? How to set the seed?FORTRAN:必须多次调用随机数生成器?如何设置种子?
【发布时间】:2016-04-15 11:23:02
【问题描述】:

我的随机随机数生成器必须被调用多次,未指定:

    WRITE(*,*) Random_Number_routine([optional seed])

我的随机数生成器有一个可选的整数参数,如果存在,它将使用它作为种子。如果不存在,它会调用 system_clock(i) 并使用 i 作为种子。到现在为止还挺好。但是,如果调用 Random_Number_routine 太快,超过每秒一次,则系统时钟没有机会改变 COUNT 参数,并返回相同的值。作为一个例子,考虑对https://gcc.gnu.org/onlinedocs/gfortran/SYSTEM_005fCLOCK.html中给出的例子的改编

PROGRAM test_system_clock

    INTEGER :: count, count_rate, count_max

    DO i = 1, 100

        CALL SYSTEM_CLOCK(count, count_rate, count_max)
        CALL srand(count) 
        WRITE(*,*) count, count_rate, count_max, rand()

    END DO

END PROGRAM test_system_clock

test_system_clock 的输出是

 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320589        1000  2147483647  0.640894175    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    
 5320590        1000  2147483647  0.640901804    

 4928363        1000  2147483647

count 参数只取两个值:5320589 和 5320590。如果将此参数用作 srand 中的种子,即

  Call srand(count) 

然后使用相同的种子并生成相同的随机数,可以在数据的最后一列中看到。解决这个问题的唯一方法是从单个种子生成 $N$ 随机变量,即

CALL SYSTEM_CLOCK(count, count_rate, count_max)
Call srand(count) 

DO j = 1, n
    res(j) = rand()
END DO

然而,这假设人们知道在调用例程之前需要多少随机变量 - 在这种情况下我不知道。

我唯一经历过的另一条路线是使用 Fortran 的 save 属性并保存以前使用的种子 - 如果它第一次执行例程,它使用 SYSTEM_CLOCK 来确定种子,持续运行通过以下方式更新种子某个整数。但是,这不会产生随机数,因为仅使用序列中的第一个数字。

其他软件如何解决这个问题?像 MATLAB 这样的程序是如何做到这一点的?以及如何调用随机数例程并保证新的随机数?

更新:回答 francescalus 评论:所以在显示的第一个代码块中,我已经调用了该例程 100 次,表中的最后一列给出了每次调用生成的随机数。随机数只有两个值:0.640894175 和 0.640901804。这似乎是因为只有两个种子值可用(如第一列所示)。我要确定的是,鉴于我在种子值更改时更快地调用例程,我如何获得随机数。即如果我调用例程 100 次,那么我期望 100 个随机值,而不是相同的两个值。会不会是 Fortran 的错误?

【问题讨论】:

  • 我想我现在明白了。如果我使用的是 PRNG,我希望它能够更新适合下一个调用的状态,并按顺序给出下一个调用。也就是说,对于call my_prng(deviate, state) 之类的东西,我希望stateintent(inout)(或在内部存储)。作为用户,我不希望在初始化后每次调用都提供不同的种子(这更像是哈希函数而不是 PRNG)。

标签: matlab random fortran random-sample


【解决方案1】:

您每次都在循环中重新播种您的 RNG。我的 FORTRAN 非常 生锈了(有人使用 FORTRAN IV 吗?)但我怀疑您需要将 srand() 调用从循环中拉出来。比如:

PROGRAM test_system_clock

  INTEGER :: count, count_rate, count_max

  CALL SYSTEM_CLOCK(count, count_rate, count_max)
  CALL srand(count) 

  DO i = 1, 100

    CALL SYSTEM_CLOCK(count, count_rate, count_max)
    WRITE(*,*) count, count_rate, count_max, rand()

  END DO

END PROGRAM test_system_clock

一般来说,您应该在程序开始时使用 srand() 为 RNG 播种一次,然后重复调用 rand() 以生成伪随机序列而无需重新播种。

【讨论】:

    【解决方案2】:

    rand() 方法是全局可用的系统调用,srand 也是如此。在程序的初始化阶段调用一次srand(在你的主程序中,或者从主程序调用的第一个例程)。然后,您可以从程序中的任何位置任意调用rand,以按需获取伪随机值,而无需事先知道需要多少。这里的关键概念是srand 用于初始化 PRNG 的状态,并且每次运行程序只能调用一次。

    当您使用它时,您应该考虑用质量更好的生成器替换rand,它非常糟糕。 gnu.org 上的 rand 网页将您指向 random_number 作为更好的选择。

    【讨论】:

    • "random_number` 的实现依赖于实现并且可以有各种质量(实际上它可以调用rand),但至少在 gfortran 中实现是相当不错的 Marsaglia 的 KISS PRNG。跨度>
    • @VladimirF 感谢您的信息,我知道对于 gfortran 它比 rand 更好,但不知道细节。
    猜你喜欢
    • 2012-11-26
    • 1970-01-01
    • 2014-09-20
    • 2011-06-08
    • 1970-01-01
    • 2021-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多