Random 线性同余发生器-输出均为0?

Random 线性同余发生器-输出均为0?,random,scope,fortran,fortran77,Random,Scope,Fortran,Fortran77,我一直在尝试用Fortran 77制作一个非常基本的LCG伪随机数生成器,将1000个随机数打印到一个文件中,但不管出于什么原因,输出值仅为1000秒。整个代码都很短,所以我已经对它进行了多次梳理,并尝试改变一些东西,但我一辈子都无法找出哪里出了问题。我有一种预感,这可能是一个范围问题(如果这样的概念在Fortran中甚至有用的话),但这确实是毫无根据的 PROGRAM RANDOM COMMON ISEED, RANDOMNUMBER ISEED = 123

我一直在尝试用Fortran 77制作一个非常基本的LCG伪随机数生成器,将1000个随机数打印到一个文件中,但不管出于什么原因,输出值仅为1000秒。整个代码都很短,所以我已经对它进行了多次梳理,并尝试改变一些东西,但我一辈子都无法找出哪里出了问题。我有一种预感,这可能是一个范围问题(如果这样的概念在Fortran中甚至有用的话),但这确实是毫无根据的

      PROGRAM RANDOM
      COMMON ISEED, RANDOMNUMBER
      ISEED = 123
      OPEN (UNIT=1,FILE='rand.in',STATUS='UNKNOWN')

      J=1

    7 CALL RANDU(ISEED)
      J=J+1
      WRITE(1,*) RANDOMNUMBER
      IF(J<1000)GOTO 7

      STOP
      END

      SUBROUTINE RANDU(ISEED)
      PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)
      ISEED = ISEED * 65539
      IF(ISEED<0) ISEED = ISEED + IMAX + 1
      RANDOMNUMBER = ISEED * IMAXINV
      RETURN
      END
随机编程
普通ISED,随机数
ISEED=123
打开(单位=1,文件='rand.in',状态='UNKNOWN')
J=1
7呼叫RANDU(ISEED)
J=J+1
写入(1,*)随机数

如果(J我用Fortran编程已经几十年了,但我会尽力帮助你

首先也是最重要的一点,
IMAXINV
是一个整数变量,因为名称以
I
开头,并且您没有声明它是一个浮点。因此除法结果将被截断为整数值
0
,这解释了您的零输出。在任何情况下,您的随机数生成器都应该使用整数运算,而不是整数运算而不是引入泛洪点操作,以确保正确性和速度

Fortran 77支持返回值的函数,是吗?这比将子程序的结果存储在全局变量中更干净、更模块化

IIRC,
COMMON
语句用于在模块之间共享全局值,这对于随机数生成器的私有状态来说是一件危险的事情

您有一个名为
ISEED
COMMON
全局变量和一个同名的子程序形式参数(除非我记不清Fortran子程序声明是如何工作的)。这会混淆问题,应该予以修复。让子例程更新其参数
ISEED
,而不是全局变量,将导致它在每次此循环调用时返回相同的值。也就是说,除非形式参数是对实际参数的引用别名调用——在该代码中具有相同的名称。您知道,它是c正在融合


你有调试器吗?如果有,单步执行程序并观察变量会很快发现程序偏离预期的地方。

好了,现在通过@Jerry101补充答案,我已经编写了一个修改过的代码。这里的关键问题是,
IMAXINV
没有明确声明为
REAL
,因此,它被解释为
INTEGER
(因此,
IMAXINV=1.0/IMAX
在原始代码中始终变为0)。此外,我已从
公共
块中删除了
ISEED
(因为它是作为参数传递的)并将另一个
COMMON
语句放入
RANDU
中,以在例程之间共享变量。通过这些修改,程序似乎可以正常工作

      PROGRAM RANDOM
      COMMON RANDOMNUMBER    !<--- ISEED is deleted from here

      ISEED = 123
      J=1

    7 CALL RANDU(ISEED)
      J=J+1
      WRITE(*,*) RANDOMNUMBER       !<--- write to STDOUT for test
      IF (J < 100) GOTO 7
      END

      SUBROUTINE RANDU(ISEED)
      real IMAXINV                   !<--- this is necessary
      COMMON RANDOMNUMBER            !<--- this is also necessary to share variables
      PARAMETER (IMAX = 2147483647, IMAXINV = 1./IMAX)

      ISEED = ISEED * 65539
      IF(ISEED<0) ISEED = ISEED + IMAX + 1
      RANDOMNUMBER = ISEED * IMAXINV
      END
但是请注意,当使用
函数
时,如果函数名不符合隐式规则,则应在调用例程中显式声明返回变量的类型。(在上述代码中,
RANDU
未显式声明,因为它被解释为
REAL
).所以无论如何,Fortran77中的隐式类型规则有很多警告


补充说明:

为了避免这些缺陷,我建议使用Fortran>=90(而不是Fortran 77),因为它提供了许多防止此类错误的功能。例如,修改最少的代码可能如下所示:

module mymodule
contains

subroutine randu ( istate, ran )
    implicit none
    integer, parameter :: IMAX = 2147483647
    real, parameter :: IMAXINV = 1.0 / IMAX
    integer, intent(inout) :: istate
    real,    intent(out)   :: ran

    istate = istate * 65539
    if ( istate < 0 ) istate = istate + IMAX + 1
    ran = istate * IMAXINV
end subroutine

end module

program main
    use mymodule, only: randu
    implicit none
    integer :: j, istate
    real    :: randomnumber

    istate = 123    !! seed for RANDU()

    do j = 1, 99
        call randu ( istate, randomnumber )
        write(*,*) randomnumber
    enddo
end program
模块mymodule
包含
子程序randu(istate,ran)
隐式无
整数,参数::IMAX=2147483647
实数,参数::IMAXINV=1.0/IMAX
整数,意图(inout)::istate
真实的,意图的:跑
istate=istate*65539
如果(istate<0)istate=istate+IMAX+1
ran=istate*IMAXINV
结束子程序
端模块
主程序
仅使用mymodule:randu
隐式无
整数::j,istate
实数:随机数
istate=123!!RANDU()的种子
j=1,99吗
致电randu(istate,随机号码)
写入(*,*)随机数
结束循环
结束程序
这里,

  • implicit none
    用于明确执行所有变量的声明。这有助于避免错误键入变量(例如问题中的
    IMAXINV
  • 子例程
    RANDU
    包含在
    模块中,因此编译器提供了明确的接口和许多有用的检查(简而言之,
    module
    类似于C++中的名称空间)。
    module
    还可以用于定义全局变量,其方式比
    COMMON
    安全得多
  • 我使用
    do
    enddo
    构造来循环
    j
    ,而不是手动递增并使用
    goto
    。前者实际上更易于使用,而且
    goto
    往往会使代码的可读性降低
  • 我将程序文件命名为“test.f90”(注意后缀.f90),它允许自由格式。此外,变量可以使用小写字母
  • [此外,由于
    iseed
    存储了有关(伪)随机数生成器当前状态的信息,因此最好使用一些不同的变量名称(如istate等)来提醒在调用期间需要保留其值。]

所以,如果你感兴趣,请考虑使用一个更现代的版本(而不是FoRtR77),它允许我们编写更安全、更健壮的代码:

不幸的是我没有调试器,但是到目前为止,你的评论一直很有帮助。恐怕我看不出在哪里有一个名为ISEED的子程序参数,除非我刚刚误解了子程序参数是如何声明的。我肯定认为这是一个意外地改变了错误的变量的情况,但我不确定如何实际捕获它。我做了一个测试,把ISED打印到同一个文件上,看起来
module mymodule
contains

subroutine randu ( istate, ran )
    implicit none
    integer, parameter :: IMAX = 2147483647
    real, parameter :: IMAXINV = 1.0 / IMAX
    integer, intent(inout) :: istate
    real,    intent(out)   :: ran

    istate = istate * 65539
    if ( istate < 0 ) istate = istate + IMAX + 1
    ran = istate * IMAXINV
end subroutine

end module

program main
    use mymodule, only: randu
    implicit none
    integer :: j, istate
    real    :: randomnumber

    istate = 123    !! seed for RANDU()

    do j = 1, 99
        call randu ( istate, randomnumber )
        write(*,*) randomnumber
    enddo
end program