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