【问题标题】:How to efficiently eliminate duplicate elements in double array (in C)?如何有效地消除双数组(在 C 中)中的重复元素?
【发布时间】:2014-05-16 11:18:06
【问题描述】:

虽然我已经阅读了有趣的线程Algorithm: efficient way to remove duplicate integers from an array,但我还没有找到满意的答案:

我有一个 doubles 的一维数组,它通常很小(最多只包含三个元素) - 尽管为了通用性,这不需要成为标准。

此外,我不想只找到真正的重复项,而是从某种意义上说元素的差异低于某个阈值的重复项。虽然这个要求很容易处理,但我的实际问题是:如何以尽可能少的开销在 ANSI C 中实现一般的重复删除?

备注:我无法从上述线程中找到解决方案的主要原因有三个:

  1. 许多给定的解决方案使用纯 C 以外的语言,因此这没有任何特别的帮助。
  2. 如果数组中的所有元素都相等,则某些解决方案将无法正常工作,我的情况可能就是这种情况。
  3. 某些描述的算法似乎仅适用于整数值。作为一个 C 菜鸟,任何建议都非常感谢。

附录: 在某种伪代码中,我想要实现的是:

1) Sort the array in ascending manner
2) Loop through array until element before the last one
   - Check if difference of element i to element i+1 is smaller than threshold
     -> If yes, store mean value as first element of new array
     -> If no, store original value of element i as first element of new array
3) Start the same again in order to check if now the differences between the new array elements lie below the threshold
   -> Abort if no difference is smaller than the threshold anymore

因此,我的主要问题是:如何实现第 3 步,以便可以进行任意次数的迭代,并且只要存在“太接近”的数组元素(相对于我的阈值),函数就会运行。

【问题讨论】:

  • 我会警告你,阈值条件使这成为一个难题。例如,如果您的阈值为 0.25,并且您有三个值 1.0、1.2 和 1.4,则有 多个 个可能的“正确”答案(消除 1.2,因为它在 1.0 的阈值内,消除 1.4出于同样的原因,关于 1.2 甚至可能不再存在,请消除 both 等)。
  • 如果数组保证很小,一个简单的二环(二次)独奏就足够了。如果你想强制相邻的值:排序+分成强制块+用平均值替换每个块
  • @joop 这就是我想做的事情。但是:如何将数组划分为可强制执行的块?这几天我一直在思考这个问题,但我离解决方案还差得远。我什至不再关心效率了......
  • @WhozCraig 我理解并在原始问题的附录中添加了我对如何解决此问题的想法。不幸的是,我仍然缺乏为该算法找到可行的 C 解决方案的技能。
  • 0) 是否允许对数组进行排序,还是应该保持原始顺序?如果允许对数组进行排序/更改:1) 排序 2) 查找块边界 3) 聚合块。 (第 2 步和第 3 步可以结合使用)如果不允许排序,您最终可能会得到某种树,例如区间/范围树。

标签: c arrays algorithm duplicate-removal


【解决方案1】:

这个问题是element distinctness problem 的变体。

由于您不仅要查找“完全重复”,还要查找“紧密重复”,因此解决方案不能包含散列。

解决方案基本上是对数组进行排序,然后对其进行迭代并“跳过”重复的元素。

这个解是 O(nlogn),并且是最优解,因为它是任意元素区别的最优解。

类C伪代码:

#define epsilon SOME_SMALL_TOLERANCE_VALUE
int trimDupes(double[] arr,int n) { 
   sort(arr);
   int i = 0;
   int currPos = 0;
   double last = -Infinity; //min double, negative infinity
   for (i = 0; i < n; i++) { 
      if (abs(last-arr[i]) > epsilon) {
          arr[currPos++] = arr[i];
          last = arr[i]; //getting this out of the condition gets a bit different behavior, think what you need.
       }
    }
    return curr; //new length of the array - after it everything is garbage.
}

此解决方案使用非常少的额外空间 [基本上是排序算法所需的任何空间 + 一些常量],以及 O(nlogn) 排序时间 + 额外的单次迭代。

【讨论】:

  • 解决方案可以包含哈希,但不能直接包含。首先,将散列键舍入为 epsilon 的倍数,其次,检查邻居 (x+ε x−ε)。
  • @amit 不幸的是,此代码的实现对我不起作用。也许我把一些东西与 currPos、curr 和/或 arr 混淆了,因为我并没有真正理解最后的评论和 return 参数。
  • @Michael 您在尝试什么,为什么会失败?一般的方法应该能够消除近乎欺骗,并且只保留低于所需差异阈值的第一个元素(在欺骗组中)。
  • @amit 我试图将我的源代码放在这里,但它在评论框中不起作用。有没有其他方法可以展示我所做的事情?
  • @Michael 我创建了一个chat room 希望它会工作。稍后我将对其进行采样,写下您所有的内容,然后我将全部阅读并在有时间时回复(今天)
【解决方案2】:

对数组进行排序。然后遍历数组,复制到另一个数组。如果与当前项目相比的下一个项目在阈值内,则有一个内部循环将当前项目与所有剩余项目进行比较,跳过阈值内的所有项目。当您到达阈值之外的项目时,您将获得下一个当前项目。

通过确定开始比较的起始元素按特定顺序排列,您就可以回避 cmets 中概述的问题来回答您的问题。但请注意,如果您更改顺序(升序排序与降序排序),结果会有所不同。

【讨论】:

    【解决方案3】:

    到目前为止,我已经找到了一个适合我的解决方案,尽管需要几个函数调用并且复杂性可能不是最佳的:

    #include <math.h>
    #include <stdlib.h>
    
    int compareDouble (const void * a, const void * b)
    {
      if ( *(double*)a <  *(double*)b ) return -1;
      if ( *(double*)a == *(double*)b ) return 0;
      if ( *(double*)a >  *(double*)b ) return 1;
    }
    
    int main(void)
    {
      double x[6] = {1.0,4.0,17.0,4.0,17.0,17.0};
      size_t n = sizeof(x)/sizeof(x[0]);
      const double thresh = 5.0;
    
      qsort(x, n, sizeof(double), compareDouble);
    
      size_t i = 0;
      size_t j = 0;
    
      while(i<=n-1)
      {
        if(i==n-1)
        {
          x[j++] = x[i];
          break;
        }
        else if(fabs(x[i]-x[i+1])>thresh)
        {
          x[j++] = x[i++];
        }
        else
        {
          x[j++] = (x[i]+x[i+1])/2;
          i+=2;
        }
      } 
    
      for(i=0; i<j; i++)
      {
        printf("result[i] = %.2f\n",i,x[i]);
      }
    }
    return 0;
    

    感谢任何额外的 cmets 或评论!

    【讨论】:

    • void main() 可能在您的实现中作为扩展被允许。我建议您使用标准保证的int main(void) 可以在所有实现中工作。
    猜你喜欢
    • 2023-03-31
    • 2010-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    • 2020-07-23
    • 2016-04-07
    • 1970-01-01
    相关资源
    最近更新 更多