在具有特定属性的高维度的数组中搜索

时间:2014-09-11 13:46:33

标签: algorithm

我有一个3D数组,其中值是单调的。如何找到所有(x,y),| f(X,Y,Z) - v1 | <吨。

4 个答案:

答案 0 :(得分:2)

有Omega(n ^ 2)个点,其坐标总和为n-1。关于这些点的值如何相互比较,没有任何事先知道,因此,在最坏的情况下,所有这些点都必须进行检查。通过在每个constant-z切片中运行2D算法,可以提供与常数因子匹配的上限。

答案 1 :(得分:1)

对于每个值(例如v1),执行以下步骤:

  1. 对与X轴相切的4个立方体面执行2D算法(Y = 0,Y = n-1,Z = 0,Z = n-1)。将得到的匹配(X,Y,Z)单元格的集合索引为下一步的X坐标。
  2. 沿X轴(X = 0..n-1)对所有n个切片执行2D算法,使用步骤1的结果初始化2D算法的第一个边界点。如果给定的x坐标没有匹配的单元格,请在恒定时间内移动到下一个切片。
  3. 最坏情况复杂度为O(O(2D算法)* n)。

    对于多个值(v2等),保留函数评估的缓存,并为每个值重新执行算法。对于100 ^ 3,密集阵列就足够了。

    将此视为等值面提取算法可能会有用,尽管您的单调性约束使其更容易。

答案 2 :(得分:1)

如果3d数组在每个维度上单调递减,那么我们知道如果

f(x0, y0, z0) < v1 - t
          or
f(x1, y1, z1) > v1 + t

那么子数组f(x0...x1, y0...y1, z0...z1)的任何元素都不能包含任何有趣的点。要看到这一点,请考虑

f(x0, y0, z0) <= f(x, y0, z0) <= f(x, y, z0) <= f(x, y, z)

适用于子阵列的每个(x, y, z),并且(x1, y1, z1)具有类似的关系(反向)。因此f(x0, y0, z0)f(x1, y1, z1)分别是子数组的最小值和最大值。

然后可以使用递归细分方案实现简单的搜索方法:

template<typename T, typename CBack>
int values(Mat3<T>& data, T v0, T v1, CBack cback,
           int x0, int y0, int z0, int x1, int y1, int z1) {
    int count = 0;
    if (x1 - x0 <= 2 && y1 - y0 <= 2 && z1 - z0 <= 2) {
        // Small block (1-8 cells), just scan it
        for (int x=x0; x<x1; x++) {
            for (int y=y0; y<y1; y++) {
                for (int z=z0; z<z1; z++) {
                    T v = data(x, y, z);
                    if (v >= v0 && v <= v1) cback(x, y, z);
                    count += 1;
                }
            }
        }
    } else {
        T va = data(x0, y0, z0), vb = data(x1-1, y1-1, z1-1);
        count += 2;
        if (vb >= v0 && va <= v1) {
            int x[] = {x0, (x0 + x1) >> 1, x1};
            int y[] = {y0, (y0 + y1) >> 1, y1};
            int z[] = {z0, (z0 + z1) >> 1, z1};
            for (int ix=0; ix<2; ix++) {
                for (int iy=0; iy<2; iy++) {
                    for (int iz=0; iz<2; iz++) {
                        count += values<T, CBack>(data, v0, v1, cback,
                                                  x[ix], y[iy], z[iz],
                                                  x[ix+1], y[iy+1], z[iz+1]);
                    }
                }
            }
        }
    }
    return count;
}

代码基本上接受一个子数组,如果最低元素太大或者最高元素太小,则简单地跳过搜索,否则将数组拆分为8个子多维数据集。当子阵列很小(2x2x2或更小)并且在这种情况下执行完整扫描时,递归结束。

通过实验,我发现通过这种非常简单的方法,可以搜索通过将元素f(i,j,k)设置为max(f(i-1,j,k), f(i,j-1,k), f(i,j,k-1)) + random(100)生成的100x200x300元素的数组中间值,并且t = 1仅检查约3%的元素(在范围内找到的每个元素检查25个元素)。

Data 100x200x300 = 6000000 elements, range [83, 48946]
Looking for [24594-1=24593, 24594+1=24595]
Result size = 6850 (5.4 ms)
Full scan = 6850 (131.3 ms)
Search count = 171391 (25.021x, 2.857%)

答案 3 :(得分:0)

由于函数不减少,我认为你可以用二进制搜索做一些事情 在(x, 1, 1)(列)向量中,您可以进行二进制搜索,以找到符合您要求的范围O(log(n))
要查找要查找的列向量,可以对(x, y, 1)(切片)向量进行二进制搜索,仅检查第一个和最后一个点,以了解该值是否可以落入其中,这将再次采用O(log(n))
要知道要查看哪些切片,您可以二元搜索整个多维数据集,检查需要(0, 0), (x, 0), (x, y), (0, y)的4个点(O(log(n)))。 总的来说,算法将采用log(z) + a * log(y) + b * log(x),其中a是匹配切片的数量,b是匹配列的数量。
天真地计算最坏的情况是O(y * z * log(x))