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;
}