分治法實現最近對問題(JAVA)
阿新 • • 發佈:2018-11-08
假設所有點都在集合S中。
1.用S中個點座標的中位數作為分割點,則會得到一個平衡的分割點m,使得子集S1,S2中有個數大致相同的點。
2.選取垂直線x=c(中位線)來作為分割線。
3.遞迴地求出S1和S2中的最近對,假設D1、D2是最近距離。
4.距離最近的點,可能線上的倆邊,所以,我們需要在以x=c為對稱的、寬度為2D的(D為D1、D2中的最小值)的垂直帶中。
5.在該範圍中遞迴得出最近距離。
import java.util.*; public class S2_4 { public static void main(String[] args) { Scanner s=new Scanner(System.in); int x=0,x1=0,x2=0,x3=0,x4=0; int y=0,y1=0,y2=0; double dis1=0,dis2=0; System.out.print("輸入需要生成多少個隨機點N:"); int n=s.nextInt(); int A[][]=new int[n][2]; int B[][]=new int[n][2]; int C[][]=new int[n][2]; int D[][]=new int[n][2]; for(int i=0;i<n;i++) { A[i][0]=(int)(Math.random()*100)+1;//生成100以內的隨機數,放入橫座標 } for(int i=0;i<n;i++) { A[i][1]=(int)(Math.random()*100)+1;//生成100以內的隨機數,放入縱座標 } for(int i=0;i<n;i++) { System.out.println("("+A[i][0]+","+A[i][1]+")"); } int minX = (int) Double.POSITIVE_INFINITY; //保證假設的初始最小值足夠大 int maxX = (int) Double.NEGATIVE_INFINITY; //保證假設的初始最大值足夠小 for(int i = 0; i < A.length; i++){ if(A[i][0] < minX) minX = A[i][0]; if(A[i][0] > maxX) maxX = A[i][0]; } int mid = (minX + maxX)/2; int p=0,t=0; for(int i=0;i<n;i++) { //將x座標分成倆個部分 if(A[i][0]<=mid) { B[p][0]=A[i][0]; B[p][1]=A[i][1]; p++; } else { C[t][0]=A[i][0]; C[t][1]=A[i][1]; t++; } } int d1=(int) Double.POSITIVE_INFINITY,d2=(int) Double.POSITIVE_INFINITY; //設定倆邊的距離最小值為較大的數值 int dx=0,dy=0,dz=0; for(int i=0;i<=p-2;i++) //得出d1的值 for(int j=i+1;j<=p-1;j++) { dx=(B[j][0]-B[i][0])*(B[j][0]-B[i][0])+(B[j][1]-B[i][1])*(B[j][1]-B[i][1]); if(dx<d1) { d1=dx; x1=i; //記錄倆個點的座標 x2=j; } } for(int i=0;i<=t-2;i++) //得出d2的值 for(int j=i+1;j<=t-1;j++) { dy=(C[j][0]-C[i][0])*(C[j][0]-C[i][0])+(C[j][1]-C[i][1])*(C[j][1]-C[i][1]); if(dy<d2) { d2=dy; x3=i; //記錄倆個點的座標 x4=j; } } System.out.println("mid="+mid+" "+"d1="+d1+" "+"d2="+d2); if(d1<d2) { dz=d1; dis1=Math.sqrt(dz); System.out.println("x座標中的最小距離的倆個點為:"+A[x1][0]+","+A[x1][1]+" "+A[x2][0]+","+A[x2][1]); System.out.println("最小距離為:"+dis1); x=x1; y=x2; }else { dz=d2; dis1=Math.sqrt(dz); System.out.println("x座標中的最小距離的倆個點為:"+A[x3][0]+","+A[x3][1]+" "+A[x4][0]+","+A[x4][1]); System.out.println("最小距離為:"+dis1); x=x3; y=x4; } int q=0; for(int i=0;i<n;i++) { if((mid-dis1)<=A[i][0] && A[i][0]<=(mid+dis1)) { //中心線左右倆邊的最小距離內尋找倆邊的最近點 D[q][0]=A[i][0]; D[q][1]=A[i][1]; q++; } } double mind=Double.POSITIVE_INFINITY;//mind設定為正無窮大,作為比較值 double dis=0; for(int k=0;k<=q-2;k++) for(int j=k+1;j<=q-1;j++) { dis=(D[j][0]-D[k][0])*(D[j][0]-D[k][0])+(D[j][1]-D[k][1])*(D[j][1]-D[k][1]); if(dis<mind) { mind=dis; y1=k; //記錄倆個點的座標 y2=j; } } dis2=Math.sqrt(mind); System.out.println("dis1="+dis1+" "+"dis2="+dis2); if(dis1<dis2) { System.out.println("最小距離為:"+dis1); System.out.print("倆個點分別為:"+"("+A[x][0]+","+A[x][1]+")"); System.out.println(" "+"("+A[y][0]+","+A[y][1]+")" ); }else { System.out.println("最小距離為:"+dis2); System.out.print("倆個點分別為:"+"("+A[y1][0]+","+A[y1][1]+")"); System.out.println(" "+"("+A[y2][0]+","+A[y2][1]+")" ); } } }