如何產生在球面上均勻分布的點呢? 這里提供若干種思路。
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 }
效果圖是這樣滴:

可以看到在兩級的地方點明顯的密集。
不要擔心,我們還有更漂亮更快速的方式產生球面上的均勻的點。
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 }
效果圖是這樣的(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
