【问题标题】:How to flag data duplicates in an array as fast as possible in C?如何在 C 中尽可能快地标记数组中的数据重复项?
【发布时间】:2013-10-14 01:14:22
【问题描述】:

我已经编写了半个程序来进行一些主要的浮点数学运算。根据它开始的数据,它可以生成描述线段的非常大的数组。这些线段的位置是用笛卡尔坐标系记录的,用浮点数记录线段各端的X、Y、Z位置。我不能在两端使用 X,Y,Z,所以我使用 X,Y,Z 作为开始,使用 Q,R,S 作为结束。所以基本上我想做的是标记所有相同或翻转的行,以便第一行的 Q、R、S 等于第二行的 X、Y、Z 和第一行的 X、Y、Z 等于Q,R,S 在第二行。我目前的标记技术是将 X 设置为 -1,因为我知道没有一条线会以负坐标结束。我不想标记两条线,只标记除一条之外的所有线。这是我目前的功能:

int filter(int lines)
{
printf("Filtering...\n");
refline=0;
scanline=1;
while(refline<(lines))
    {
        if( segpointX[refline] == segpointQ[scanline] && segpointY[refline] == segpointR[scanline] && segpointZ[refline] == segpointS[scanline] && segpointQ[refline] == segpointX[scanline] && segpointR[refline] == segpointY[scanline] && segpointS[refline] == segpointZ[scanline] 
         || segpointX[refline] == segpointX[scanline] && segpointY[refline] == segpointY[scanline] && segpointZ[refline] == segpointZ[scanline] && segpointQ[refline] == segpointQ[scanline] && segpointR[refline] == segpointR[scanline] && segpointS[refline] == segpointS[scanline])
            {
                //printf("Origional: %f  %f  %f  ><  %f  %f  %f\n",segpointX[refline],segpointY[refline],segpointZ[refline],segpointQ[refline],segpointR[refline],segpointS[refline]);
                //printf("Duplicate: %f  %f  %f  ><  %f  %f  %f\n\n",segpointX[scanline],segpointY[scanline],segpointZ[scanline],segpointQ[scanline],segpointR[scanline],segpointS[scanline]);
                segpointX[scanline]=-1;
            }

         scanline++;

        if(scanline==lines+1)
            {
                refline++;
                scanline=refline+1;
            }   
    }
return(0);
}

我知道我有多少行,这就是“行”整数。这段代码完全按照它应该的方式工作,但与我的程序的其余部分相比它真的很慢。我认为必须有一种方法可以更快地做到这一点,但我不确定如何。拥有这个功能真的很可惜,因为它拖累了我的程序的其余部分,考虑到它的所有浮点数学,它的速度非常快。如果没有像样的方法让这个速度比现在快 3 倍,我可能只需要忍受混乱的数据并让下一个函数足够聪明以忽略它。然而,现在标记坏行将非常有用,因为下一个函数已经足够复杂,因为它不会尝试补偿我的数据中的重复项。

【问题讨论】:

  • 线的哪一边留在 XYZ/QRS 中重要吗?顺便说一句,除非您的工作主要是在单个坐标上(例如,您首先处理所有 X,然后处理所有 Y,......)您应该使用一个合适的 struct 数组来改善数据局部性并避免缓存未命中(除了,你会得到更容易理解的代码)。
  • 不,哪一端是 X,Y,Z Q,R,S 都没有关系。现在最重要的是,如果发现重复的行,则将 X 设置为 -1
  • 将你的区间放入类似于二叉搜索树的东西中,这样可以更快地搜索重复项(N*log(N) i/o O(N^2))。可以在这里寻找灵感:cslibrary.stanford.edu/110/BinaryTrees.html
  • 使用哈希表通常会在 O(N) 中找到重复项。

标签: c arrays performance


【解决方案1】:

标记数组中重复项的经典方法是以某种方式对数组进行排序 (O(N·logN)),然后在单次遍历 (O(N)) 中标记/删除连续的相同元素;这具有 O(N·logN) 的总复杂度,而您的方法是 O(N2)。

在您的情况下,所有困难归结为在您的数据点之间建立某种排序关系。

首先,我会将您的线条格式标准化,以便等效线条(相同端点)以相同方式表示。为此,对每一行比较元组 (XYZ)/(QRS);如果 Q 小于 X,则 QRS 与 XYZ 交换;如果 X==Q,则检查 Y 和 R,如果它们再次等于 Z 和 S。

在这个 O(N) 通道结束时,所有等效线都具有相同的 XYZQRS 表示。

现在,如果您不想更改数据的表示形式(6 个独立数组,其中 structs 的单个数组会更简单、更高效),对索引数组进行排序而不是实际数据(此外,如果您不想更改实际数据的顺序,它可能更有效,甚至是唯一可行的可能性)。用从 0 到 lines-1 的数字初始化一个整数数组;然后,您可以使用qsort 函数进行排序,传递您的自定义比较器函数。

此函数将接收要比较的索引;您将使用这些索引来访问相应的 XYZ/QRS 并按顺序比较它们(比较第一个元素的 X 和第二个元素的 X,如果它们相等,则继续 Ys,依此类推)。在排序结束时,您的索引数组将被排序,附近有相同的项目。

现在你可以做最后一遍:扫描索引数组,并将当前索引对应的元素与下一个索引的元素进行比较:如果相等,则将第一个标记为重复;否则,您要么处于重复序列的最后一项,要么处于新序列的开头,因此您将希望(至少暂时)保留该项目。由于物品是有序的,相同的物品都排成一排,因此您可以一次标记它们。


请注意,只有在您希望 exact 匹配时才能正常工作 - 即它不会考虑 FP 不准确性。

【讨论】:

【解决方案2】:

您的算法是N^2。您应该可以在N log N 中执行此操作。然而,到达N log N 所需的复杂性在某种程度上取决于您的数据。

假设您的数据在 X 方向上分布良好,我会这样做。

  1. 对于每个线段,如果需要,将其翻转以使 X
  2. 按 X 对所有线段进行排序 - 这是 O(N log N)。
  3. 现在重复项将相邻,因此只需简单扫描即可将其删除。

这不是 100% 正确,但大致正确 - 你需要处理一些退化的情况..

  1. 你在翻转阶段做什么是X==Q。 (Ans. 基于 Y/Q 翻转。如果也相等,则使用 Z/S)
  2. 如果两个段在排序阶段有相同的 X,你会怎么做。 (Ans. 依次使用 Y、Z、Q、R、S 作为辅助键。)

【讨论】:

    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 2021-10-26
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 2016-04-16
    • 2013-06-28
    相关资源
    最近更新 更多