亂數的產生與使用對於電腦科學以及自然科學都有很重大的意義,20世紀初電腦的出生一部分的任務就是為了利用亂數計算出原子彈能不能爆炸。到了現代,統計學、電腦模擬、基因演算法等等先進的演算法仍然是建立在亂數上。最重要的,樂透電腦選號網路遊戲掉寶網路麻將洗牌註冊認證碼等等現代虛擬世界常見的功能,也是用亂數做的!!所以,學會怎麼使用亂數是很重要的!
  Fortran 90語言的標準中規定了標準亂數副程式RANDOM_SEED()以及RANDOM_NUMBER(),在F90以前的F77則沒有統一的標準。使用亂數前必須呼叫RANDOM_SEED()函式將亂數函數中的種子值(seed)初始化,不先呼叫本函數、或種子值固定,都會造成新手常見的「每次執行產生的亂數結果都相同」問題。大多數的Fortran compiler為了方便起見在使用者呼叫RANDOM_SEED()時會自動代入系統內的clock作為seed。GCC系列的Fortran則一定需要使用者自行設定seed值。一般而言,只需在程式開始使用亂數前呼叫一次RANDOM_SEED()即可,不必重複呼叫 (也不要不小心把RANDOM_SEED()包在迴圈裏面)。RANDOM_NUMBER(rand)是一個亂數副程式,使用時把要設為亂數的變數rand傳進去即可得到亂數,rand必須是浮點數型態。Fortran很特別的一點是,傳進去的參數rand可以是任意維度的陣列,所以可以一次得到大量的亂數。
  在下面的程式中阿賢最簡單的示範如何使用RANDOM_SEED()、RANDOM_NUMBER()將亂數初始化,並印出來。如果是使用gfortran,則需要改成呼叫INIT_RANDOM_SEED()這個自訂的副程式(不是使用gfortran也可以用)。在INIT_RANDOM_SEED()內示範了如何使用SYSTEM_CLOCK()作為亂數seed的基礎。

Fortran_code.GIF
program random_test
implicit none
  real*8 :: x(10)

  call random_seed()
! call init_random_seed() !<- this line is for GFortran
  call random_number(x)
  write(*,'(F21.18)') x
end program

SUBROUTINE init_random_seed()
  INTEGER :: i, n, clock
  INTEGER, ALLOCATABLE :: seed(:)

  call RANDOM_SEED(SIZE = n)
  ALLOCATE(seed(n))
  call SYSTEM_CLOCK(COUNT=clock)
  seed = clock - 2047 * (/ (i - 1, i = 1, n) /)
  seed = seed * 1103515245 + 2531011
  call RANDOM_SEED(PUT = seed)
  DEALLOCATE(seed)
END SUBROUTINE

  下面是程式在Compaq Visual Fortran中的一次執行結果,每次執行結果應該都會有所不同:

C_code.GIF
 0.454882986687613622
 0.794924496937812641
 0.454520097297712367
 0.896909541095284313
 0.347645981027701334
 0.001694017156954599
 0.617503421142598707
 0.835969154749705678
 0.080039571879135102
 0.404321436475645890

   RANDOM_NUMBER產生的亂數範圍為: 0 <= rand < 1,產生的亂數理論上是均勻分布在0~1之間。RAND_MAX是RANDOM_NUMBER產生的亂數的有效範圍,視rand的型態而定。以目前常見的電腦系統來說,32bits浮點數(real*4)的RAND_MAX的值為224-1=16777215;64bits浮點數(real*8)的RAND_MAX的值為253-1=9007199254740991。一般而言RAND_MAX的值愈大愈好,原因留待下回分解。


創作者介紹
創作者 latinboy 的頭像
latinboy

阿賢的部落格

latinboy 發表在 痞客邦 留言(7) 人氣()


留言列表 (7)

發表留言
  • s359999
  • 請問如何產生常態分佈(高斯分佈)的亂數

    請問如何產生常態分佈(高斯分佈)的亂數, 請問他的範圍落在哪?? -1到1之間嗎?? 他的變異數又要如何設定呢??
    謝謝 或是我要參考哪本書比較好呢
  • 以下為C示範程式碼
    其中frand31就是一個普通uniform random number,0<=數值<1。產生的高斯亂數平均值為0,變異數為1。
    要改變產生的數值,用高斯基本定義:
    grandx = grand * 變異數 + 平均值
    這樣就可以囉!

    double grand(void){
    double logE, theta;
    for( logE = frand31(); logE == 0;);
    logE = sqrt( -2. * log( logE ) );
    theta = frand31() * 3.14159265358979324;
    return logE * cos( theta );
    }

    latinboy 於 2010/03/12 23:05 回覆

  • leafy
  • 請問一下如何在30*30的二維裡產生範圍0<i<30,O<j<30的亂數用蒙地卡羅
  • fortran可直接產生random number的陣列
    --程式碼開始--
    real*8 :: a(30,30)
    call random_number( a )
    a = a * 30
    --程式碼結束--
    這樣產生的亂數 0<=i,j<30
    如果不要產生0,可以用再自行處裡一下完成的陣列

    latinboy 於 2010/03/12 22:55 回覆

  • Victor
  • 請問我照格主一樓回覆的方式把高斯分布產生亂數寫成fortran碼如下:
    real g
    call RANDOM_SEED()
    do i=1, 100
    g = sqrt(-2*log(x))*cos(x*3.14159)
    end do
    請問這樣的式子對嗎?因為我發現跑出來的亂數會超過正1,所以不知道這樣寫對不對。謝謝!
  • real g, x, y
    call RANDOM_SEED()
    do i = 1, 100
    call random_number(x)
    call random_number(y)
    g = sqrt(-2*log(x))*cos(y*3.1415926535)
    end do
    必須要使用2個亂數值
    另外,產生出來的數字超過正負1是正常的,請研究一下高斯亂數的定義

    latinboy 於 2010/07/24 16:34 回覆

  • FS
  • 阿賢樓主您好

    我的疑問是產生亂數一定是在0與1之間嗎?是否其她的方法使產生的亂數在其他範圍?

    1. 比如說0到2*Pi之間,這個好做,我的方法是:

    REAL RR, Theta ! 宣告RR以及角度Theta

    Call random_seed() ! 呼叫函數

    Call random_number(RR) ! 產生0與1之間的亂數

    RR = RR*2*3.14 ! 產生的亂數再乘上2*PI

    Theta = RR ! 產生的亂數再乘上2*PI等於宣告的角度

    S12 = Sin(Theta) ! 算出正弦值

    2. 如果我要以某一個數值(比如1)為中心,以0.5為半徑,在1+-0.5的範圍取亂數。

    這就有一點困難了,我思考中...想好的話再貼上來交流討論...
  • FS
  • REAL RR(5), S12, S12_delta

    Call random_seed() ! 呼叫函數

    Call random_number(RR(1)) ! 產生0與1之間的亂數

    ! 我要以3.5為中心,正負0.0035為範圍取亂數,
    ! 也就是3.5±0.0035之間的範圍取亂數。

    RR(2) = 3.5*(2*RR(1)-1) ! 以3.5為中心,正負3.5為範圍取到的亂數。

    RR(3) = RR(2)*0.01 ! 把正負3.5範圍縮小0.01

    S12 = Sin(3.5) ! 算出正弦值

    S12_delta = Sin(3.5+RR(3)) ! 用亂數做一個小小的擾動得到的正旋值。


  • 黃帥舞
  • 謝謝你~我學到好多東西唷!!!
  • 陳凱
  • 樓主你好~想問~如果範圍-1,1分佈要怎麼寫勒?
  • random_number() * 2 - 1

    latinboy 於 2016/12/28 10:12 回覆