每周算法练习——最近对问题
时间:2022-05-04
本文章向大家介绍每周算法练习——最近对问题,主要内容包括一、最近对问题的解释、二、最近对问题的蛮力解法、三、最近对问题的分治解法、基本概念、基础应用、原理机制和需要注意的事项等,并结合实例形式分析了其使用技巧,希望通过本文能帮助到大家理解应用这部分内容。
一、最近对问题的解释
看到算法书上有最近对的问题,简单来讲最近对问题要求出一个包含
个点的集合中距离最近的两个点。抽象出来就是求解任意两个点之间的距离,返回距离最小的点的坐标,以及最小距离。这里会使用到欧式距离的求法:
以上是二维的情况,这其实和相似性的计算是类似的,所以便想去实现这样的一个问题。
二、最近对问题的蛮力解法
蛮力法是最直接的方法,就是求解任意两个点之间的距离,返回坐标和最小的距离
Java代码实现
package org.algorithm.closestpair;
/**
* 蛮力法是最显然的方法,也是最直接的方法
*
* @author dell
*
*/
public class ClosestPairProblem01 {
public static final int length = 20;// 点的个数
// 主函数
public static void main(String args[]) {
// 存放x和y
Point p[] = new Point[length];
for (int i = 0; i < length; i++) {
p[i] = new Point(Util.createXY(), Util.createXY());
}
// 打印出每个点的坐标
for (int i = 0; i < length; i++) {
System.out.println(i + "t" + p[i].getX() + "t" + p[i].getY());
}
// 计算出最近对
double result[] = Util.closestPair(p, length);
System.out.println("最近对为:");
System.out.println((int) result[0] + "t" + (int) result[1] + "t"
+ Math.sqrt(result[2]));
}
}
最终的结果
三、最近对问题的分治解法
分治的思想是将一个问题划分成几个独立的子问题,分别对子问题的求解,最终将子问题的解组合成原始问题的解。个人理解的分治法与现在的并行十分相似,如在演化计算中,由于运算规模比较大,十分强调是否可以并行计算,本身演化计算又特别适合做并行。再如Map-Reduce,同样是一种并行的计算方式。如何将原始问题划分成子问题成为分治的关键。
在最近对问题中,首先通过一维坐标将整个空间分成坐标点个数相同的两个区间,如下图:
(图片摘自:http://www.cnblogs.com/AdaByron/archive/2011/10/07/2200966.html)
Java代码实现
package org.algorithm.closestpair;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
/**
* 主要通过分治的思想求解
*
* @author dell
*
*/
public class ClosestPairProblem02 {
public static final int length = 20;// 点的个数
// 主函数
public static void main(String args[]) {
// 存放x和y
Point p[] = new Point[length];
for (int i = 0; i < length; i++) {
p[i] = new Point(Util.createXY(), Util.createXY());
}
// 打印出每个点的坐标
for (int i = 0; i < length; i++) {
System.out.println(i + "t" + p[i].getX() + "t" + p[i].getY());
}
// 计算出最近对
int middle = length / 2;
Set<Double> st = new TreeSet<Double>();
for (int i = 0; i < length; i++) {
st.add(p[i].getX());
}
// System.out.println(st);
// 找到middle位置的x的值
Iterator<Double> it = st.iterator();
int index = 0;
double middleValue = 0;
while (it.hasNext()) {
if (index == middle) {
middleValue = it.next();
break;
}
index++;
it.next();
}
// System.out.println(middleValue);
// 将前一半放入到mpLeft中,后一半放入到mpRight中
Map<Integer, Point> mpLeft = new HashMap<Integer, Point>();
Map<Integer, Point> mpRight = new HashMap<Integer, Point>();
for (int i = 0; i < length; i++) {
if (p[i].getX() < middleValue) {
mpLeft.put(i, p[i]);
} else {
mpRight.put(i, p[i]);
}
}
// System.out.println(mpLeft.size() + "t" + mpRight.size());
// 分别计算左右两边的最小距离
Collection<Point> cLeft = mpLeft.values();
Collection<Point> cRight = mpRight.values();
Point pLeft[] = cLeft.toArray(new Point[0]);
Point pRight[] = cRight.toArray(new Point[0]);
double d1[] = Util.closestPair(pLeft, pLeft.length);
double d2[] = Util.closestPair(pRight, pRight.length);
Set<Integer> stKeyLeft = mpLeft.keySet();
Set<Integer> stKeyRight = mpRight.keySet();
Integer[] iLeft = stKeyLeft.toArray(new Integer[0]);
Integer[] iRight = stKeyRight.toArray(new Integer[0]);
d1[0] = iLeft[(int) d1[0]];
d1[1] = iLeft[(int) d1[1]];
d2[0] = iRight[(int) d2[0]];
d2[1] = iRight[(int) d2[1]];
double d[];
// d1和d2中的距离的最小值
if (d1[2] < d2[2]) {
d = d1;
} else {
d = d2;
}
// System.out.println(d);
// 存放中间的[middleValue-d,middleValue+d]之间的点
Map<Integer, Point> mpMiddle = new HashMap<Integer, Point>();
for (int i = 0; i < length; i++) {
if (p[i].getX() >= (middleValue - d[2])
&& p[i].getX() <= (middleValue + d[2])) {
mpMiddle.put(i, p[i]);
}
}
Collection<Point> cMiddle = mpMiddle.values();
Point pMiddle[] = cMiddle.toArray(new Point[0]);
double d3[] = Util.closestPair(pMiddle, pMiddle.length);
Set<Integer> stKeyMiddle = mpMiddle.keySet();
Integer[] iMiddle = stKeyMiddle.toArray(new Integer[0]);
d3[0] = iMiddle[(int) d3[0]];
d3[1] = iMiddle[(int) d3[1]];
double dMin[];
if (d3[2] < d[2]) {
dMin = d3;
} else {
dMin = d;
}
System.out.println(dMin[0] + "t" + dMin[1] + "t" + Math.sqrt(dMin[2]));
}
}
运行结果:
自我感觉这段代码写得特别复杂,希望看到这篇博客的朋友们提点意见,帮助我提高下我的写代码的能力。
这里我还构造了公共的代码:
Point类:
package org.algorithm.closestpair;
/**
* 主要用于存储坐标
*
* @author dell
*
*/
public class Point {
private double x;// x的坐标
private double y;// y的坐标
public Point(double x, double y){
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
}
工具类:
package org.algorithm.closestpair;
public class Util {
// 计算两个点之间的距离的平方
public static double distance(Point x1, Point x2) {
return (Math.pow((x1.getX() - x2.getX()), 2) + Math.pow(
(x1.getY() - x2.getY()), 2));
}
// 随机产生坐标
public static double createXY() {
return Math.random() * 10;
}
// 计算最近对
public static double[] closestPair(Point p[], int length) {
double maxDistance = Double.MAX_VALUE;// 设置一个最大值
double result[] = new double[3];
for (int i = 0; i < length - 1; i++) {
for (int j = i + 1; j < length; j++) {
double tmp = Util.distance(p[i], p[j]);
if (tmp < maxDistance) {
maxDistance = tmp;
result[0] = i;
result[1] = j;
}
}
}
result[2] = maxDistance;
return result;
}
}
- 51. Socket服务端和客户端使用TCP协议通讯 | 厚土Go学习笔记
- 备库报警邮件的分析案例(二) (r7笔记第15天)
- Gotorch - 多机定时任务管理系统
- 备库报警邮件的分析案例(三)(r7笔记第16天)
- 简单易学的机器学习算法——神经网络之BP神经网络
- 24(02)多线程锁,线程通讯,线程组,线程池,多线程三种方式,匿名内部类,定时器,设计模式,单例模式,Runtime
- Go代码打通HTTPs
- 一个简单的MySQL参数导致的连接问题解惑(r7笔记第33天)
- [基础篇]Go语言变量
- [转载]Golang 编译成 DLL 文件
- [转载]Go JSON 技巧
- 简单易学的机器学习算法——Rosenblatt感知机的对偶解法
- Spring-拾遗
- Golang面试题
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- Unix每分钟监控进程的状态
- webpack实战——资源输入与输出
- R语言读取 xlsx 和xls 文件
- pytest文档42-fixture参数化params
- 搭建node服务(三):使用TypeScript
- Antd for Vue使用Form组件报错You cannot set a form field before rendering
- IDEA配置Resin
- BERT详解(附带ELMo、GPT介绍)
- Centos7创建LVM及扩容
- python3 使用session模拟post实现修改活码内容
- linux LVM逻辑卷的创建,扩容,缩减和删除
- shell脚本中各种括号的区别以及用法
- linux 实现centos7在线升级最新版本内核
- linux centos系统开机启动流程
- mysql的备份及恢复