【问题标题】:Is there a better way to implement the 2-SUM algorithm?有没有更好的方法来实现 2-SUM 算法?
【发布时间】:2015-11-26 19:59:52
【问题描述】:

目前,我正在尝试创建一个 2-SUM 算法,给定一组大约 100 万个整数,找到目标值的数量 t (-10,000 由集合中任意两个值 x,y 的总和形成。

我对 t 的单个值的 2-SUM 没有问题,只需使用哈希表并在表中查找每个哈希条目 x 如果存在另一个条目 t-x 。这将在O(N) 时间运行。

但是,现在我必须找到多个 t 值,从 -10000 到 10000。如果我只使用一个普通的 for 循环,那么现在运行时将是 O(N^2)。 我已经尝试过这段代码,它从 -10000 到 10000 对所有 t 进行暴力破解,但它运行速度太慢(大约 1 小时无法执行)。

所以,我的问题是,是否有任何提示可以更好地处理约 20,001 个目标,而无需暴力破解所有 20,001 个值?

这是我用于O(N^2) 解决方案的代码:

for(long long t = -10000; t <= 10000; t++)
{
  for(unordered_set<long long>::iterator it=S.begin(); it != S.end(); ++it)
  {
     long long value = *it;
     if((S.find(t-value) != S.end()) & (t-value != value))
     {
        values++;
        //cout << "Found pair target " << t << " " <<   value << " " << t-value << '\n';
        break;
     }
  }
}

【问题讨论】:

  • 第 3 行中的 S 是什么?
  • are there any hints for better ways 是的,确保您的数据集首先排序。
  • @user3437460 我试过使用有序的 set (cplusplus.com/reference/set/set) ,但它有一个对数 O(log N) 访问时间。你知道更好的哈希表吗?
  • @erip S 是我使用的数据集(类似哈希表)。
  • @PatrickYu 如果允许您先对数据集进行排序,然后使用合并排序/快速排序等有效算法进行排序,即O(n log n)

标签: c++ algorithm


【解决方案1】:

更好的方法是使用有序集(如果值是唯一的,或者如果您关心重复,则使用有序数组/列表)。

然后,您使用以下方法为您的值搜索匹配对:

  1. 对于每个 Val (-10000, -9999, ...)
  2. iS为0
  3. iE 为长度 - 1
  4. (S[iS] + S[iE]) != Val
    4.1 (S[iS] + S[iE]) > Val : 二分查找(​​iS -> iE - 1)中的最大值,小于等于(Val - S[iS]) 并设置 iE 匹配。
    4.2 (S[iS] + S[iE]) : 在(iS +1 -> iE) 中二分查找最小值,大于等于(Val - S[iE]) 并设置 iS 匹配。
    4.3 如果iS > iEVal不存在。

这给你 O(n log(n)) 用于排序,O(m n)m 是 20001 表示 -10000 -> 10000) 用于搜索虽然实际上,搜索会比 O(m n) 执行得更好。由于m > log(n),整个解是O(m n)

它可以通过使用匹配值的映射进一步优化,并且在每次迭代中,找到匹配后,推进 iE 直到 (S[iS] + S[iE]) > maxValue (10000) 并将所有和标记为已找到,则外循环中的迭代次数较少。

【讨论】:

  • while 循环不是 O(N),因为在最坏的情况下,iSiE 将遍历列表直到它们在中点相遇?
  • @PatrickYu - 是的,我只是在想这个。答案不准确 - 将修改。
【解决方案2】:

正如其他人已经建议的那样,如果您想要一种“尽力而为”的方法(意味着它可能不是最好的,但仍然足够好),您可以对数据进行排序并使用 std::lower_bound 进行搜索。

std::lower_bound 函数被实现为二进制搜索,这意味着在最坏的情况下,对于 1000000 个整数,您将进行 20 次比较才能找到匹配项。如果您在 -10000..10000 循环内执行此操作,您将获得 20000*20 = 400000 次比较,这应该花费不到一个小时(我的猜测是几分钟,具体取决于 CPU 能力)。

unordered_set 上的 map::find 是线性搜索,这意味着在最坏的情况下,您将进行 20000*1000000 = 20000000000 次比较,这会差 50000 倍。

您可以改进二分搜索(例如,通过查看您与目标的“接近”程度,如果您的值存在特定差异,则从那里切换到线性搜索),但我认为这不会加快搜索速度。

还有其他方法,可能更快(也许您可以使用 64 位精度的 15625 个整数丢弃重复项,并设置与数据集中的值匹配的位,从而为您和 O(n) 时间进行设置和 O(1 ) 用于查找,但您将需要两组,一组用于正值,另一组用于负值),但实现起来要困难得多。

【讨论】:

    【解决方案3】:

    感谢所有帮助过的人!

    我通过将输入划分为多个“桶”解决了这个问题,也就是说,我将对数据集进行排序,然后将其拆分为间隔为 10,000 的桶。因此,最小的 10k 数字进入第一个存储桶,接下来是 10k 到第二个,依此类推......我的 10,000 个数字,而不是全部 1,000,000 个数字。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-08-19
      • 2010-12-08
      • 2011-10-23
      • 2021-11-15
      • 1970-01-01
      • 2017-11-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多