• 在球面上隨機均勻分布點的算法

    在球面上隨機均勻分布點的算法

    文/Sobereva @北京科音 2015-Dec-12


    寫個程序時遇到一個問題,需要得到在一個單位球面上隨機分布一批點的坐標,想了想辦法,第一個辦法是選取一個起始點(1,0,0),然后依次繞著X、Y、Z軸隨機旋轉一定的角度,Fortran代碼如下
    x=1
    y=0
    z=0
    !Step 1: Rotate about Z axis
    call RANDOM_NUMBER(rotval)
    rotval=rotval*2*pi
    xtmp=cos(rotval)*x-sin(rotval)*y
    ytmp=sin(rotval)*x+cos(rotval)*y
    x=xtmp
    y=ytmp
    !Step 2: Rotate about Y axis
    call RANDOM_NUMBER(rotval)
    rotval=rotval*2*pi
    xtmp=cos(rotval)*x-sin(rotval)*z
    ztmp=sin(rotval)*x+cos(rotval)*z
    x=xtmp
    z=ztmp
    !Step 3: Rotate about X axis
    call RANDOM_NUMBER(rotval)
    rotval=rotval*2*pi
    ytmp=cos(rotval)*y-sin(rotval)*z
    ztmp=sin(rotval)*y+cos(rotval)*z
    y=ytmp
    z=ztmp

    點不是很多的話,看起來還蠻均勻,能用,但點數多了就會發現點的分布并不均勻,100000個點時如下所示,可以隱約看到有一個環狀分布比較密集。



    出現此問題的原因是,雖然第一步得到了一個均勻隨機的環狀分布,但是第二步的時候在球的兩極已經有點的聚集現象了,第三步時把這個問題弱化了,但是聚集的兩極經過繞著X軸轉一圈后,還是略微形成了環狀密集分布區域。


    感覺不夠完美,又在網上找找有沒有什么其它方法,看到一個做法,對應的Fortran代碼:
    call RANDOM_NUMBER(rotval)
    theta = pi*rotval
    call RANDOM_NUMBER(rotval)
    phi = 2*pi*rotval
    x=sin(theta)*cos(phi)
    y=sin(theta)*sin(phi)
    z=cos(theta)
    試了下,發現很坑爹,點在兩極嚴重聚集。給出這樣算法的人真是不負責任,10000個點時分布如下所示




    于是搜英文資料,發現一個頁面很好,http://mathworld.wolfram.com/SpherePointPicking.html,專門介紹了獲得球面上隨機分布點的算法,沒想到還專門有算法解決這個問題。其中最簡單實用的Marsaglia的方法,非常好。過程是,在(-1,1)區間內隨機取兩個值x1和x2,若這兩個值的平方和大于等于1則重新選取。然后球面上的點的x、y、z的坐標用x1和x2就可以很簡單得到,Fortran代碼如下
    do while(.true.)
        call RANDOM_NUMBER(x1)
        call RANDOM_NUMBER(x2)
        x1=2*(x1-0.5D0)
        x2=2*(x2-0.5D0)
        if (x1**2+x2**2<1) exit
    end do
    x=2*x1*dsqrt(1-x1**2-x2**2)
    y=2*x2*dsqrt(1-x1**2-x2**2)
    z=1-2*(x1**2+x2**2)

    10000個點的時候分布如下,非常均勻完美

    久久精品国产99久久香蕉