亂數的產生與使用對於電腦科學以及自然科學都有很重大的意義,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) 人氣()