Fortran 为什么这个代码给了我不正确的答案?

Fortran 为什么这个代码给了我不正确的答案?,fortran,gfortran,fortran90,Fortran,Gfortran,Fortran90,我正在尝试编写一个程序,使用lcg作为函数,使用box-muller算法计算更多的随机数。我已经让lcg工作,但是使用box-muller算法的函数给出了错误的值 这是我的密码: module rng implicit none integer, parameter :: dp = selected_real_kind(15,300) real(kind=dp) :: A=100, B= 104001, M = 714025 contains function lcg(seed

我正在尝试编写一个程序,使用lcg作为函数,使用box-muller算法计算更多的随机数。我已经让lcg工作,但是使用box-muller算法的函数给出了错误的值

这是我的密码:

module rng
  implicit none

  integer, parameter :: dp = selected_real_kind(15,300)
  real(kind=dp) :: A=100, B= 104001, M = 714025

contains

function lcg(seed)

  integer, optional, intent(in) :: seed
  real(kind=dp) :: x = 0, lcg

  if(present(seed)) x = seed
  x = mod(A * x + B, M)
  lcg = x/714025

end function


function muller(seed)
  integer, parameter :: dp = selected_real_kind(15,300)
  integer, optional, intent(in) :: seed
  real(kind = dp) :: y1, y2, mean = 0.49, sd = 0.5, muller1, muller2, 
muller, x1, x2, pi = 4.0*ATAN(1.0)
  integer :: N  = 0

! I had to add the do while loop to ensure that this chunk of code would 
only execute once  

do while (N<1)

  x1 = lcg()
  x2 = lcg()
  N = N + 1
  y1 = sd * SQRT(-2.0*LOG(x1)) * COS(2*pi*(x2)) + mean
  y2 = sd * SQRT(-2.0*LOG(x1)) * SIN(2*pi*(x2)) + mean

  print *, y1, y2, x1, x2  ! Printing x1 and x2 to allow me to use a 
calculator to check program is working correctly
end do

end function

end module

program lcgtest
  use rng
  implicit none
  integer :: N

  real(kind=dp) :: lcgmean, ttl = 0, sumof, lcgvar, dev1, muller1, muller2, 
lcgerr, lcgdev
  real, dimension(10000) :: array


 do N = 1, 10000

  ttl = ttl + lcg()
  dev1 = lcg() - lcgmean
  sumof = sumof + dev1  

end do
  muller1 = muller()
  muller2 = muller()
  lcgmean = ttl/10000
  lcgvar = ((sumof)**2)/10000
  lcgdev = SQRT((sumof)**2)/10000
  lcgerr = lcgdev/100
  print *, lcg(), "mean=", lcgmean, "variance=", lcgvar, lcgerr


end program
模块rng
隐式无
整数,参数::dp=所选的实数种类(15300)
实数(种类=dp)::A=100,B=104001,M=714025
包含
功能lcg(种子)
整数,可选,意图(in)::种子
实际(种类=dp)::x=0,lcg
如果(存在(种子))x=种子
x=mod(A*x+B,M)
lcg=x/714025
端函数
功能研磨机(种子)
整数,参数::dp=所选的实数种类(15300)
整数,可选,意图(in)::种子
实际(种类=dp):y1,y2,平均值=0.49,标准差=0.5,穆勒尔1,穆勒尔2,
穆勒,x1,x2,pi=4.0*ATAN(1.0)
整数::N=0
! 我必须添加do-while循环,以确保这段代码
只执行一次

我不知道你对这个程序的预期结果是什么。但是,阅读它,我可以很容易地得到你遵循的逻辑。我注意到两个事实错误。请通读下面的代码以查看我的增强功能

module rng
    implicit none
    integer, parameter :: dp = selected_real_kind(15,300)
    real(kind=dp) :: A=100, B= 104001, M = 714025

contains

    function lcg(seed)

        integer, optional, intent(in) :: seed
        real(kind=dp) :: x = 0, lcg

        if(present(seed)) x = seed
        x = mod(A * x + B, M)
        lcg = x/714025

    end function lcg ! Note 'lcg' here @ the end 


    function muller(seed)
        integer, parameter :: dp = selected_real_kind(15,300)
        integer, optional, intent(in) :: seed
        real(kind = dp) :: y1, y2, mean = 0.49, sd = 0.5, muller1, & 
                           muller2, muller, x1, x2, pi = 4.0*ATAN(1.0)
        integer :: N  = 0

        ! I had to add the do while loop to ensure that this chunk
        ! of code would only execute once  

        do while (N<1)
            x1 = lcg()
            x2 = lcg()
            N = N + 1
            y1 = sd * SQRT(-2.0*LOG(x1)) * COS(2*pi*(x2)) + mean
            y2 = sd * SQRT(-2.0*LOG(x1)) * SIN(2*pi*(x2)) + mean

            ! Printing x1 and x2 to allow me to use a 
            ! calculator to check program is working correctly
            print *, y1, y2, x1, x2  
        enddo
    end function muller ! note the function name @ the end here

end module rng ! Note 'rng' here added.

program lcgtest
    use rng
    implicit none
    integer :: N

    real(kind=dp) :: lcgmean, ttl = 0, sumof, lcgvar, dev1, muller1, &
                     muller2, lcgerr, lcgdev
    real, dimension(10000) :: array


    ! In the original code the variables  'lcgmean' and 'dev1' were      
    ! *undefined* before they were used in the do-loop. This will cause the   
    ! compiler to set them some random garbage values, and it will 
    ! inevitably leads to unexpected result or error in most cases.

    ! In, order to avoid this by setting them.
    ! For example, lcgmean = 1.0 and dev1 = 0.1
    ! We'll then have the following:
    lcgmean = 1.0
    dev1 = 0.1
    do N = 1, 10000
        ttl = ttl + lcg()
        dev1 = lcg() - lcgmean
        sumof = sumof + dev1  
    end do

    muller1 = muller()
    muller2 = muller()
    lcgmean = ttl/10000
    lcgvar = ((sumof)**2)/10000
    lcgdev = SQRT((sumof)**2)/10000
    lcgerr = lcgdev/100
    print *, lcg(), "mean=", lcgmean, "variance=", lcgvar, lcgerr

end program
备注:变量
seed
在函数
muller
中未使用。可能不需要它。

根据我的阅读,似乎最好用子例程替换函数muller,这样它可以同时计算y1y2。事实上,“块”*muller“目的是根据您的程序结构,基于先前生成的两个伪随机数x1和x2生成另外两个伪随机数

然后,在主程序中,不应将muller作为函数调用,而应通过在相应位置写入call muller将其作为子例程调用。但是,仍然可以使用函数而不是子例程,但要返回两个值y1y2,您可以rn是向量v,使得v(1)=y1;v(2)=y2

原始程序将变为以下内容:

module rng
    implicit none
    integer, parameter :: dp = selected_real_kind(15,300)
    real(kind=dp) :: A=100, B= 104001, M = 714025

contains

    function lcg(seed)
        implicit none    
        integer, optional, intent(in) :: seed
        real(kind=dp) :: x = 0, lcg

        if(present(seed)) x = seed
        x = mod(A * x + B, M)
        lcg = x/714025

    end function lcg ! Note 'lcg' here @ the end 

    !-------------------------------------------------------------------
    ! Subroutine muller.
    ! Here, we supply 4 arguments *y1, y2* and the optional 
    ! argaument *seed* which apparently is not required since it is not
    ! used (but can be used in order to have a better pseudo number     
    ! generator.
    !-------------------------------------------------------------------
    subroutine muller(y1, y2, seed)
        implicit none
        real(kind=dp), intent(out)     :: y1, y2
        integer, optional, intent(in)  :: seed

        ! Local variables
        real(kind=dp)                  :: x1, x2
        real(kind=dp)                  :: mean = 0.49, sd = 0.5 
        real(kind=dp)                  :: pi = 4.0*ATAN(1.0)
        integer                        :: N  = 0

        ! The **do while** loop is not needed
        ! do while (N<1)
        x1 = lcg()
        x2 = lcg()
        N = N + 1
        y1 = sd * SQRT(-2.0*LOG(x1)) * COS(2*pi*(x2)) + mean
        y2 = sd * SQRT(-2.0*LOG(x1)) * SIN(2*pi*(x2)) + mean

        ! display to the screen the values of x1, x2, y1, y2
        print *, y1, y2, x1, x2  
        ! enddo
    end subroutine muller
end module rng

program lcgtest
    use rng
    implicit none
    integer :: N
    real(kind=dp) :: lcgmean, ttl = 0, sumof, lcgvar, dev1
    real(kind=dp) :: lcgerr, lcgdev

    ! Because the variable **array** is not used, I will comment it out
    !real, dimension(10000) :: array
    real(kind=dp) :: out_lcg

    ! In the original code the variables  'lcgmean' and 'dev1' were      
    ! *undefined* before they were used in the do-loop. This will cause the   
    ! compiler to set them some random garbage values, and it will 
    ! inevitably leads to unexpected result or error in most cases.

    ! In, order to avoid this by setting them.
    ! For example, lcgmean = 1.0 and dev1 = 0.1
    ! We'll then have the following:
    lcgmean = 1.0
    dev1 = 0.1
    ! The above is true for the variables **sumof**
    sumof = 0.0
    do N = 1, 10000
        ttl = ttl + lcg()
        dev1 = lcg() - lcgmean
        sumof = sumof + dev1  
    enddo


   call muller(y1, y2)
   call muller(y1, y2)
   lcgmean = ttl/10000
   lcgvar = ((sumof)**2)/10000
   lcgdev = SQRT((sumof)**2)/10000
   lcgerr = lcgdev/100
   out_lcg = lcg()
   print *, out_lcg, "mean=", lcgmean, "variance=", lcgvar, lcgerr

end program
模块rng
隐式无
整数,参数::dp=所选的实数种类(15300)
实数(种类=dp)::A=100,B=104001,M=714025
包含
功能lcg(种子)
隐式无
整数,可选,意图(in)::种子
实际(种类=dp)::x=0,lcg
如果(存在(种子))x=种子
x=mod(A*x+B,M)
lcg=x/714025
结束函数lcg!在结束处注意“lcg”
!-------------------------------------------------------------------
!子程序穆勒。
!在这里,我们提供4个参数*y1、y2*和可选参数
!argaument*seed*显然不需要,因为它不是
!used(但可用于获得更好的伪编号
!发电机。
!-------------------------------------------------------------------
子程序研磨器(y1,y2,种子)
隐式无
真实(种类=dp),意图(输出):y1,y2
整数,可选,意图(in)::种子
!局部变量
实际(种类=dp)::x1,x2
实际值(种类=dp):平均值=0.49,标准差=0.5
实数(种类=dp)::pi=4.0*ATAN(1.0)
整数::N=0
!不需要**执行时**循环

快点(请阅读。它给出了哪个答案?你希望得到哪个答案?为什么?
y1
y2
是局部变量,你不给函数结果赋值。这是一个答案,但我不确定它是否有用:你似乎对函数如何工作缺少很多基本的理解。你能提出一个更简单的问题吗urious,为什么不在模块级别执行
double(kind=dp),parameter::pi=4.0*ATAN(1.0)
,这样就不会每次都重新计算它?@ja72它不是每次都重新计算,它是一个保存的变量。a(可能是全局的)常量会更好…请注意,这仍然是无效代码:函数结果在函数执行期间未定义
muller
。如果OP在正确使用函数方面确实存在问题,则问题相当大。感谢@fracescalus的观点。我将在下面的回答中提供一个备选方案@至少需要注意的是,这个函数不返回任何内容,应该在这个答案中进行编辑,否则它是非常不完整的。@Vladimir F。请看下面我新改进的代码,我想我已经解决了这个问题。
module rng
    implicit none
    integer, parameter :: dp = selected_real_kind(15,300)
    real(kind=dp) :: A=100, B= 104001, M = 714025

contains

    function lcg(seed)
        implicit none    
        integer, optional, intent(in) :: seed
        real(kind=dp) :: x = 0, lcg

        if(present(seed)) x = seed
        x = mod(A * x + B, M)
        lcg = x/714025

    end function lcg ! Note 'lcg' here @ the end 

    !-------------------------------------------------------------------
    ! Subroutine muller.
    ! Here, we supply 4 arguments *y1, y2* and the optional 
    ! argaument *seed* which apparently is not required since it is not
    ! used (but can be used in order to have a better pseudo number     
    ! generator.
    !-------------------------------------------------------------------
    subroutine muller(y1, y2, seed)
        implicit none
        real(kind=dp), intent(out)     :: y1, y2
        integer, optional, intent(in)  :: seed

        ! Local variables
        real(kind=dp)                  :: x1, x2
        real(kind=dp)                  :: mean = 0.49, sd = 0.5 
        real(kind=dp)                  :: pi = 4.0*ATAN(1.0)
        integer                        :: N  = 0

        ! The **do while** loop is not needed
        ! do while (N<1)
        x1 = lcg()
        x2 = lcg()
        N = N + 1
        y1 = sd * SQRT(-2.0*LOG(x1)) * COS(2*pi*(x2)) + mean
        y2 = sd * SQRT(-2.0*LOG(x1)) * SIN(2*pi*(x2)) + mean

        ! display to the screen the values of x1, x2, y1, y2
        print *, y1, y2, x1, x2  
        ! enddo
    end subroutine muller
end module rng

program lcgtest
    use rng
    implicit none
    integer :: N
    real(kind=dp) :: lcgmean, ttl = 0, sumof, lcgvar, dev1
    real(kind=dp) :: lcgerr, lcgdev

    ! Because the variable **array** is not used, I will comment it out
    !real, dimension(10000) :: array
    real(kind=dp) :: out_lcg

    ! In the original code the variables  'lcgmean' and 'dev1' were      
    ! *undefined* before they were used in the do-loop. This will cause the   
    ! compiler to set them some random garbage values, and it will 
    ! inevitably leads to unexpected result or error in most cases.

    ! In, order to avoid this by setting them.
    ! For example, lcgmean = 1.0 and dev1 = 0.1
    ! We'll then have the following:
    lcgmean = 1.0
    dev1 = 0.1
    ! The above is true for the variables **sumof**
    sumof = 0.0
    do N = 1, 10000
        ttl = ttl + lcg()
        dev1 = lcg() - lcgmean
        sumof = sumof + dev1  
    enddo


   call muller(y1, y2)
   call muller(y1, y2)
   lcgmean = ttl/10000
   lcgvar = ((sumof)**2)/10000
   lcgdev = SQRT((sumof)**2)/10000
   lcgerr = lcgdev/100
   out_lcg = lcg()
   print *, out_lcg, "mean=", lcgmean, "variance=", lcgvar, lcgerr

end program