繁体   English   中英

如何有效地比较二维数组

[英]How to compare 2d array effectively

这是一个二维数组

{ {0.3072, 1.1262, 1.5706},
  {0.5068, 1.0630, 0.9470},
  {0.2470, 0.6872, 1.9626},
  {1.0686, 1.1348, 1.7506},
  {1.2874, 1.5664, 0.2470},
  {0.9072, 0.6268, 0.6684},
  {1.3164, 0.6506, 1.8462},
  {0.9072, 0.6268, 0.8706},
  {0.3072, 1.1262, 1.5706} }

我需要根据以下条件比较每一行:

  1. a 点的所有坐标都小于或等于它们对应的 b 点坐标。
  2. 至少存在一个点 a 的坐标严格小于其对应的点 b 的坐标。

如果条件为真,那么我将删除点b。 这里有些例子:

  1. 第 1 点和第 4 点比较后,第 4 点将被删除。 因为点 1 的所有坐标都小于它们对应的点 4 的坐标。
  2. 第 6 点和第 8 点比较后,第 8 点将被删除。 因为点 6 的坐标 3 小于点 8 的对应坐标,尽管它们的其他两个坐标具有相同的值。
  3. 点 1 和点 2 比较后,不会删除任何点。 因为点 1 的坐标 1 小于点 2 的对应坐标,但是点 1 的坐标 2 和坐标 3 大于点 2 的对应坐标,反之亦然。
  4. 第 1 点和第 9 点比较后,不会删除任何点。 因为它们的所有坐标都是等价的。

从上面的二维数组中,我将删除点 4,7,8。

我的尝试:我正在使用嵌套循环来比较所有点

for(int i = 0; i < N; i++){
        for(int j = 0; j < N; j++){
            int sum = 0;
            for(int k = 0; k < 3; k++){
                if(point[i][k] <= point[j][k])
                    sum++;
            }
            if(sum == 3)
                ... // delete point[j]
        }
    }

这种尝试有效,但效率不高,它的大 O 表示法是 O(n^3)。 是否有任何有效的算法可以进行这种比较?

您有N行数据,每行有M列。 您的解决方案是 O( M · N ²) 与 O(1) 空间。 下面的代码是 O( M · N log N ),但它需要 O( N ²) 额外的空间,所以它可能不是一个非常实用的解决方案。 但它确实加快了大N的过程。

首先,我假设所有的行都是不同的。 (这使得解释算法更容易,但我稍后会回到这一点。)

我们可以比较两行rs 的k列中的两个值。 如果r kr krk中“占主导地位” 如果对于所有k存在支配r的“所有支配”行s ,则必须删除行r

我们可以通过按该列中的值对行进行排序来找到每列k的“支配”。 如果在排序表中ns之前则行rk中占主导地位。

作为一个简单的示例,假设您有一行的值小于所有行的所有其他值。 该节点支配所有其他要删除的节点。 在所有排序表中,此节点将是第一个。

让我们看一下您的示例,按三列排序:

 2     0.2470  0.6872  1.9626     5 ▼   0.9072  0.6268  0.6684     4     1.2874  1.5664  0.2470
 0     0.3072  1.1262  1.5706     7 ○   0.9072  0.6268  0.8706     5 ▼   0.9072  0.6268  0.6684
 8     0.3072  1.1262  1.5706     6 ○   1.3164  0.6506  1.8462     7 ○   0.9072  0.6268  0.8706
 1     0.5068  1.0630  0.9470     2     0.2470  0.6872  1.9626     1     0.5068  1.0630  0.9470
 5 ▼   0.9072  0.6268  0.6684     1     0.5068  1.0630  0.9470     0     0.3072  1.1262  1.5706
 7 ○   0.9072  0.6268  0.8706     0     0.3072  1.1262  1.5706     8     0.3072  1.1262  1.5706
 3 ○   1.0686  1.1348  1.7506     8     0.3072  1.1262  1.5706     3 ○   1.0686  1.1348  1.7506
 4     1.2874  1.5664  0.2470     3 ○   1.0686  1.1348  1.7506     6 ○   1.3164  0.6506  1.8462
 6 ○   1.3164  0.6506  1.8462     4     1.2874  1.5664  0.2470     2     0.2470  0.6872  1.9626

标记应删除的节点。 这里,第 5 行 ( ) 支配所有删除候选者,即第 3、6 和 7 行。此外,第 1 行支配第 3 行。

我们现在必须找到一种方法来确定每一行r是否存在一个占主导地位的行a ( r )。 我们可以使用一组S ( r ) 来做到这一点,它以完整的行集R 开始 每当行sr控制时,它就会从该集合中删除。 处理完所有行k后,集合S ( r ) 是行r的所有支配行的集合。

一步步:

  • let S ( r ) = R for each r , rR
  • 对于每一列k , k = 1, ..., M
    • 对表进行排序:索引k ( r ) <索引k ( s ) ⇔ r k < s k
    • A = ∅
    • 对于排序表 R k中的每个索引i = 0, ..., N
      • r = R i k
      • S ( r ) = S ( r ) ∩ A
      • A = A ∪ { r }
  • 标记所有r , S ( r ) = ∅ 删除。

N行的M次排序给出了 O( M · N log N ) 的总体时间复杂度。 NS i使 O( N ²) 额外空间。

表示从 1 到N的一组数字的一种好方法是位数组,这意味着一组S i占用 ⌊ N − 1⌋ / 8 个字节。 我看不到任何更有效的集合表示。 它们不是稀疏的,因为它们 go 从R到∅。 其他数据结构可能更节省空间,但需要更长的查找时间。

现在关于那些重复的行:如果我们有两个相同的行rsr k = s k , k = 1, ..., M ,有两种情况:

  • 存在另一行a全占rs :那么, rs都应该被删除。
  • 没有其他占主导地位的行: rs都应该保留。

第一种情况已经由算法处理。 第二种情况可以通过使用稳定的排序算法来处理。 稳定的排序算法在排序时不会改变相同项目的顺序。 (C 的qsort不保证是稳定的。)

在我们的算法中使用稳定排序会产生以下属性:

  • 对所有行进行排序后,相同的项目将彼此相邻;
  • 如果rs相同,并且r在原始表中位于s之前,则它将在所有表中位于(并且“支配”) s之前。

当遍历行列表并且当前行与上一行相同并且上一行没有被删除时,也保留这一行。

如果您不进行此额外测试,而是使用稳定的排序算法,则只会保留一组重复行中的第一行。 这对我来说似乎是一个更自然的要求。 (我假设至少有一行必须不同才能删除一行的附加条件只是为了排除两个相同的节点相互抵消。)

C 中的实现(带有硬接线NM ):

#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>

enum {
    N = 9,
    M = 3,
    S = (N / 8) + 1,
};

struct Ref {
    double *ref;
    unsigned index;
    unsigned order;
    uint8_t mask[S];
};

static unsigned cmp_index;

int refcmp(const void *pa, const void *pb)
{
    const struct Ref *a = pa;
    const struct Ref *b = pb;
    
    if (a->ref[cmp_index] == b->ref[cmp_index]) {
        return (a->order < b->order) ? -1 : 1;
    }
    
    return (a->ref[cmp_index] < b->ref[cmp_index]) ? -1 : 1;
}
    
void stable_refsort(struct Ref *ref, unsigned n, unsigned dim)
{
    for (unsigned i = 0; i < n; i++) {
        ref[i].order = i;
    }

    cmp_index = dim;    
    qsort(ref, n, sizeof(*ref), refcmp);
}

int same(const struct Ref a, const struct Ref b)
{
    for (unsigned i = 0; i < M; i++) {
        if (a.ref[i] != b.ref[i]) return 0;
    }
    
    return 1;
}

int main(void)
{
    double line[N][M] = {
        {0.3072, 1.1262, 1.5706},
        {0.1068, 1.0630, 0.9470},
        {0.2470, 0.6872, 1.9626},
        {1.0686, 1.1348, 1.7506},
        {1.2874, 1.5664, 0.2470},
        {0.9072, 0.6268, 0.6684},
        {1.3164, 0.6506, 1.8462},
        {0.9072, 0.6268, 0.8706},
        {0.3072, 1.1262, 1.5706}
    };
    struct Ref ref[N];
    uint8_t zero[S] = {0};
    int kept = 0;

    for (unsigned i = 0; i < N; i++) {
        ref[i].ref = line[i];
        ref[i].index = i;
        memset(ref[i].mask, ~0u, S);
    }
    
    for (unsigned dim = 0; dim < M; dim++) {
        uint8_t mask[S] = {0};
        
        stable_refsort(ref, N, dim);

        for (unsigned i = 0; i < N; i++) {
            unsigned s = S;
            unsigned k = ref[i].index;

            while(s--) ref[i].mask[s] &= mask[s];

            mask[k / 8] |= (1u << (k % 8));
        } 
    }

    for (unsigned i = 0; i < N; i++) {
        if (memcmp(ref[i].mask, zero, S)) {
            if (kept && same(ref[i - 1], ref[i])) {
                kept = 1;
            } else {
                printf("delete %u\n", ref[i].index);
                kept = 0;
            }
        } else {
            kept = 1;
        }        
    }
    
    return 0; 
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM