[英]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} }
我需要根据以下条件比较每一行:
如果条件为真,那么我将删除点b。 这里有些例子:
从上面的二维数组中,我将删除点 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的过程。
首先,我假设所有的行都是不同的。 (这使得解释算法更容易,但我稍后会回到这一点。)
我们可以比较两行r和s 的k列中的两个值。 如果r k ≤ r k , r在k中“占主导地位” 。 如果对于所有k存在支配r的“所有支配”行s ,则必须删除行r 。
我们可以通过按该列中的值对行进行排序来找到每列k的“支配”。 如果在排序表中n在s之前,则行r在k中占主导地位。
作为一个简单的示例,假设您有一行的值小于所有行的所有其他值。 该节点支配所有其他要删除的节点。 在所有排序表中,此节点将是第一个。
让我们看一下您的示例,按三列排序:
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 开始。 每当行s由r控制时,它就会从该集合中删除。 处理完所有行k后,集合S ( r ) 是行r的所有支配行的集合。
一步步:
N行的M次排序给出了 O( M · N log N ) 的总体时间复杂度。 N组S i使 O( N ²) 额外空间。
表示从 1 到N的一组数字的一种好方法是位数组,这意味着一组S i占用 ⌊ N − 1⌋ / 8 个字节。 我看不到任何更有效的集合表示。 它们不是稀疏的,因为它们 go 从R到∅。 其他数据结构可能更节省空间,但需要更长的查找时间。
现在关于那些重复的行:如果我们有两个相同的行r和s与r k = s k , k = 1, ..., M ,有两种情况:
第一种情况已经由算法处理。 第二种情况可以通过使用稳定的排序算法来处理。 稳定的排序算法在排序时不会改变相同项目的顺序。 (C 的qsort
不保证是稳定的。)
在我们的算法中使用稳定排序会产生以下属性:
当遍历行列表并且当前行与上一行相同并且上一行没有被删除时,也保留这一行。
如果您不进行此额外测试,而是使用稳定的排序算法,则只会保留一组重复行中的第一行。 这对我来说似乎是一个更自然的要求。 (我假设至少有一行必须不同才能删除一行的附加条件只是为了排除两个相同的节点相互抵消。)
C 中的实现(带有硬接线N和M ):
#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.