二维最接近点对问题 代码+实验报告

1. 算法介绍

代码使用的算法思想为分治算法,即将一个大问题分割为若干个类似的、独立的子问题,随后递归地解决这些子问题,最终将子问题的解合并起来以解 决原始问题。此算法一般包含三个步骤:分解、解决、合并。

2. 代码说明

代码为 C 语言编写,共计定义了一个结构体和五个函数。下面对各个部分分 别进行介绍。

Point 结构体:成员为整型数据的 x 和 y,用于表示一个二维点的坐标;

Calc_distance(Point a,Point b)函数:计算并返回两个点的欧几里得距 离;

Swap(Point* a,Point* b)函数:交换两个结构体的内容;

Fmin(double a, double b)函数:模仿标准库中的 fmin 行为,返回两个给定值中的最小值; quicksort(Point arr[],int left,int right)函数:实现快速排序算法, 根据 x 坐标对点数组进行排序; closest_pair_distance(Point points[],int start,int end)函数:使用 分治方法递归地找出两点之间的最小距离。

进一步说明:

该函数接收一个点数组以及区间的开始索引和结束索引作为输入,随后分为三种情况:

(1) 开始索引大于等于结束索引:函数将返回一个值(此处设定为 1e9);

(2) 开始索引与结束索引之差为 1:函数将直接计算并返回这两个点之间 的距离;

(3) 其他情况:函数将计算中间索引(int mid = (end + start)/2),随后递归调用自身,分别计算左半部分和右半部分的最小距离,然后取 两者的最小值作为当前区间的最小距离。

在这之后,函数创建了一个 strip,用于存储距离中间线距离小于已知最小距离的点对。通过遍历点数组,将满足条件的点加入 strip 数组内。接着, 在 strip 内部,通过双循环遍历每对点,更新当前的最小距离为 strip 内部 点对的距离中的最小值,最后返回最小距离。

再说一下主函数:在主函数中,首先读取点的数量 n,随后通过 malloc 为这些点动态分配内存,并通过 for 循环逐一读取各个点的坐标。之后借由 quicksort 函数按照 x 坐标对这些点进行一个递归之外的排序,最后调用 closest_pair_distance 函数计算最近点对的距离并返回。

3. 算法分析

当输入点的数量逐渐增加时,如果不预先对点进行排序,可能会降低计算效 率并大大提高最近点对搜索和距离计算的时间复杂度。因此,对二维点进行 排序是必要的。

代码中使用的排序算法为快速排序。首先从待排序数组中选择一个元素作为基准值,随后进行分区:将数组中小于基准值的元素移到基准值的左边,将 大于基准值的元素移动到基准值的右边,而此时基准值的位置会被确定下来。 接着进行递归,对基准值左右两个子数组分别重复以上步骤,直到子数组的大小为 1 或 0,即子数组已经有序。经过计算,快速排序的时间复杂度为 O(n log n),n 是待排序数组的长度,最坏情况下为 O(n^2),但通常情况下表现良好。

对比另一种排序方法,冒泡排序,后者的时间复杂度最好情况下(数组已然有序)为 O(n),平均时间复杂度为 O(n^2)。通过图表可以很直观地感受到快速排序在数据量增多时的优势。

在计算最近点距离时,代码采用的是分治算法,期望时间复杂度为 O(n log n)。如果采用穷举法,即计算所有点对之间的距离并找出最小的距离,具体而言,对于 n 个点,穷举法需要计算大约 n*(n-1)/2 个点对的距离,则时间复杂度变为 O(n^2)。

下面是实际测算出的代码运行时间表格。

按理绘制出的曲线应是呈 nlogn 速率增长的。

4. 总结

在本次实验中,通过研究二维最接近点对距离问题,我深入理解了分治算法的 思想,并对其在实际问题中的应用有了更深入的认识。

首先,分治算法是一种非常重要且广泛应用的算法思想,它将一个大问题划分 为多个小问题,然后递归地解决这些小问题,并将它们的解合并起来得到最终的结果。在二维最接近点对距离问题中,我将问题划分为两个子问题,分别在左右两个子集中求解最接近点对距离,然后通过合并步骤来求解跨越两个子集 的最接近点对距离。

其次,分治算法的关键在于如何将问题划分为更小的子问题,以及如何将子问题的解合并起来。在本实验中,我采用了一种基于 x 坐标进行排序的方法来将 问题划分为两个子集,然后通过合并步骤来求解跨越两个子集的最接近点对距离。这种划分和合并的策略使得算法的时间复杂度能够达到 O(n log n),在实 际应用中具有很高的效率。

最后,通过本次实验,我不仅加深了对分治算法的理解,还学习到了如何将这种算法思想应用到解决实际问题中。同时,我也发现了分治算法在处理一些复 杂问题时可能存在的一些挑战,如如何进行合并步骤、如何处理边界情况等。 这些挑战需要我们进一步思考和研究,以提高算法的效率。

综上所述,通过本次实验,我对分治算法的思想有了更深入的理解,也掌握了如何将其应用到解决实际问题中的技巧与方法,这对我今后的算法设计与分析工作都具有重要的指导意义

5.源代码

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define _CRT_SECURE_NO_WARNINGS
typedef struct {
    int x, y;
} Point;

// 计算两点之间的距离
double calc_distance(Point a, Point b) {
    return sqrt(pow(a.x - b.x, 2) + pow(a.y - b.y, 2));
}

void swap(Point * a, Point * b) {
    Point temp = *a;
    *a = *b;
    *b = temp;
}

double fmin(double a, double b) {
    if (a > b) return b;
    else return a;
}

void quickSort(Point arr[], int left, int right) {
    if (left < right) {
        int i = left, j = right;
        Point pivot = arr[left + (right - left) / 2];  // 选择中间点作为枢纽
        while (i <= j) {
            while (arr[i].x < pivot.x)  // 在枢纽左侧找到第一个大于等于枢纽值的元素
                i++;
            while (arr[j].x > pivot.x)  // 在枢纽右侧找到第一个小于等于枢纽值的元素
                j--;
            if (i <= j) {
                swap(&arr[i], &arr[j]);  // 交换两个元素
                i++;
                j--;
            }
        }
        if (left < j)
            quickSort(arr, left, j);  // 递归排序枢纽左侧的元素
        if (i < right)
            quickSort(arr, i, right);  // 递归排序枢纽右侧的元素
    }
}


// 计算最近点对的距离
double closest_pair_distance(Point points[], int start, int end) {
    if (start >= end) {
        return 1e9;  // 假设初始最小距离为1e9
    }
    else if (end - start == 1) {
        return calc_distance(points[start], points[end]);
    }

    int mid =  (end + start) / 2;
    double left_min = closest_pair_distance(points, start, mid);
    double right_min = closest_pair_distance(points, mid + 1, end);
    double min_distance = fmin(left_min, right_min);

    int strip_size = 0;
    Point* strip = (Point*)malloc((end - start + 1) * sizeof(Point));  // 动态分配内存初始化 strip 数组
    for (int i = start; i <= end; i++) {
        if (abs(points[i].x - points[mid].x) < min_distance) {
            strip[strip_size++] = points[i];
        }
    }

    // 寻找距离更近的点对
    for (int i = 0; i < strip_size; i++) {
        for (int j = i + 1; j < strip_size ; j++) {
            min_distance = fmin(min_distance, calc_distance(strip[i], strip[j]));
        }
    }

    return min_distance;
}

int main() {
    int n;
    scanf("%d", &n);
    Point* points = (Point*)malloc(n * sizeof(Point));

    // 读取点的坐标
    for (int i = 0; i < n; i++) {
        scanf("%d %d", &points[i].x, & points[i].y);
    }

    // 按x坐标对点进行排序
    quickSort(points, 0, n - 1);

    // 计算最近点对的距离
    double min_distance = closest_pair_distance(points, 0, n - 1);
    printf("%.2lf\n", min_distance);

    free(points);
    return 0;
}

  • 11
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值