查找3d空间中所有点的数量是否严格小于该空间中的任何点?

时间:2016-09-04 05:22:40

标签: algorithm 3d

我们在3d空间中给出n个点,我们需要找到严格小于3d空间中至少一个点的所有点的计数 即。

x1<x2 and y1<y2  and z1<z2

所以(x1,y1,z1)就是这样一个点。

For example,Given points

1 4 2
4 3 2
2 5 3


(1,4,2)<(2,5,3)

So the answer for the above case should be the count of such points i.e. 1.

我知道这可以通过O(n ^ 2)算法解决,但我需要更快的东西,我尝试通过一个维度进行排序,然后只搜索密钥的大部分,但它仍然是o(n ^ 2 ) 最糟糕的情况。

这样做的有效方法是什么?

2 个答案:

答案 0 :(得分:1)

有一种优化搜索的方法可能比O(n^2)更快 - 我欢迎反采样输入。

保留三个点的索引列表,分别按x,y和z排序。制作第四个列表,将每个点与它在每个列表中的位置相关联(indexes在下面的代码中;例如,indexes[0] = [5,124,789]表示第一个点在x排序列表中是第5个,在第124个位置是y排序列表,z排序列表中的第789位。

现在迭代点 - 选择点最高的列表并根据列表中较高的索引点测试点,如果该点严格小于其中一个,则提前退出。如果所有三个列表上的点都很低,则找到严格更高点的可能性更大。否则,其中一个列表中的较高位置意味着较少的迭代。

JavaScript代码:

function strictlyLessThan(p1,p2){
  return p1[0] < p2[0] && p1[1] < p2[1] && p1[2] < p2[2];
}

// iterations
var it = 0;

function f(ps){
  var res = 0,
      indexes = new Array(ps.length);
  
  // sort by x
  var sortedX = 
        ps.map(function(x,i){ return i; })
          .sort(function(a,b){ return ps[a][0] - ps[b][0]; });
  
  // record index of point in x-sorted list
  for (var i=0; i<sortedX.length; i++){
    indexes[sortedX[i]] = [i,null,null];
  }
  
  // sort by y
  var sortedY = 
        ps.map(function(x,i){ return i; })
          .sort(function(a,b){ return ps[a][1] - ps[b][1]; });
  
  // record index of point in y-sorted list
  for (var i=0; i<sortedY.length; i++){
    indexes[sortedY[i]][1] = i;
  }
  
  // sort by z
  var sortedZ = 
        ps.map(function(x,i){ return i; })
          .sort(function(a,b){ return ps[a][2] - ps[b][2]; });
  
  // record index of point in z-sorted list
  for (var i=0; i<sortedZ.length; i++){
    indexes[sortedZ[i]][2] = i;
  }
  
  // check for possible greater points only in the list
  // where the point is highest
  for (var i=0; i<ps.length; i++){
    var listToCheck,
        startIndex;
    
    if (indexes[i][0] > indexes[i][1]){
      if (indexes[i][0] > indexes[i][2]){
        listToCheck = sortedX;
        startIndex = indexes[i][0];
      } else {
        listToCheck = sortedZ;
        startIndex = indexes[i][2];
      }
      
    } else {
      if (indexes[i][1] > indexes[i][2]){
        listToCheck = sortedY;
        startIndex = indexes[i][1];
      } else {
        listToCheck = sortedZ;
        startIndex = indexes[i][2];
      }
    }
    
    var j = startIndex + 1;
 
    while (listToCheck[j] !== undefined){
      it++;
      var point = ps[listToCheck[j]];
 
      if (strictlyLessThan(ps[i],point)){
        res++;
        break;
      }
      j++;
    }
  }
  
  return res;
}

// var input = [[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]];

var input = new Array(10000);

for (var i=0; i<input.length; i++){
  input[i] = [Math.random(),Math.random(),Math.random()];
}

console.log(input.length + ' points');
console.log('result: ' + f(input));
console.log(it + ' iterations not including sorts');

答案 1 :(得分:0)

我怀疑最坏情况的复杂性可以降低到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&lt; b或b&lt; 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]];                                // put first point in list
    for (var i = 1; i < input.length; i++) {              // check every point in input
        var append = true;
        for (var j = 0; j < list.length; j++) {           // against every point in list
            if (xyzLess(input[i], list[j])) {             // new point < list point
                append = false;
                break;                                    // continue with next point
            }
            if (xyzLess(list[j], input[i])) {             // new point > list point
                list[j] = input[i];                       // replace list point
                for (var k = list.length - 1; k > j; k--) {
                    if (xyzLess(list[k], list[j])) {      // check rest of list
                        list.splice(k, 1);                // remove list point
                    }
                }
                append = false;
                break;                                    // continue with next point
            }
        }
        if (append) list.push(input[i]);                  // append new point to list
    }
    return input.length - list.length;

    function xyzLess(a, b) {
        return a.x < b.x && a.y < b.y && a.z < b.z;
    }
}

var points = [];                                          // random test data
for (var i = 0; i < 1000000; i++) {
    points.push({x: Math.random(), y: Math.random(), z: Math.random()});
}
document.write("1000000 &rarr; " + xyzLessCount(points));