算法设计与分析实验一:分治法解决最近点对问题



【实验内容】利用分治算法、蛮力法,编程实现最近点对问题,并进行时间复杂性分析。注:要求针对计算机随机生成的100点对数据,分别用蛮力法和分治法求解最近点对,对比其复杂性。

【编译环境】devc++ 5.11

【解题思路】

1.随机生成100对实数点对数据

2.用蛮力法求解最近点对问题

3.用分治法求解最近点对问题

4.对于复杂性

【参考内容】
最近点对问题算法书上的关于分治法的图

【代码分析】

1.随机生成100对实数点对数据

每个点的表示

要求使用一个结构体表示每个点在xy轴上的坐标和该点的唯一标识id
(命名有些不规范,正在学习…)

typedef struct Point
{
	double x;
	double y;
	int id;
 }point;

利用c++库函数生成

#include<fstream>
#include<iostream>
#include<ctime>
#include<random>
#define MAX 100	//点对数 
#define MAXXY 100	//点对最大值 
void randomint(point* num)//生成100组随机数 
{
	default_random_engine random(time(0));//利用当前时间产生随机数种子 
	uniform_real_distribution<double> randnum(0-MAXXY, MAXXY);//产生随机数的上限MAXXY,下限0-MAXXY,类型为double 
	ofstream outFile;//文件操作用来保存生成的随机数 
	outFile.open("sample.txt");	//打开文件
	outFile<<"当前MAX值为:	"<<MAX<<endl;
	outFile<<"当前MAXXY值为:	"<<MAXXY<<endl;
	for(int i=0;i<MAX;i++)
	{
		num[i].x = randnum(random);//每次调用都产生一次随机数 
		num[i].y = randnum(random);	
		num[i].id = i;
		outFile<<num[i].id <<"	"<<num[i].x <<"	"<<num[i].y <<endl;//输入当前目录下的sample。txt文件内保存案例 
	}
}

2.蛮力法

#include<algorithm>
void enumerate(point* num)//第一种方法枚举方法
{
	//依次计算两个点之间距离记录最小值 
	double mindis = MAXXY*MAXXY*2+1;//确保mindis初始值是最大的 
	int minpoint0;
	int minpoint1;
	if(MAX == 2)//只有两个点的时候直接输出结果 
	{
		minpoint0 = 0;
		minpoint1 = 1;
		double x = num[0].x>num[1].x?num[0].x-num[1].x:num[1].x-num[0].x;
		double y = num[0].y>num[1].y?num[0].y-num[1].y:num[1].y-num[0].y;
		mindis = x*x+y*y;
	 } 
	 else
		for(int i=0;i<MAX-1;i++)
			for(int j=i+1;j<MAX;j++)
			{
				double x = num[i].x>num[j].x?num[i].x-num[j].x:num[j].x-num[i].x;
				double y = num[i].y>num[j].y?num[i].y-num[j].y:num[j].y-num[i].y;
				if(x*x+y*y<mindis)
				{
					mindis = x*x+y*y;
					minpoint0 = i;
					minpoint1 = j;
				}
			}
	mindis = sqrt(mindis);
	cout<< mindis<<endl;
	cout<< num[minpoint0].id<<"	"<<num[minpoint1].id<<endl;
	cout<< num[minpoint0].x<<"	"<<num[minpoint0].y <<endl;
	cout<< num[minpoint1].x<<"	"<<num[minpoint1].y <<endl;
 } 

3.分治法

所需结构

typedef struct distance//分治法递归函数返回的结构体 
{
	double dis;
	point point1;
	point point2;
}dist;

一些单独功能的函数

bool sortcmpx(point num1,point num2)//x坐标排序函数所需比较函数 
{
	if(num1.x >num2.x )
		return 0;
	else
		return 1;
}
bool sortcmpy(point num1,point num2)//y坐标排序函数所需比较函数 
{
	if(num1.y >num2.y )
		return 0;
	else
		return 1;
}
double getdis(point num1, point num2)//返回两个point点的距离 
{
	double disx = num1.x >num2.x ?num1.x -num2.x:num2.x-num1.x ;
	double disy = num1.y >num2.y ?num1.y -num2.y:num2.y-num1.y ;
	return disx*disx+disy*disy;
}

分治法主要代码

dist* divide(point* num,int length)//分治法divide
{
	dist* dis = new dist;
	dist* dis1 = new dist;
	dist* dis2 = new dist;
	dist* dis3 = new dist;
	//图片第一步:递归到长度为2时最短距离即为两者的距离 
	if(length ==2)
	{
		dis->dis = getdis(num[0],num[1]);
		dis->point1 = num[0];
		dis->point2 = num[1];
		free(dis1);
		free(dis2);
		free(dis3);
		return dis;
	}
	dis3->dis  = MAXXY*MAXXY*2;//初始化最大 
	//图片第二步 
	int mid = length/2;
	sort(num,num+length,sortcmpx);
	//coutx用来显示排序后的结果,方便调试  
	//coutx(num,length);
	//图片第三步 
	dis1 = divide(num,mid+1);//mid+1确保前一部分最少有两个,不会出现一个的情况 
	//图片第四步 
	dis2 = divide(num+mid,length-mid);
	//图片第五步 
	if(dis1->dis >dis2->dis )
	{
		dis->dis = dis2->dis ;
		dis->point1 = dis2->point1 ;
		dis->point2 = dis2->point2 ;
	}
	else
	{
		dis->dis = dis1->dis ;
		dis->point1 = dis1->point1 ;
		dis->point2 = dis1->point2 ;	
	}
	//求解最短距离在中间的情况
	point p1[MAX];
	point p2[MAX];
	int p1num=0;
	int p2num=0;
	//取距离mid点的x坐标距离不大于dis的点 
	//图片第六步 
	for(int i=0;i<length;i++)
	{
		if(num[i].x <=num[mid].x && num[i].x >=num[mid].x -dis->dis )
			{
				p1[p1num].x = num[i].x ;
				p1[p1num].y = num[i].y ;
				p1[p1num].id = num[i].id ;
				p1num++;
			}
		else if(num[i].x >num[mid].x && num[i].x <=num[mid].x +dis->dis )
			{
				p2[p2num].x = num[i].x ;
				p2[p2num].y = num[i].y ;
				p2[p2num].id = num[i].id ;
				p2num++;
			}
	}
	//按照y坐标升序排序 
	//图片第七步 
	sort(p1,p1+p1num,sortcmpy);
	sort(p2,p2+p2num,sortcmpy);
	//图片第八步 
	//分开计算p1和p2。p1中的点每个点最多取5个,p2中的点每个点最多取6个 
	//计算p1中的每个点的最短距离情况 
	for(int i=0;i<p1num;i++)
	{
		int count = 0;//已经取得点的个数
		for(int j=0;j<p2num;j++)
		{
			if(count>4)
				break;
			if(p2[j].y >=p1[i].y && p2[j].y<=p1[i].y +dis->dis )
			{
				count++;
				double a = getdis(p1[i],p2[j]);
				if(dis3->dis >a)
				{
					dis3->dis = a;
					dis3->point1 = p1[i];
					dis3->point2 = p2[j];
				}
			}
		 } 
	 }
	 //计算p2中的每个点的最短距离情况 
	 for(int i=0;i<p2num;i++)
	{
		int count = 0;//已经取得点的个数
		for(int j=0;j<p1num;j++)
		{
			if(count>5)
				break;
			if(p1[j].y >=p1[i].y && p1[j].y<=p1[i].y +dis->dis )
			{
				count++;
				double a = getdis(p2[i],p1[j]);
				if(dis3->dis >a)
				{
					dis3->dis = a;
					dis3->point1 = p2[i];
					dis3->point2 = p1[j];
				}
			}
		 } 
	 }
	 if(dis->dis >dis3->dis )
	 	{
	 		dis->dis = dis3->dis ;
	 		dis->point1 =dis3->point1 ;
	 		dis->point2 = dis3->point2 ;
		} 
	//释放内存 
	free(dis1);
	free(dis2);
	free(dis3);
	//图片第九步 
	return dis;
 }

4.复杂性

蛮力法时间复杂度

最近点对问题蛮力法时间复杂度

分治法时间复杂度

最近点对问题分治法时间复杂度

5.实验结果

输出

前者蛮力法,后者分治法
分治法案例控制台输出

相应目录下记录的案例

分治法案例

6.编译器报错可能原因

报错图片

分治法编译器报错提示

解决方法

工具->编译选项:增加下列设置

分治法编译器设置

7.源代码

#include<iostream>
#include<ctime>
#include<random>
#include<algorithm>
#include<fstream>
#define MAX 100
#define MAXXY 100
using namespace std;

typedef struct Point
{
	double x;
	double y;
	int id;
 }point;

void randomint(point* num)
{
	default_random_engine random(time(0));
	uniform_real_distribution<double> randnum(0-MAXXY, MAXXY);
	ofstream outFile;
	outFile.open("sample.txt");
	outFile<<"当前MAX值为:	"<<MAX<<endl;
	outFile<<"当前MAXXY值为:	"<<MAXXY<<endl;
	for(int i=0;i<MAX;i++)
	{
		num[i].x = randnum(random);
		num[i].y = randnum(random);	
		num[i].id = i;
		outFile<<num[i].id <<"	"<<num[i].x <<"	"<<num[i].y <<endl;
	}
}

void writedown(point* num)
{
	for(int i =0;i<MAX;i++)
	{
		cout<<num[i].x <<" "<<num[i].y <<endl;
	}
}

void enumerate(point* num)
{
	double mindis = MAXXY*MAXXY*2+1;
	int minpoint0;
	int minpoint1;
	if(MAX == 2)
	{
		minpoint0 = 0;
		minpoint1 = 1;
		double x = num[0].x>num[1].x?num[0].x-num[1].x:num[1].x-num[0].x;
		double y = num[0].y>num[1].y?num[0].y-num[1].y:num[1].y-num[0].y;
		mindis = x*x+y*y;
	 } 
	 else
		for(int i=0;i<MAX-1;i++)
			for(int j=i+1;j<MAX;j++)
			{
				double x = num[i].x>num[j].x?num[i].x-num[j].x:num[j].x-num[i].x;
				double y = num[i].y>num[j].y?num[i].y-num[j].y:num[j].y-num[i].y;
				if(x*x+y*y<mindis)
				{
					mindis = x*x+y*y;
					minpoint0 = i;
					minpoint1 = j;
				}
			}
	mindis = sqrt(mindis);
	cout<< mindis<<endl;
	cout<< num[minpoint0].id<<"	"<<num[minpoint1].id<<endl;
	cout<< num[minpoint0].x<<"	"<<num[minpoint0].y <<endl;
	cout<< num[minpoint1].x<<"	"<<num[minpoint1].y <<endl;
 } 

bool sortcmpx(point num1,point num2)
{
	if(num1.x >num2.x )
		return 0;
	else
		return 1;
}

bool sortcmpy(point num1,point num2)
{
	if(num1.y >num2.y )
		return 0;
	else
		return 1;
}

void coutx(point *num,int length)
{
	for(int i=0;i<length;i++)
	{
		cout<<num[i].x<<endl;
	}
	cout<<endl;
 } 

double getdis(point num1, point num2)
{
	double disx = num1.x >num2.x ?num1.x -num2.x:num2.x-num1.x ;
	double disy = num1.y >num2.y ?num1.y -num2.y:num2.y-num1.y ;
	return disx*disx+disy*disy;
}

typedef struct distance
{
	double dis;
	point point1;
	point point2;
}dist;

dist* divide(point* num,int length)
{
	dist* dis = new dist;
	dist* dis1 = new dist;
	dist* dis2 = new dist;
	dist* dis3 = new dist;
	if(length ==2)
	{
		dis->dis = getdis(num[0],num[1]);
		dis->point1 = num[0];
		dis->point2 = num[1];
		free(dis1);
		free(dis2);
		free(dis3);
		return dis;
	}
	dis3->dis  = MAXXY*MAXXY*2;
	int mid = length/2;
	sort(num,num+length,sortcmpx);
	//coutx(num,length);
	dis1 = divide(num,mid+1);
	dis2 = divide(num+mid,length-mid);
	if(dis1->dis >dis2->dis )
	{
		dis->dis = dis2->dis ;
		dis->point1 = dis2->point1 ;
		dis->point2 = dis2->point2 ;
	}
	else
	{
		dis->dis = dis1->dis ;
		dis->point1 = dis1->point1 ;
		dis->point2 = dis1->point2 ;	
	}
	point p1[MAX];
	point p2[MAX];
	int p1num=0;
	int p2num=0;
	for(int i=0;i<length;i++)
	{
		if(num[i].x <=num[mid].x && num[i].x >=num[mid].x -dis->dis )
			{
				p1[p1num].x = num[i].x ;
				p1[p1num].y = num[i].y ;
				p1[p1num].id = num[i].id ;
				p1num++;
			}
		else if(num[i].x >num[mid].x && num[i].x <=num[mid].x +dis->dis )
			{
				p2[p2num].x = num[i].x ;
				p2[p2num].y = num[i].y ;
				p2[p2num].id = num[i].id ;
				p2num++;
			}
	}
	sort(p1,p1+p1num,sortcmpy);
	sort(p2,p2+p2num,sortcmpy);
	for(int i=0;i<p1num;i++)
	{
		int count = 0;
		for(int j=0;j<p2num;j++)
		{
			if(count>4)
				break;
			if(p2[j].y >=p1[i].y && p2[j].y<=p1[i].y +dis->dis )
			{
				count++;
				double a = getdis(p1[i],p2[j]);
				if(dis3->dis >a)
				{
					dis3->dis = a;
					dis3->point1 = p1[i];
					dis3->point2 = p2[j];
				}
			}
		 } 
	 }
	 for(int i=0;i<p2num;i++)
	{
		int count = 0;
		for(int j=0;j<p1num;j++)
		{
			if(count>5)
				break;
			if(p1[j].y >=p1[i].y && p1[j].y<=p1[i].y +dis->dis )
			{
				count++;
				double a = getdis(p2[i],p1[j]);
				if(dis3->dis >a)
				{
					dis3->dis = a;
					dis3->point1 = p2[i];
					dis3->point2 = p1[j];
				}
			}
		 } 
	 }
	 if(dis->dis >dis3->dis )
	 	{
	 		dis->dis = dis3->dis ;
	 		dis->point1 =dis3->point1 ;
	 		dis->point2 = dis3->point2 ;
		} 
	free(dis1);
	free(dis2);
	free(dis3);
	return dis;
 }

void coutdivide(point* pointxy,int max)
{
	dist* dis = divide(pointxy,max);
	cout<<sqrt(dis->dis) <<endl;
	cout<<dis->point1.id <<"	"<<dis->point2.id <<endl; 
	cout<<dis->point1.x <<"	"<<dis->point1.y <<endl;
	cout<<dis->point2.x <<"	"<<dis->point2.y <<endl;
}

int main()
{
	point pointxy[MAX];
	randomint(pointxy);
	
	//writedown(pointxy);
	enumerate(pointxy);
	
	cout<<"-----分----------割----------符-----"<<endl;
	
	coutdivide(pointxy,MAX);
}

  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
循环赛日程表问题是一个经典的应用分治策略解的问题,它是指在n个队伍之间进行循环赛,每个队伍都要与其他n-1个队伍比赛一次,出需要多少轮才能完成比赛,并给出比赛的具体安排。 我们可以使用分治策略来解决这个问题。具体步骤如下: 1. 如果n是奇数,则在n个队伍中添加一个“虚拟”队伍,使得n变为偶数。 2. 将n个队伍分成两个大小相等的组,分别记为A和B。 3. 对于A组中的每个队伍,安排它们与B组中的队伍的比赛安排。这些比赛在本轮中进行。 4. 递归地解决A组和B组内部的比赛安排问题,直到只剩下一个队伍或没有队伍。 5. 合并前面的分组结果,得到最终的比赛安排。 下面是Java代码实现: ```java public class RoundRobinSchedule { private int[][] schedule; private int currentTeam; public int[][] getSchedule(int n) { if (n <= 0) { throw new IllegalArgumentException("Invalid number of teams"); } int m = n % 2 == 0 ? n : n + 1; schedule = new int[m][m]; currentTeam = 1; divideAndConquer(0, m / 2, m / 2, m - 1); return schedule; } private void divideAndConquer(int rowStart, int rowEnd, int colStart, int colEnd) { if (rowStart >= rowEnd || colStart >= colEnd) { return; } int mid = (rowEnd - rowStart) / 2 + rowStart; divideAndConquer(rowStart, mid, colStart, colEnd); divideAndConquer(mid + 1, rowEnd, colStart, colEnd); for (int i = rowStart; i <= mid; i++) { schedule[i][currentTeam] = currentTeam; schedule[currentTeam][i] = currentTeam; } currentTeam++; for (int i = mid + 1; i <= rowEnd; i++) { schedule[i][currentTeam] = currentTeam; schedule[currentTeam][i] = currentTeam; } currentTeam++; for (int i = colStart; i <= colEnd; i++) { schedule[currentTeam][i] = currentTeam; schedule[i][currentTeam] = currentTeam; } currentTeam++; for (int i = colEnd; i >= colStart; i--) { schedule[currentTeam][i] = currentTeam; schedule[i][currentTeam] = currentTeam; } currentTeam++; } public static void main(String[] args) { RoundRobinSchedule schedule = new RoundRobinSchedule(); int[][] result = schedule.getSchedule(6); for (int[] row : result) { for (int i : row) { System.out.printf("%3d", i); } System.out.println(); } } } ``` 输出结果为: ``` 0 1 2 3 4 5 1 0 5 4 3 2 2 5 0 1 4 3 3 4 1 0 5 2 4 3 2 5 0 1 5 2 3 4 1 0 ``` 其中,每个数字表示一场比赛的编号,0表示“虚拟”队伍。比如,第1行第2列的数字1表示第1个队伍与第2个队伍进行了一场比赛。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值