1. 程式人生 > >分治法在求解“最近對”問題中的應用(JAVA)

分治法在求解“最近對”問題中的應用(JAVA)

分治法在求解“最近對”問題中的應用

最近對問題在蠻力法中有過講解,時間複雜度為O(n^2),下面將會採用分治法講解這類問題,時間複雜度會降到O(nlogn)

我們將笛卡爾平面上n>1個點構成的集合稱為P。若2<= n <= 3時,我們1可以通過蠻力法求解。但當n>3時,採用分治法或許是個更好的選擇。假設這些點是按照x軸、y軸升序排列的,可以找出點集在x軸方向上的中位數m,做一條垂直x軸的分割線,由此點將點集劃分為左右兩個大小為n/2的子集P1和P2,之後通過遞迴求解出在子集中的最近對距離d1,d2,最後找出d=max{d1,d2}。

但是!!!不巧的是,我們忽略了一個問題,如果距離最近的兩個點剛好分別在兩個子集中,那麼d就不是所有點對的最小距離。我們需要在每次合併子問題結果時,要加以判斷是否存在這樣的點對。方法是:只考慮以分割線為對稱軸、寬度為2d的垂直帶中的的點,因為其他點對的距離都是大於d的。

這裡給出一個優化,當我們在垂直帶中找到一個點p,只需要考慮p之後的5個點即可。

這是因為:如果我們在垂直帶中找到p-p'兩點的距離小於p,由於我們的序列時經過排序的,所以p'一定在p之後,且兩點在y軸上的距離一定是小於d的(根據勾股定理,兩點之間的距離如果小於d,那麼x軸分量和y軸分量都是小於d的,反之,不可能存在這個點)。所以在幾何學上,p'的位置一定在下圖中的淡黃色矩形區域。而矩形區域內一般只能包含少量的候選點,這個數量最大為6(根據鴿巢定理)。圖中6個紅色點為極端的臨界情況。我們將d * 2d的矩形劃分為d/2 * 2d/3的6塊區域,如果超過6個點,假設為7,那麼一定會出現某個小矩形中有兩個點,這兩個點的最大距離為圖中紅線距離5/6d <d,這和d的意義不符。


import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;

class Point {
    double x;
    double y;
    Point (double x, double y) {
        this.x = x;
        this.y = y;
    }
}
public class Main {
    static Point[] point;
    static Point[] minP = new Point[2];
    static Scanner in = new Scanner(System.in);
    public static void main(String[] args) {
        int n = in.nextInt();
        point = new Point[n];
//        for (int i = 0; i < n; i++) {
//            int a = in.nextInt();
//            int b = in.nextInt();
//            point[i] = new Point(a, b);
//        }
        point[0] = new Point(1,3);
        point[1] = new Point(2,1);
        point[2] = new Point(3,5);
        point[3] = new Point(4,4);
        point[4] = new Point(5,2);
        Arrays.sort(point,0, n, new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                return (int) (o1.x - o2.x);
            }
        });
        System.out.println(point.length);
        double minD = closestPoint(0, point.length-1);
        for (int i = 0; i < 2; i++) {
            System.out.println(minP[i].x + "," + minP[i].y);
        }
        System.out.println(minD);
    }

    private static double closestPoint(int low, int high) {
        Point[] temp1 = new Point[2];
        Point[] temp2 = new Point[2];
        Point[] p = new Point[high - low + 1];
        double d, d1, d2, d3;
        int index = 0;
        if (high - low == 1) {
            minP[0] = new Point(point[low].x, point[low].y);
            minP[1] = new Point(point[high].x, point[high].y);
            return distance(point[low], point[high]);
        }
        if (high - low == 2) {
            d1 = distance(point[low], point[low+1]);
            d2 = distance(point[low+1], point[high]);
            d3 = distance(point[low], point[high]);
            if ((d1 <= d2) && (d1 <= d3)) {
                minP[0] = new Point(point[low].x, point[low].y);
                minP[1] = new Point(point[low+1].x, point[low+1].y);
                return d1;
            } else if (d2 <= d3) {
                minP[0] = new Point(point[low+1].x, point[low+1].y);
                minP[1] = new Point(point[high].x, point[high].y);
                return d2;
            } else {
                minP[0] = new Point(point[low].x, point[low].y);
                minP[1] = new Point(point[high].x, point[high].y);
                return d3;
            }
        }
        int mid = (low + high) / 2;
        d1 = closestPoint(low, mid);
        temp1[0] = minP[0];
        temp1[1] = minP[1];
        d2 = closestPoint(mid+ 1, high);
        temp2[0] = minP[0];
        temp2[1] = minP[1];
        if (d1 < d2) {
            d = d1;
            minP[0] = temp1[0];
            minP[1] = temp1[1];
        } else {
            d = d2;
            minP[0] = temp2[0];
            minP[1] = temp2[1];
        }
        for (int i = mid;i>=low && (point[mid].x - point[i].x) < d; i--) {
            p[index++] = point[i];
        }
        for (int i = mid+1;i<=high && (point[i].x - point[mid].x) < d; i++) {
            p[index++] = point[i];
        }
        Arrays.sort(p, 0, index, new Comparator<Point>() {
            @Override
            public int compare(Point o1, Point o2) {
                return (int) (o1.y - o2.y);
            }
        });
        for (int i = 0; i < index-1; i++) {
            for (int j = i+1; j < index; j++) {
                if ((p[j].y - p[i].y) >= d) {
                    break;
                } else {
                    d3 = distance(p[i], p[j]);
                    if (d3 < d) {
                        minP[0] = new Point(p[i].x, p[i].y);
                        minP[1] = new Point(p[j].x, p[j].y);
                    }
                }
            }
        }
        return d;
    }

    private static double distance(Point p1, Point p2) {
        return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
    }
}

Input:

5

Output:

4.0,4.0
3.0,5.0
1.4142135623730951

相關推薦

治法實現最大子陣列問題Java

終於把這個搞出來了,中間出現了好多小問題,虛擬碼和演算法思想可以參考演算法導論。package com.alibaba; public class MaxSubarray { public sta

治法求解最近”問題應用JAVA

分治法在求解“最近對”問題中的應用 最近對問題在蠻力法中有過講解,時間複雜度為O(n^2),下面將會採用分治法講解這類問題,時間複雜度會降到O(nlogn) 我們將笛卡爾平面上n>1個點構成的集合稱為P。若2<= n <= 3時,我們1可以通過蠻力法求解。

蠻力法在求解最近”問題應用JAVA

最近對問題是在計算幾何問題中最簡單的,是指在一個包含n個點的集合中,找到距離最近的兩個點,我們這裡只研究二維空間中的版本,高維計算基本類似,區別只在於計算兩點之間距離的公式略有不同,下面是標準的歐幾里

治法實現最近問題JAVA

  假設所有點都在集合S中。   1.用S中個點座標的中位數作為分割點,則會得到一個平衡的分割點m,使得子集S1,S2中有個數大致相同的點。   2.選取垂直線x=c(中位線)來作為分割線。   3.遞迴地求出S1和S2中的最近對,假設D1、D2是

治法在排序演算法應用JAVA--快速排序Lomuto劃分、Hoare劃分、隨機化快排

分治法在排序演算法中的應用 快速排序:時間複雜度O(nlogn) 如果說歸併排序是按照元素在陣列中的位置劃分的話,那麼快速排序就是按照元素的值進行劃分。劃分方法由兩種,本節將主要介紹Huare劃分,在減治法在查詢演算法中的應用(JAVA)--快速查詢這篇文章中講述了Lomu

演算法設計--蠻力法&&治法最近問題C++實現

最近對問題? 設p1=(x1,y1), p2(x2,y2), ....,pn=(xn,yn)是平面上n個點構成的集合S,最近對問題就是找出集合S中距離最近的點對。 兩種演算法思想: 1. 蠻力法:顧名思義,利用正常的思維,使用強硬的方式求解出結果。 2. 分治法:分治,分而

治法最近問題

首先感謝博主https://www.cnblogs.com/zuoyou151/p/9059903.html,讓我收穫很多,今天感覺很困,狀態不佳,解析與講解會改天補上,註明:我這裡採用遞迴時是左閉右開區間,而博主採用的左閉右閉。 還有感謝“NX”童鞋為我調好VS2017,之前因為環境問題一

貪婪演算法在求解最小生成樹應用JAVA--Kruskal演算法

Kruskal演算法又被稱為“加邊法”,這種演算法會將加權連通圖的最小生成樹看成具有V-1條邊的無環子圖,且邊的權重和最小。演算法開始時,會按照權重的非遞減順序對圖中的邊排序,之後迭代的以貪婪的方式新增邊。下面以下圖為例來講解Kruskal演算法的過程:Input:6 101

蠻力法在求解凸包問題應用JAVA

凸包問題向來是計算幾何中最重要的問題之一,許多各式各樣的應用大多要麼本身就是圖凸包問題要麼其中一部分需要按照凸包問題解決。 凸集合定義:對於平面上一個點集合,如果集合中的任意兩點p和q為端點的線段都屬於該集合,那麼稱這個集合為凸集合。 凸包定義:一個點集合S的凸包是包含S的

治法在查詢演算法應用JAVA--快速查詢

減治法在查詢演算法中的應用 快速查詢:選擇問題是求一個n個數列表的第k個最小元素的問題,這個數k被稱為順序統計量。對於k=1或k=n來說,這並沒有什麼意義,我們通常會要找出這樣的元素:該元素比列表中一

蠻力法和治法最近問題——Java 實現

public class ClosestPair2{ public static void main(String[] args) {  /**   *輸入需要比較的點的對數存在變數n中   */  Scanner in=new Scanner(System.in);  System.out.println(

jieba詞的應用java

在上一篇說的猜你喜歡功能中,又加了新的需求,需要對關鍵詞進行分詞,擴大推薦文章的範圍,這樣能夠拓展使用者的喜歡範圍,這時候我就想到可以用jieba分詞對中文進行分詞,同樣的需要去官網下載原始碼,這樣方便自己對原始碼的修改以達到自己的目的。這裡,我需要判斷切分出來

Redis在三層服務框架應用——Redis與Model的結合

個人宣告:本系列所有文章旨在拋磚引玉,為有興趣深入使用Redis的同學提供一些參考。本系列所有文章純屬原創,均是筆者在實際工作中的總結。本文所有引用的MDB系列元件均由米多網路架構部提供,在此向架構部表示感謝。筆者經驗能力有限,如有不適之處還請多多指教。一、為什麼要使用Red

劍指offer:資料流位數java

/** * 題目: * 如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值, * 那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值, * 那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Ins

LeetCode|4.兩個排序陣列的位數Java

給定兩個大小為 m 和 n 的有序陣列 nums1 和 nums2 。請找出這兩個有序陣列的中位數。要求演算法的時間複雜度為 O(log (m+n)) 。示例 1:nums1 = [1, 3] nums2 = [2] 中位數是 2.0 示例 2:nums1 = [1, 2]

治法求解平面n點距離最近的兩點

步驟4:將L-d~L+d內的點以y值排序,對於每一個點(x1,y1)找出y值在y1-d~y1+d內的所有點,計算距離為d'。                 如果d'小於d,令d=d',最後的d值就是答案。 實驗1  遞迴與分治演算法 一,實驗目的和要求 (1)進一步掌握遞迴演算法的設計思想以及遞迴程式的除錯

矩陣相乘問題治法求解

採用蠻力+分治進行求解: 矩陣相乘公式: 程式碼: public class Matrix { //初始化一個隨機nxn階矩陣 public static int[][] initializationMatrix(int n){

【演算法導論】【筆記】【治法最近問題

尋找最近點對 目錄 尋找最近點對 問題分析 分治法 時間複雜度 題目:在一個 n≥2 個點的集合 Q 中尋找距離最近的點對 問題分析 首先很容易想到暴力破解的方法,但是需要 O(n2) 的時間複雜度,所以考慮使用分治法,但

治法求解兩個升序陣列的位數

#include<iostream> using namespace std; //求序列的中位數 float mid(int m, int n,int arry[]) { int middle = (n+m+1)/2; floa

大型網站架構系列:緩存在布式系統應用

內存空間 設備 keep 訪問速度 整數 存儲方式 統一 客戶端 物理內存 原文:大型網站架構系列:緩存在分布式系統中的應用(二)緩存是分布式系統中的重要組件,主要解決高並發,大數據場景下,熱點數據訪問的性能問題。提供高性能的數據快速訪問。 本文是緩存在分布式應用第二篇文