我怀疑最坏情况的复杂度无法降低到N×N以下,因为可以创建输入,其中没有一个点严格小于任何其他点:
对于任何值n,考虑与Z轴、Y轴和Z轴相交于(n,0,0)、(0,n,0)和(0,0,n)的平面,由方程x+y+z=n描述。如果输入由这样的平面上的点组成,则没有任何一个点严格小于任何其他点。
最坏情况输入的示例:
(5,0,0) (4,1,0) (3,2,0) (2,3,0) (1,4,0) (0,5,0)
(4,0,1) (3,1,1) (2,2,1) (1,3,1) (0,4,1)
(3,0,2) (2,1,2) (1,2,2) (0,3,2)
(2,0,3) (1,1,3) (0,2,3)
(1,0,4) (0,1,4)
(0,0,5)
然而,平均复杂度可以降低到远远小于N×N,例如采用以下方法:
- 从输入中取出第一个点并将其放入列表中。
- 从输入中取出第二个点,并将其与列表中的第一个点进行比较。如果它严格小于,则丢弃新点。如果它严格大于,则用新点替换列表中的点。如果两者都不是,则将该点添加到列表中。
- 对于来自输入的每个新点,将其与列表中的每个点进行比较。如果它严格小于列表中的任何点,则丢弃新点。如果它严格大于,则用新点替换列表中的点,并且丢弃严格小于新点的任何进一步点。如果新点既不严格小于也不严格大于列表中的任何点,则将新点添加到列表中。
- 在检查输入中的每个点之后,结果是输入中的点数减去列表中的点数。
由于对于任意两个随机点a和b,a<b或b<a的概率为25%,因此列表不会变得非常大(除非输入被特别制作为包含几乎没有严格小于任何其他点的点)。
以下代码的有限测试(100个案例),在一个立方空间中随机分布了1,000,000个点,结果显示平均列表大小约为116(最大值为160),检查一个点是否严格小于另一个点的次数约为1,333,000次(最大值为2,150,000次)。
(对10,000,000个点进行了一些测试,结果显示平均检查次数约为11,000,000次,列表大小约为150。)
因此,在实践中,平均复杂度接近于N而不是N×N。
function xyzLessCount(input) {
var list = [input[0]];
for (var i = 1; i < input.length; i++) {
var append = true;
for (var j = 0; j < list.length; j++) {
if (xyzLess(input[i], list[j])) {
append = false;
break;
}
if (xyzLess(list[j], input[i])) {
list[j] = input[i];
for (var k = list.length - 1; k > j; k--) {
if (xyzLess(list[k], list[j])) {
list.splice(k, 1);
}
}
append = false;
break;
}
}
if (append) list.push(input[i]);
}
return input.length - list.length;
function xyzLess(a, b) {
return a.x < b.x && a.y < b.y && a.z < b.z;
}
}
var points = [];
for (var i = 0; i < 1000000; i++) {
points.push({x: Math.random(), y: Math.random(), z: Math.random()});
}
document.write("1000000 → " + xyzLessCount(points));
(1,5,2) < (2,4,2)
吗?你的意思是最靠近原点,就像d^2 = x^2+y^2+z^2
并比较距离d
吗? - John Alexiou