產生在球面上均勻分布的點


如何產生在球面上均勻分布的點呢? 這里提供若干種思路。

1. 生成兩個隨機分布的角度,並且按照球坐標的形式產生。

缺點: 產生的點是按照角度均勻分布的, 但是注意這並不是球面上均勻分布的點,后面可以看到兩極的地方角度明顯密集。並且,由於在計算過程中有大量的三角函數的計算,程序的效率不高。 

 要求不高時可以采用這種方法。

思路是,采用10807 方法產生一組兩個隨機數,分別作為角度使用。將產生的隨機數輸出到plt 文件中,使用tecplot繪圖。(tecplot是非常好用的CFD后處理可視化的軟件,強烈推薦使用)。關於10807 方法產生隨機數,有空就另外再開一篇帖子。

下面附上 C++  代碼:

 1 #include <iostream>
 2 #include<cmath>
 3 #include<fstream>
 4 
 5 using namespace std;
 6 
 7 class cRandom
 8 {
 9     public:
10         cRandom(int x,double y):seed(x),random(y){};
11         cRandom():seed(0),random(0){};
12 
13         int seed;
14         double random;
15 };
16 
17 cRandom my_random(int z)
18 // 16807 way to create random numbers
19 // z is the seed number, num is the total random number to create
20 {
21     //z(n+1)=(a*z(n)+b) mod m
22     //describe m=a*q+r to avoid that the number is large than the computer can bear
23     const int m=pow(2,31)-1;
24     const int a=16807;
25     const int q=127773;
26     const int r=2836;
27 
28     int temp=a*(z%q)-r*(z/q);
29 
30     if(temp<0)
31     {
32         temp=m+temp;
33     }
34     //z is the seed number
35     z=temp;
36     double t = z*1.0/m;
37 
38     cRandom cr;
39     cr.random=t;
40     cr.seed=z;
41 
42     return cr;
43 }
44 
45 int main()
46 {
47     cout << "Hello world!" << endl;
48     const int num = pow(10,5);
49     int z1 = 20;
50     int z2=112;
51 
52     ofstream fcout;
53     fcout.open("result.plt");
54     fcout<<" title=random  "<<endl;
55     fcout<<"  VARIABLES = \"X\",\"Y \",\"Z \" "<<endl;
56     fcout<<"zone I= "<<num<<",datapacking=POINT"<<endl;
57     cRandom sita(z1,0.0);
58     cRandom pesi(z2,0.0);
59     const double pi = 3.141592;
60 
61     for(int i=0;i!=num;++i)
62     {
63         sita=my_random(pesi.seed);
64         pesi=my_random(sita.seed);
65 
66         double x=1.0*sin(pi*sita.random)*cos(2*pi*pesi.random);
67         double y=1.0*sin(pi*sita.random)*sin(2*pi*pesi.random);
68         double z=1.0*cos(pi*sita.random);
69 
70         fcout<<x<<" "<<y<<" " <<z<<endl;
71     }
72 
73 
74     fcout.close();
75     fcout<<"this program is finished"<<endl;
76 
77     return 0;
78 }
View Code

 效果圖是這樣滴:

             

可以看到在兩級的地方點明顯的密集。

 不要擔心,我們還有更漂亮更快速的方式產生球面上的均勻的點。

2. 三維球面上的Marsaglia 方法

這是一種基於變換抽樣的方法,產生的結果非常漂亮。原理我會單開一篇帖子講述,這里僅僅給出實行的方法。

 step1:
    隨機抽樣產生一對均勻分布的隨機數 u ,v ;這里u,v 在[-1,1] 范圍內
 step2 :
    計算 r^2 = u^2+v^2;
    如果 r^2 > 1 則重新抽樣,直到滿足 r^2 < 1 .
step3 :
    計算
  
    x=2*u*sqrt(1-r2);

    y=2*v*sqrt(1-r2);

    z=1-2*r2;  

 好了,基本原理非常簡單,直接上代碼:

 1 #include <iostream>
 2 #include<cmath>
 3 #include<fstream>
 4 
 5 using namespace std;
 6 
 7 class cRandom
 8 {
 9     public:
10         cRandom(int x,double y):seed(x),random(y){};
11         cRandom():seed(0),random(0){};
12 
13         int seed;
14         double random;
15 };
16 
17 cRandom my_random(int z)
18 // 16807 way to create random numbers
19 // z is the seed number, num is the total random number to create
20 {
21     //z(n+1)=(a*z(n)+b) mod m
22     //describe m=a*q+r to avoid that the number is large than the computer can bear
23     const int m=pow(2,31)-1;
24     const int a=16807;
25     const int q=127773;
26     const int r=2836;
27 
28     int temp=a*(z%q)-r*(z/q);
29 
30     if(temp<0)
31     {
32         temp=m+temp;
33     }
34     //z is the seed number
35     z=temp;
36     double t = z*1.0/m;
37 
38     cRandom cr;
39     cr.random=t;
40     cr.seed=z;
41 
42     return cr;
43 }
44 
45 int main()
46 {
47     cout << "Hello world!" << endl;
48     const int num = pow(10,5);
49     int z1 = 20;
50     int z2=112;
51 
52     ofstream fcout;
53     fcout.open("result.plt");
54     fcout<<" title=random  "<<endl;
55     fcout<<"  VARIABLES = \"X\",\"Y \",\"Z \" "<<endl;
56     fcout<<"zone I= "<<num/2<<",datapacking=POINT"<<endl;
57     cRandom sita(z1,0.0);
58     cRandom pesi(z2,0.0);
59 
60 
61     for(int i=0;i!=num;++i)
62     {
63         sita=my_random(pesi.seed);
64         pesi=my_random(sita.seed);
65 
66         double u=2*sita.random-1.0;
67         double v=2*pesi.random-1.0;
68 
69         double r2=pow(u,2)+pow(v,2);
70         if(r2<1)
71         {
72            double x=2*u*sqrt(1-r2);
73            double y=2*v*sqrt(1-r2);
74            double z=1-2*r2;
75 
76            fcout<<x<<" "<<y<<" " <<z<<endl;
77         }
78     }
79 
80     fcout.close();
81     fcout<<"this program is finished"<<endl;
82 
83     return 0;
84 }
View Code

效果圖是這樣的(360°無死角均勻):

 

3. 直接抽樣法產生球面上均勻分布的點

這種方法是使用直接抽樣法,產生球面上隨機分布的點,與變換抽樣法不同。

  1 #include <iostream>
  2 #include <fstream>
  3 #include<cmath>
  4 using namespace std;
  5 
  6 double is_independence(const int N);
  7     
  8 void my_random(ofstream & fcout,const int num,int z)
  9 // 16807 way to create random numbers
 10 // z is the seed number, num is the total random number to create
 11 {
 12     //z(n+1)=(a*z(n)+b) mod m
 13     //describe m=a*q+r to avoid that the number is large than the computer can bear
 14     const int m=pow(2,31)-1;
 15     const int a=16807;
 16     const int q=127773;
 17     const int r=2836;
 18 
 19     for (int i=0;i!=num;++i)
 20     {
 21         int temp=a*(z%q)-r*(z/q);
 22     
 23         if(temp<0)
 24         {
 25             temp=m+temp;
 26         }
 27         //z is the seed number
 28         z=temp;
 29         double t = z*1.0/m;
 30          
 31         fcout<<t<<endl;
 32     }
 33 }
 34 
 35 void my_randomz(ofstream & fcout,const int num,int z)
 36 // another way to create random numbers
 37 {
 38     const int m=100000001;
 39     const int a=329;
 40     const int q=303951;
 41     const int r=122;
 42 
 43     for (int i=0;i!=num;++i)
 44     {
 45         int temp=a*(z%q)-r*(z/q);
 46 
 47         if(temp<0)
 48         {
 49             temp=m+temp;
 50         }
 51         z=temp;
 52         double t = z*1.0/m;
 53 
 54         fcout<<t<<endl;
 55     }
 56 }
 57 
 58 double is_uniform(const int N,const int K)
 59 //judge whether the random is uniform
 60 {
 61 
 62     ifstream fcin;
 63     fcin.open("re2.dat");
 64     double esp(0);
 65     double total(0);
 66     for (int i = 0; i != N; ++ i)
 67     {
 68         double temp;
 69         fcin>>temp;
 70         total += pow(temp,K);
 71     }
 72     esp = total/N-1.0-1/(K+1);
 73     fcin.close();    
 74     return esp;
 75 }
 76 
 77 double is_independence(const int N)
 78 //judge whether the randoms are independence
 79 {
 80     ifstream fcin;
 81     fcin.open("re2.dat");    // open the file
 82     if(!fcin)
 83     {
 84         cout<<"error! :open file error!"<<endl;
 85         return -1.0;
 86     }
 87 
 88     double esp(0);
 89     double xSq(0);
 90     double x(0);
 91     double xy(0);
 92     
 93     double temp(0);      //start to input the random number
 94     fcin>>temp;
 95     double oldTemp(0);
 96 
 97     for(int i=0;i!=N;++i)
 98     {
 99         oldTemp=temp;
100 
101         x+=temp;
102         xSq+=temp*temp;
103 
104         fcin>>temp;
105         xy +=temp*oldTemp;
106     }
107     
108     fcin.close();
109 
110     double cResult(0);
111     cResult=(xy-x*x)/(xSq-x*x);
112     return cResult;
113 }
114 
115 int main()
116 {
117     cout<<"hello,world!"<<endl;
118     int num=pow(10,7);  //the total number of randoms
119     int z=45;     //the seed of the program
120 /*
121     // get random number using 10807 and another way
122     //save the result in two files
123 
124     ofstream fcout;
125     fcout.open("re1.dat");
126     my_random(fcout,num,z); //call 16807 way to create random number
127     fcout.close();
128     fcout.open("re2.dat");
129     my_randomz(fcout,num,z); //call 16807 way to create random number
130     fcout.close();
131 */
132     
133     //const int N = pow(10,3);  // the number of the random
134     int N(0);
135     cout<<"inter the number of the checked number:"<<endl;
136     cin >> N;
137 
138     const int K = 10;  //the section number divide
139     double esp(0);
140     esp=is_uniform(N,K);
141     cout<<"uniform number is "<<"esp="<<esp<<endl;
142 
143     esp=is_independence(N);
144     cout<<"indenpendence number is "<<"esp="<<esp<<endl;
145 
146     return 0;
147 }

 

 

4.力學方法產生。

這種方法是基於力學原理產生的,球面上的點是主動運動的,他們相互排斥,直到一個最均勻的狀態。

這里是傳送門: http://zhidao.baidu.com/link?url=hN6nj60BC0RvMZorGtT6VToPH2T9cXcC26MwL2G94e_nFSUPXUstInmXxwydCx-0PI3l4jlKnCv2ZvyLrVsA4oe3b-3PldLS2V2D-hcH0OO

思路是: 對球面上的每一個點施加 坐標: x,y,z; 施加速度,vx,vy,vz ; 計算其余點對當前點的作用力,求解一個微分方程組。初始狀態隨機分布,每一個計算步更新當前粒子的運動速度和位移。

傳送門的matlab代碼:

function []=main()
    N=12; %點數量
    a=rand(N,1)*2*pi;  %根據隨機求面均勻分布,先生成一個初始狀態
    b=asin(rand(N,1)*2-1);
    r0=[cos(a).*cos(b),sin(a).*cos(b),sin(b)];
    v0=zeros(size(r0));
    G=1e-2;%斥力常數,試驗這個值比較不錯
 
   for ii=1:200%模擬200步,一般已經收斂,其實可以在之下退出
         [rn,vn]=countnext(r0,v0,G);%更新狀態
          r0=rn;v0=vn;
    end

    plot3(rn(:,1),rn(:,2),rn(:,3),'.');hold on;%畫結果
    [xx,yy,zz]=sphere(50); 
    h2=surf(xx,yy,zz); %畫一個單位球做參考
    set(h2,'edgecolor','none','facecolor','r','facealpha',0.7);
    axis equal;
    axis([-1 1 -1 1 -1 1]);
    hold off;

 end

function [rn vn]=countnext(r,v,G) %更新狀態的函數
%r存放每點的x,y,z數據,v存放每點的速度數據
    num=size(r,1);
    dd=zeros(3,num,num); %各點間的矢量差
    
    for m=1:num-1
          for n=m+1:num
              dd(:,m,n)=(r(m,:)-r(n,:))';
              dd(:,n,m)=-dd(:,m,n);
          end
     end 

      L=sqrt(sum(dd.^2,1));%各點間的距離
      L(L<1e-2)=1e-2; %距離過小限定
      F=sum(dd./repmat(L.^3,[3 1 1]),3)';%計算合力

      Fr=r.*repmat(dot(F,r,2),[1 3]); %計算合力徑向分量
      Fv=F-Fr; %切向分量

       rn=r+v;  %更新坐標
       rn=rn./repmat(sqrt(sum(rn.^2,2)),[1 3]);
       vn=v+G*Fv;%跟新速度
end
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM