有没有更好的方法来实现2-SUM算法

Is there a better way to implement the 2-SUM algorithm?

本文关键字:实现 2-SUM 算法 方法 更好 有没有      更新时间:2023-10-16

目前,我正在尝试创建一个2-SUM算法,该算法将在给定一组约100万个整数的情况下,找到目标值t(-10000<=t<=10000)的数量,这些目标值由集合中任意两个值x,y的总和组成。

对于t的单个值,我对2-SUM没有问题,只需使用哈希表并查找表中的每个哈希条目x(如果存在另一个条目t-x

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

所以,我的问题是,有没有什么提示可以更好地处理约20001个目标,而不必强行通过所有20001个值?

以下是我用于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;
     }
  }
}

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

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

  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])<Val:二进制搜索(iS+1->iE)中的最小值,大于或等于(Val-S[iE]),并将iS设置为匹配
    4.3如果iS>iE,则Val不存在

这为您提供了O(n log(n))用于排序,以及O(m n)对于-10000->10000为20001)用于搜索,尽管实际上,搜索的性能将比O(m n)好得多。由于m>log(n),整个解决方案是O(mn)

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

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

std::lower_bound函数是作为二进制搜索实现的,这意味着在最坏的情况下,对于1000000个整数,您需要进行20次比较才能找到匹配项。如果你在-10000.-10000循环中这样做,你会得到20000*20=400000的比较,这应该需要不到一个小时的时间(我的猜测是几分钟,取决于CPU功率)。

在一个无序集合上的map::find是一个线性搜索,这意味着在最坏的情况下,你将有20000*1000000=200000000000个比较,这是50000倍的差。

你可以改进二进制搜索(例如,看看你离目标有多"近",如果你的值有特定差异,就从那里切换到线性搜索),但我认为这不会加快搜索速度。

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

感谢所有提供帮助的人!

我通过将输入划分为多个"桶"来解决这个问题,也就是说,我会对数据集进行排序,然后将其拆分为间隔为10000的桶。因此,最小的10k数字进入第一个桶,下一个10k进入第二个桶,以此类推。。。。我会把它分成,所以当我必须搜索条目t-x时,我会搜索我的10000个数字,而不是所有1000000个数字。