【算法学习笔记】分治法 最近点对问题

一、问题描述

设一组点  C = \left \{ p_1, p_2, ..., p_n \right \},其中每个 p_i 点由其坐标 p_i = (x_i, y_i) 标识,并且它们的顺序是 p_i< p_j\, \,\, \, \, if\, \, \, \, x_i< x_j 。

目标是找到:min\left \{ d(p_i, p_j) \right \} \, \, \forall \, i, j   where:  d( p_i , p_j ) = \sqrt{(x_i-x_j)^{2} + (y_i-y_j)^{2} }


二、蛮力法求解

解决问题的基本算法是遍历每对点并保持找到的最小距离 S。 最后,将返回找到的最小距离。

伪代码:

ALGORITHM S = BASIC (C = {p₁, p₂,…, pn}: Set of points)

    S = Infinity 
    For each i from 1 to n-1:
        For each j from i + 1 to n:
            If (d (pi, pj) <S)
                S = d (pi, pj)
            End-If
        End-For
    End-For
Return S

三、分治法求解

算法设计:

1. 将问题划分为子问题:

-原始问题 P = C 被分成 k = 2 个子问题 P1、P2,分别求解,从而产生 P1 的解 S1 和 P2 的 S2。--问题 P 被分成两半, 假设 c 是集合 C 中的中心点,我们创建问题 P1 = {p1, p2,..., pc-1} 和问题P2 = {pc, pc + 1,..., pn }。因此, | P1 | 和 | P2 | 的大小大致相同,问题C与原问题P具有相同的性质,它们是独立的,可以单独解决。 由于点是按 X 坐标排序的,我们可以将 P1 称为“左侧部分”,将 P2 称为“右侧部分”。

2. 基本情况的存在:

-当集合中的点数为 | P | <= 3 时,我们将停止除法。 在这种情况下,我们用基本算法解决。

3.合并子问题:

-S1 是子问题 P1 的点的最小距离,S2 是子问题 P2 的点的最小距离。 将 P1 的解 S1 和 P2 的 S2 结合起来,我们可以认为 S = min {S1, S2}。 但是,还需要考虑最近的点是 P1 中的一个,P2 中的另一个。 因此,我们将选择 P1 的 X 坐标中距离中心为 min {S1, S2} 的点,以及 P2 在其 X 坐标系中距离中心为 min {S1, S2} 的点。 X 坐标,将左侧选中的点与右侧选中的点进行比较。

伪代码:

ALGORITHM S = DC (C: Set of points ordered by their x coordinate) 
    if |C| <= 3 then:
        Return S = BASIC (C)
    else:
        p_c ← Center point of C 
        P1 ← Points of C to p_c (not included) 
        P2 ← Points of C from p_c to n 
        S1 ← DC (P1) 
        S2 ← DC (P2)
        S ← COMBINE (p_c, P1, S1, P2, S2)
        Return S 
    End-if


ALGORITHM S = COMBINE (p_c: Central point of the p
                       P1: Subproblem of P (left part) 
                       S1: Solution of P1, 
                       P2: Subproblem of P (right part) 
                       S2: Solution of P2):
        
    PARTIAL = min {S1, S2}
    S = PARTIAL 
    left←index the point of P1 farthest from p_c with d_x (p_left, pc) <= PARTIAL             
    right←index the point of P2 farthest from p_c with d_x (p_right, pc) <= PARTIAL
    For each point p_i in P1 with i> = left:
        For each point p_j in P2 with j <= right:
            If d(p_i, p_j) < S:
                S = d(p_i, p_j)
            End-If
        End-For
    End-For
    Return S

四、实验代码(部分代码)

// 表示二维点的结构
typedef struct {
    float x, y;
} Point;

//
// BASIC SOLUTION
//
/*
 Basic algorithm: 将所有点相互比较。结果返回点数
 */
float BASICO(const Point *cPoints, const int first, const int last) {

    float S = -1; // 最近点的距离。初始化为 -1 表示无穷大
    float dist; // 当前点的距离

    for (int i = first; i < last; i++) {
        for (int j = i+1; j < last; j++) {

            // 计算 cPoints [i] 和 cPoints [j] 之间的距离
            dist = distance(cPoints[i], cPoints[j]);
            if (S == -1 || dist < S) {
                S= dist; // 更新最近的点
            }
        }
    }
    // 返回结果
    return S;
}

//
// DC SOLUTION
//

float Merge(const Point *cPoints, const int first, const int center, const int last, const float S1, const float S2) {
    
    const float partial= (S1 < S2)? S1:S2;
    int centerLeft, centerRight; // 合并的中心位置
    float distAux; // 比较的点的距离
    float S; // 返回的结果
    S= partial; // 初始化结果

    // 合并中央部分
    // 搜索问题 P1 在最右边的点(左边部分)
    centerLeft = center;
    distAux = 0;
    do {
        centerLeft--;
        if (centerLeft >= first) {
            distAux= distanceX(cPoints[center], cPoints[centerLeft]);
        }
    } while (centerLeft > first && distAux < partial);

    // 搜索问题 P2 在最左边的点(右边部分)
    centerRight = center-1;
    distAux= 0;
    do {
        centerRight++;
        if (centerRight < last) {
            distAux= distanceX(cPoints[center], cPoints[centerRight]);
        }
    } while (centerRight < last && distAux < partial);

    if (centerRight < last) centerRight++; 

    // 在中央部分找到最小距离
    for (int i = centerLeft; i < center; i++)  {
        for (int j = center; j < centerRight; j++)  {
            distAux = distance(cPoints[i], cPoints[j]);
            if (S > distAux){
                S = distAux;
            }
        }
    }
    return S;
}

/*
 algorithm DC: 将所有的点分为 2
 */
float DC(const Point *cPoints, const int first, const int last) {

    int center;
    float S1, S2, S;
    // 基本情况检查:数据包含 2 或 3 个点
    if (last - first <= 3) {
        S = BASIC(cPoints, first, last); 
    } else {

        center = (first + last) / 2;
        S1= DC(cPoints, first, center);
        S2= DC(cPoints, center, last);

        S= Merge(cPoints, first, center, last, S1, S2);
    }
    return S;
}

float distance(const Point pi, const Point pj) {
    float dx, dy;

    dx = (pi.x - pj.x)*(pi.x - pj.x);
    dy = (pi.y - pj.y)*(pi.y - pj.y);
    return sqrt(dx + dy);
}


float distanceX(const Point pi, const Point pj) {
    float dx, dy;

    dx = (pi.x - pj.x)*(pi.x - pj.x);
    return sqrt(dx);
}

完整版:https://download.csdn.net/download/weixin_47154327/19835120

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值