两点之间的最短距离(不相交集合)

15

图片描述

这个问题是在两个不相交集合之间求最近点对。 上面的图片表示了这个问题。有两种不相交的集合,-x平面上的蓝色点和+x平面上的红色点。

我想要计算一个蓝点和一个红点之间的最小距离 (距离为 |y2-y1| + |x2 - x1|) ,而且我认为使用二分查找来寻找距离。如何在这种情况下使用二分查找呢? 我遇到的困难在于将两个不相交集合表达为二分查找。我已经知道了单个集合的情况,但我不知道在两个不相交集合的情况下应该怎么做。

++ ) 使用Delaunay三角测量可以在线性时间内解决吗?(啊,这只是我的好奇心,我想使用二分查找)

下面的代码是我已经编写的一个集合情况的代码(使用问题解决技术、分治),并转化为两个不相交集合。我不明白在两个集合的情况下该怎么做。 例如,提示。好吧,请有人帮帮我?

#include <iostream>
#include <algorithm>
#include <iomanip>
#include <cmath>

/**
test input
10
-16 -4 
-1 -3 
-9 -1 
-4 -10 
-11 -6 
-20 4 
-13 6 
-3 -10 
-19 -1 
-12 -4
10
8 2
10 3 
10 10 
20 -3 
20 3 
16 2
3 -5 
14 -10
8 -2 
14 0

10
-3 39
-2 -28
-1 20
-3 11
-3 45
-2 -44
-1 -47
-5 -35
-5 -19
-5 -45
10
27 5
28 0
28 5
21 5
2 3
13 -1
16 -2
20 -2
33 -3
27 1
 **/


using namespace std;

const int MAX = 10001;

struct point{
    int x,y;
};

bool xCompare(struct point, struct point);
bool yCompare(struct point, struct point);
int dis(struct point, struct point);

int absd(int);
int trace(int,int,int,int);

point p[MAX], q[MAX], tmp[MAX];

int main(){

    int left;
    int right;

    scanf("%d\n", &left);
    memset(p,0,sizeof(p));
    memset(q,0,sizeof(q));
    memset(tmp,0,sizeof(tmp));


    for(int i=0; i<left; i++){
        cin >> p[i].x >> p[i].y;
    }

    scanf("%d\n", &right);

    for(int j=0; j<right; j++){
        cin >> q[j].x >> q[j].y;
    }

    sort(p, p+left, xCompare);
    sort(q, q+right, xCompare);

    int min = trace(0,0, left-1, right-1);

    printf("%d\n", min);


    /** this is one set case.
    while(true){
        cin >> n;

        if(n == 0)  break;

        memset(p,0,sizeof(p));
        memset(tmp,0,sizeof(tmp));

        for(int i= 0;i<n;i++)
            cin >> p[i].x >> p[i].y;

        sort(p,p+n,xCompare);

        int min = trace(0,n-1);

        if(min < 10000 && n > 1){
            cout << fixed;
            cout << setprecision(4) << min << endl;
        }
        else
            cout << "INFINITY" << endl;
    }
     **/

    return 0;
}

int trace(int low1, int low2, int high1, int high2){

    if(high1 - low1 < 3){
        int value = dis(p[low1],q[low2+1]);
        int nextValue;

        if(high1 - low1 == 2){  
            nextValue = dis(p[low1],q[low2+2]);

            if(value > nextValue)
                value = nextValue;

            nextValue = dis(p[low1+1],q[low2+2]);

            if(value > nextValue)
                value = nextValue;
        }
        return value;
    }
    else{

        /* DIVIDE & QONQUER */

        int mid1 = (low1 + high1) >> 1;
        int mid2 = (low2 + high2) >> 1;
        int cnt = 0;

        int leftValue = trace(low1,low2,mid1,mid2);     // left trace
        int rightValue = trace(mid1+1,mid2+1,high1,high2);  // right trace

        // min value find
        int value = leftValue < rightValue ? leftValue : rightValue;

        /* Middle Condition Check : Y Line */

        // saving left
        for(int i = low1;i<=mid1;i++){
            if(abs(p[i].x - q[mid2].x) <= value)
                tmp[cnt++] = p[i];
        }

        // saving right
        for(int i = mid1+1;i<=high1;i++){
            if(absd(p[i].x - q[mid2+1].x) <= value)
                tmp[cnt++] = p[i];
        }

        sort(tmp,tmp+cnt,yCompare);

        for(int i = 0;i<cnt;i++){
            int count = 0;

            for(int j = i-3;count < 6 && j < cnt;j++){
                if(j >= 0 && i != j){
                    int distance = dis(tmp[i],tmp[j]);

                    if(value > distance)
                        value = distance;

                    count++;
                }
            }
        }
        return value;
    }
}

int absd(int x){
    if( x < 0)
        return -x;
    return x;
}

int dis(struct point a, struct point b){
    return (abs(a.x-b.x) + abs(a.y-b.y));
}

bool xCompare(struct point a, struct point b){
    return a.x < b.x;
}

bool yCompare(struct point a, struct point b){
    return a.y < b.y;
}

这是一个最近邻问题。http://en.wikipedia.org/wiki/K-nearest_neighbor_algorithm 似乎是一道课堂作业题,是吗? - madmik3
我解决ACM问题~ :) 尤其是计算几何和图形。 - Silvester
德劳内三角剖分包含一棵最小生成树,而这棵树又包含穿过切割的最便宜的边(蓝点、红点)。 - Per
@Per:这可能值得一份答案? - Matthieu M.
蓝点在-x平面上,红点在+x平面上。这是一直都这样,还是巧合吗? - Per
您可以尝试使用此代码来获取答案:http://www.codeguru.com/cpp/cpp/algorithms/general/article.php/c8901/Delaunay-Triangles.htm - dogrush
4个回答

5
这个问题通常被称为最近的两个不同颜色 pair 问题。以下是一些方法。
  1. 德劳内三角剖分。(如果是L2(= 欧几里得)距离,这肯定有效;我认为步骤推广到L1。) 对于每个德劳内三角剖分(在退化情况下可能会有多个),都存在一棵最小生成树,其所有边都属于三角剖分。反过来,这棵最小生成树包含一条穿过颜色类之间的最短边。

  2. 最近邻数据结构。

  3. 如果已知红点与蓝点在x方向上分开,则可以适应Shamos - Hoey分治算法的O(n)合并步骤以解决最接近(单色)pair问题,具体描述见这里


1
如果你想在空间数据上进行二分查找,你可以使用一种空间数据结构,比如四叉树或k-d树。

0

这里还有一种解决方案。它的基本原理是将两组点加载到两个kd树实例中(具有在x和y轴上切割树的构建机制),然后通过检查每个节点来遍历两个树,如果两个节点之间的距离小于前一个节点之间的距离,则继续直到找不到任何两个节点的相互距离小于其他任何距离。

下面的代码打印了在节点之间导航时发现的距离并将其打印出来。两组点也可以可视化以查看算法的正确性。

无论一组点是否嵌套在另一组点的右侧、左侧、上方或下方,此代码都可以正确地找到最近的节点。

 #include <iostream>

using namespace std;

int const k=2; // the number of dimensions
double min_distance = 10000; // set a large default value, in this example all distance will be shorter than this. 

double distance(int arr[], int arr2[])
{
 return sqrt(pow(arr2[0] - arr[0], 2) + pow(arr2[1] - arr[1], 2));

}

struct Node {
 int point[k];
 Node *left, *right;
 Node()
 {
  left = right = NULL;  

 }
};

// A method to create a node of K D tree
struct Node* newNode(int arr[])
{
 struct Node* temp = new Node;

 for (int i = 0; i<k; i++) temp->point[i] = arr[i];

 return temp;
}

Node * insertNode(Node * node, int arr[], int d)
{
 if (node == NULL)
  return newNode(arr);

 int dim = d%k;


 if (node->point[dim] > arr[dim])
 {


  node->left = insertNode(node->left, arr, dim + 1);
 }
 else
 {

  node->right = insertNode(node->right, arr, dim + 1);
 }

 return node;
}
Node * Nearest=NULL;

Node * FindnearestNode(Node * head1, int arr[], int d)
{
 // if empty tree, return
 if (head1 == NULL)
  return NULL;

 // check for each tree. 
   if (min_distance > distance(head1->point, arr))
 {
  min_distance = distance(head1->point, arr);
  Nearest = head1;
 }

 if (head1->left == NULL && head1->right == NULL)
  return head1;

 // findout current dimension, in this case it either x or y i.e. 0 or 1
 int dim = d%k;

 // navigate through the tree as if inserting to a new member (to remain to the nearest member in closeness). in the path for insert it will find the nearest member. 
 if (head1->right && head1->point[dim] < arr[dim]) return FindnearestNode(head1->right, arr, d+1);
 else if(head1->left && head1->point[dim] > arr[dim] )
  return FindnearestNode(head1->left, arr, d+1);

 return Nearest;
}


int main()
{
 int const an = 10;
 int const bn = 10;

 int ax[an] = { 34,55,11,79,77,65,3,9,5,66 };
 int ay[an] = { 5, 6, 7, 9, 32,3,15,7,10,35 };

 int bx[bn] = { 5,35,4,41,32,64,41,54,87,3 };
 int by[bn] = { 23,33,17,15,32,22,33,23,21,32 };



 Node * head1=NULL;
 Node * head2 = NULL;



 double Final_Min_Distance = min_distance;

 // fill the K-D trees with the two dimensional data in two trees.
 for (int i = 0; i < an; i++)
 {
  int temp[k];
  temp[0] = ax[i];
  temp[1] = ay[i];

  head1=insertNode(head1, temp, 0);
  temp[0] = bx[i];
  temp[1] = by[i];

  head2=insertNode(head2, temp, 0);


 }
 Node * AnearB=NULL;

 Node * BnearA = NULL;



 min_distance = 1000;
   Final_Min_Distance = min_distance;
 for (int i = 0; i < an; i++) { int temp[k]; temp[0] = bx[i]; temp[1] = by[i]; Node * Nearer2 = FindnearestNode(head1, temp, 0); if (Final_Min_Distance > min_distance)
  {
   BnearA = Nearer2;
   Final_Min_Distance = min_distance;
  }
  cout << " distance of B (" << temp[0] << "," << temp[1] << ") to nearest A (" << BnearA->point[0] << "," << BnearA->point[1] << ") distance:" << Final_Min_Distance << endl;
  min_distance = 1000;


 }
 cout << "Minimum Distance is " << Final_Min_Distance<<endl<<endl;


 min_distance = 1000;
 Final_Min_Distance = min_distance;
 for (int i = 0; i < an; i++) { int temp[k]; temp[0] = ax[i]; temp[1] = ay[i]; Node * Nearer2 = FindnearestNode(head2, temp, 0); if (Final_Min_Distance > min_distance)
  {
   AnearB = Nearer2;
   Final_Min_Distance = min_distance;
  }
  cout << " distance of A (" << temp[0] << "," << temp[1] << ") to nearest B (" << AnearB->point[0] << "," << AnearB->point[1] << ") distance:" << Final_Min_Distance << endl;
  min_distance = 1000;


 }
 cout << "Minimum Distance is " << Final_Min_Distance;



 system("pause");

}

https://tahirnaeem.net/finding-closest-pair-of-points-in-two-disjoint-sets-of-points


你应该添加一个算法描述和你的设计选择。 - bolov

-1

我曾经处理过一个类似的问题,需要找到最近的成员以确定该成员是否属于一个群集中的群集。我试图识别群集内的群集。以下是代码,这可能会帮助您入门。

/**
 * Find the nearest neighbor based on the distance threshold.
 * TODO:
 * @param currentPoint current point in the memory.
 * @param threshold dynamic distance threshold.
 * @return return the neighbor.
 */

private double nearestNeighbor(double currentPoint) {

    HashMap<Double, Double> unsorted = new HashMap<Double, Double>();
    TreeMap<Double, Double> sorted = null; 
    double foundNeighbor = 0.0;

    for (int i = 0; i < bigCluster.length; i++) {
        if (bigCluster[i] != 0.0 && bigCluster[i] != currentPoint) {
            double shortestDistance = Math.abs(currentPoint - bigCluster[i]);
            if (shortestDistance <= this.getDistanceThreshold())
                unsorted.put(shortestDistance, bigCluster[i]);
        }
    }
    if (!unsorted.isEmpty()) {
        sorted = new TreeMap<Double, Double>(unsorted);
        this.setDistanceThreshold(avgDistanceInCluster());
        foundNeighbor = sorted.firstEntry().getValue();
        return foundNeighbor;
    } else {
        return 0.0;
    }
} 


/**
 * Method will check if a point belongs to a cluster based on the dynamic 
 * threshold.
 */
public void isBelongToCluster() {


        for (int i=0; i < tempList.size(); i++) {

            double aPointInCluster = tempList.get(i);

            cluster.add(aPointInCluster);
            double newNeighbor = nearestNeighbor(aPointInCluster);
            if ( newNeighbor != 0.0) {
                cluster.add(newNeighbor);
                if (i + 1 > tempList.size() && (visited[i] != true)) {
                    isBelongToCluster();
                }
            }

        }

    for (int i=0; i < cluster.size(); i++) {
        if (cluster.get(i) != 0.0)
            System.out.println("whats in the cluster -> " + cluster.get(i)); 
    } 
}

我没有意识到你要求答案用C语言,对此感到抱歉。 - add-semi-colons

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接