
本文将详细探讨如何在给定半径`r`的情况下,高效地找出平面上距离原点`(0,0)`最近,且其与原点距离严格大于`r`的整数坐标`(x,y)`。我们将从数学原理出发,逐步优化算法,最终实现一个具有线性时间复杂度的解决方案,显著提升处理大半径时的性能。
给定一个整数半径r,我们的目标是寻找一对正整数坐标(x, y),使得它们到原点的欧几里得距离D = sqrt(x^2 + y^2)满足两个条件:
为了简化搜索空间,通常假定x <= y,因为(x, y)和(y, x)到原点的距离相同。
最初的尝试可能涉及两层嵌套循环,遍历所有可能的x和y值,计算距离并跟踪最小值。例如,一个常见的朴素方法可能如下:
import java.util.Scanner;
public class DiscDistrictQuadratic {
public static void main(String[] args) {
Scanner new_scanner = new Scanner(System.in);
int radius = new_scanner.nextInt();
new_scanner.close();
double min_max_dist = Double.MAX_VALUE;
int[] new_min_pair = new int[2];
// 遍历x的范围,通常从1到radius,或根据问题特性优化
for (int i = 1; i <= radius; i++) {
// 遍历y的范围,通常从i到radius,确保y >= x
for (int j = i; j <= radius; j++) {
double new_dist = Math.sqrt(Math.pow(i, 2) + Math.pow(j, 2));
if (new_dist > radius) {
if (min_max_dist > new_dist) {
min_max_dist = new_dist;
new_min_pair[0] = i;
new_min_pair[1] = j;
}
}
}
}
System.out.println(new_min_pair[0] + " " + new_min_pair[1]);
}
}这种方法的时间复杂度为O(radius^2),对于较小的radius(如r <= 5000)可能尚可接受。然而,当radius增大到10000甚至更大时,radius^2会迅速增长,导致程序运行时间呈指数级(实际上是二次方)增长,变得不可接受。这是因为嵌套循环导致了大量的重复计算和不必要的距离比较。
为了提高效率,我们需要减少内层循环的迭代次数。关键在于利用距离条件sqrt(x^2 + y^2) > r。 由于x, y, r都是正数,我们可以将其平方:x^2 + y^2 > r^2。 对于一个固定的x (即代码中的i),我们需要找到最小的y (即代码中的j),使得j^2 > r^2 - i^2。 这意味着j > sqrt(r^2 - i^2)。
因此,对于每个i值,j的起始值可以被精确计算出来,而不是从i开始盲目遍历。 j的最小整数值应为 floor(sqrt(r^2 - i^2)) + 1。
考虑到j也必须大于或等于i (为了避免重复和利用对称性),j的实际起始值应为 max(i, floor(sqrt(r^2 - i^2)) + 1)。
上述优化使得内层循环的起始点更精确,但如果仍保留内层循环并遍历到radius,复杂度仍是二次方。然而,我们寻求的是最小的距离。对于一个给定的i,我们已经找到了满足j > sqrt(r^2 - i^2)的最小整数j。这个j值将产生一个距离sqrt(i^2 + j^2)。如果存在一个j'使得j' > j,那么sqrt(i^2 + j'^2)必然大于sqrt(i^2 + j^2)。因此,对于每个i,我们只需要检查这个唯一的、最小的满足条件的j,而无需遍历所有更大的j值。
这将内层循环完全消除,从而将算法复杂度从O(radius^2)降低到O(radius),实现线性时间复杂度。
import java.util.Scanner;
public class DiscDistrictLinear {
public static void main(String[] args) {
Scanner new_scanner = new Scanner(new_scanner);
int radius = new_scanner.nextInt();
new_scanner.close();
double min_max_dist = Double.MAX_VALUE; // 初始化为最大值,以便后续更新
int[] result_pair = new int[2]; // 存储最终结果 (x, y)
// 优化i的遍历范围:
// 1. i <= radius 是显而易见的。
// 2. i从 radius / 2 开始是一个常见的启发式优化,因为当i过小时,j会非常接近radius,
// 此时sqrt(i^2 + j^2)会略大于radius,但通常不是最小的。
// 最小的距离通常发生在i和j的值相对接近时(例如i和j都接近r/sqrt(2))。
// 这个范围确保了我们能找到最优解,同时避免了不必要的早期迭代。
for (int i = (radius / 2); i <= radius; i++) {
// 计算j的最小起始值,使其满足 j > sqrt(r^2 - i^2)
// Math.pow(radius, 2) - Math.pow(i, 2) 确保了平方根内的值非负,因为 i <= radius
int start_j_from_inequation = (int) Math.floor(Math.sqrt(Math.pow(radius, 2) - Math.pow(i, 2))) + 1;
// 确保 j >= i,并使用上面计算出的最小j值
int j = Math.max(i, start_j_from_inequation);
// 计算当前 (i, j) 对的距离
double current_dist = Math.sqrt(Math.pow(i, 2) + Math.pow(j, 2));
// 检查当前距离是否大于半径且小于已找到的最小距离
// current_dist > radius 条件在 j 的计算中已经隐含满足,但显式检查更清晰
if (current_dist > radius) {
if (min_max_dist > current_dist) {
min_max_dist = current_dist;
result_pair[0] = i;
result_pair[1] = j;
}
}
}
System.out.println(result_pair[0] + " " + result_pair[1]);
}
}让我们通过几个例子来验证这个算法:
半径 r = 1:
半径 r = 5:
半径 r = 10:
通过深入理解问题背后的数学原理,并巧妙地运用不等式和循环优化,我们能够将一个看似复杂的几何搜索问题转化为一个高效的线性时间算法。
以上就是寻找平面上距离原点最近且超出给定半径的整数坐标点的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号