C# 变量优化的二和算法

C# 变量优化的二和算法,c#,algorithm,C#,Algorithm,我需要计算区间[-1000010000](包括在内)中目标值t的数量,以便在输入文件中存在满足x+y=t的不同数字x,y。我有代码的工作,并希望优化它运行得更快 此外,我们将只计算一次总和t。如果有两个x,y对相加到55,则只会增加一次结果。输入数字可以是正整数和负整数 public static int Calculate(int start, int finish, HashSet<long> numbers) { int result = 0; for (int

我需要计算区间[-1000010000](包括在内)中目标值t的数量,以便在输入文件中存在满足x+y=t的不同数字x,y。我有代码的工作,并希望优化它运行得更快

此外,我们将只计算一次总和t。如果有两个x,y对相加到55,则只会增加一次结果。输入数字可以是正整数和负整数

public static int Calculate(int start, int finish, HashSet<long> numbers)
{
    int result = 0;
    for (int sum = start; sum <= finish; sum++)
    {
        foreach (long n in numbers)
        {
            if (numbers.Contains(sum - n) && n != (sum - n))
            {
                result++;
                break;
            }
        }
    }

    return result;
}
公共静态int计算(int开始、int结束、HashSet数字)
{
int结果=0;

对于(int sum=start;sum此代码可以通过使用更好的数据结构进行大量优化。使用
SortedSet
而不是
HashSet
。现在可以轻松优化代码:

public static int calculate(int start , int finish , SortedSet<long> numbers){
    int count = 0;

    for(long l in numbers){
        SortedSet<long> sub = numbers.GetViewBetween(l - start , l - finish);
        count += sub.Count() - (sub.Contains(l) ? 1 : 0);
    }

    return count;
}
公共静态整数计算(整数开始、整数结束、排序集编号){
整数计数=0;
用于(长l的数字){
SortedSet sub=编号。GetViewBetween(l-开始,l-完成);
计数+=子计数();
}
返回计数;
}

请注意,这段代码可能无法编译,因为我已经很久没有使用c了。您甚至可以通过忽略满足
number.min()+n>finish
number.max()的值来进一步优化这段代码+n

有一种经典的线性时间算法,用于在排序数组中查找两个与指定目标求和的数字
a
。将
i
初始化为第一个索引,将
j
初始化为最后一个索引。如果
a[i]+a[j]
小于目标,增加
i
。如果小于目标,减少
j
。当
i>=j
时停止

使用比较排序对数组进行排序的成本逐渐高于散列排序,但此设置成本可以忽略不计,并且改进的数组内存布局将显著提高性能

为了进一步优化,您可以反转循环结构,并为数组中的每个数字计算出它的组合


要进一步优化(至少是渐进优化),请使用傅里叶变换将阵列的(部分)彼此卷积。由于您的数字不同,我不相信这在实践中会有所改进。

请查看以下解决方案:

    public static int Calculate(int start, int finish, List<long> numbers)
    {
        HashSet<long> resultSet = new HashSet<long>();            
        for (int indexA = 0; indexA < numbers.Count - 1; indexA++)
        {
            for (int indexB = indexA+1; indexB < numbers.Count; indexB++)
            {
                long tempSum = numbers[indexA] + numbers[indexB];
                if ((tempSum >= start) && (tempSum <= finish))
                {
                    resultSet.Add(tempSum);
                }
            }
        }
        return resultSet.Count();
    }
公共静态整数计算(整数开始、整数结束、列表编号)
{
HashSet resultSet=新HashSet();
for(int indexA=0;indexA如果((tempSum>=start)和&(tempSum归功于David Eisenstat),则将所有这些结合在一起,并针对具体案例采取行动:

public static int Calculate(this IEnumerable<long> input, int minSum, int maxSum)
{
    var sortedSet = input.AsParallel().Distinct().OrderBy(n => n).ToArray();
    for (int lo = 0, hi = sortedSet.Length - 1; lo < hi;)
    {
        var sum = sortedSet[lo] + sortedSet[hi];
        if (sum < minSum) lo++;
        else if (sum > maxSum) hi--;
        else return 1 + Calculate(sortedSet, lo, hi, minSum, (int)sum - 1) + Calculate(sortedSet, lo, hi, (int)sum + 1, maxSum);
    }
    return 0;
}
private static int Calculate(long[] sortedSet, int lo, int hi, int minSum, int maxSum)
{
    int count = 0;
    for (int targetSum = minSum; targetSum <= maxSum; targetSum++)
    {
        for (int i = lo, j = hi; i < j;)
        {
            var sum = sortedSet[i] + sortedSet[j];
            if (sum < targetSum) i++;
            else if (sum > targetSum) j--;
            else { count++; break; }
        }
    }
    return count;
}
公共静态int计算(此IEnumerable输入,int minSum,int maxSum)
{
var sortedSet=input.AsParallel().Distinct().OrderBy(n=>n.ToArray();
对于(int-lo=0,hi=sortedSet.Length-1;lo最大总和)高--;
否则返回1+Calculate(sortedSet,lo,hi,minSum,(int)sum-1)+Calculate(sortedSet,lo,hi,(int)sum+1,maxSum);
}
返回0;
}
专用静态整数计算(长[]排序集,整数低,整数高,整数分钟,整数最大和)
{
整数计数=0;
对于(int targetSum=minSum;targetSum targetSum)j--;
else{count++;break;}
}
}
返回计数;
}
编辑更好:

public static int Calculate(this IEnumerable<long> input, int minSum, int maxSum)
{
    if (minSum > maxSum) return 0;
    var sortedSet = input.AsParallel().Distinct().OrderBy(n => n).ToArray();
    return CalculateCore(sortedSet, 0, sortedSet.Length - 1, minSum, maxSum);
}
private static int CalculateCore(long[] sortedSet, int lo, int hi, int minSum, int maxSum)
{
    if (minSum > maxSum) return 0;
    int count = 0;
    while (lo < hi)
    {
        var sum = sortedSet[lo] + sortedSet[hi];
        if (sum < minSum) lo++;
        else if (sum > maxSum) hi--;
        else
        {
            count++;
            if (minSum == maxSum) break;
            if (sum - minSum <= maxSum - sum)
            {
                count += CalculateCore(sortedSet, lo, hi - 1, minSum, (int)sum - 1);
                minSum = (int)sum + 1;
                lo++;
            }
            else
            {
                count += CalculateCore(sortedSet, lo + 1, hi, (int)sum + 1, maxSum);
                maxSum = (int)sum - 1;
                hi--;
            }
        };
    }
    return count;
}
公共静态int计算(此IEnumerable输入,int minSum,int maxSum)
{
如果(最小值>最大值)返回0;
var sortedSet=input.AsParallel().Distinct().OrderBy(n=>n.ToArray();
返回CalculateCore(sortedSet,0,sortedSet.Length-1,minSum,maxSum);
}
专用静态int-CalculateCore(长[]排序集,int-lo,int-hi,int-minSum,int-maxSum)
{
如果(最小值>最大值)返回0;
整数计数=0;
while(lo最大总和)高--;
其他的
{
计数++;
如果(minSum==maxSum)中断;

如果(sum-minSum我相信它基本上可以在O(NlogN)中完成,其中N是构造和的点数,假设M,则要查找的和的范围相对较小

private int CalcSlide(int first, int last, HashSet<long> numbers)
{
  if (last < first)
    return 0;

  // Strategy:
  // running over the input numbers, in ordered fashion from below, we'll scan an "appropriate" sliding window
  // in that same collection to see if we find any sums that we didn't find before.
  // We start out with the lowest number as an "anchor" and determine the HIGHEST number that might be relevant,
  // i.e. that will not sum to beyond "last".
  // Saving that separately, for later use, we scan down until we sum (always with the "anchor") to below "first".
  //
  // We maintain the sums-to-be-found in a hash.
  // As soon as our scan hits a yet-to-be-found sum, we remove it from the hash and update first/last as appropriate.
  // Having completed this cycle we increase the anchor, redetermine the hightest relevant number to start a new scan,
  // (this redetermination starts at the saved previous value, walking down), and then we scan again.
  // Exits occur whever the obvious conditions are fullfilled: there's nothing left to search for, we've nothing left to scan.
  //
  // Time-complexity:
  // Given an input hash set we first construct a sorted array from that: O(nLogn)
  // The subsequent sliding-window-scan procedure is O(N) if we look at scnario's with N (the number of input points) being much larger
  // than the range of sums-to-find. More precisely the worst-case behaviour is O(N M logM), where M is the range of sums to find.
  // (It would be somewhat difficult to construct an scenario where we really have such "worst case behaviour").
  // 1. Obviously, the "outer loop" over the anchor is just "from 1 to N".
  // 2. Computing the upperbound of the scan, for each anchor again, is also linear: it just runs from N-1 down to the anchor
  //    (in separate strides for each anchor).
  // 3. The scan, at each round, is somewhat more complicated. It is potentially long - over all numbers down to the anchor.
  //    However, since all numbers are different (coming from a hashset), the scanwindow can never be larger than the range of sums (yet) to find.
  //    The worst-case behaviour is then O(N*MlogM), where the logM factor arises from having to search for a sum in the hashSet of sums.
  //    Of course, as we find "some" sums on the way, both the search-in-sumHashSet diminishes and the scanwindow gets smaller whenever we find
  //    the current "first" or "last".

  int sumCountOriginal = last - first + 1;

  // Get a sorted array from the provided numbers.
  long[] sortedSet = new long[this.HNumbers.Count];
  int j = 0;
  foreach (long value in HNumbers)
    sortedSet[j++] = value;
  Array.Sort(sortedSet);

  // Create a (relatively small) HashSet from the sums sought.
  // This serves to easily remove items that we find, and then verify if there are any left.
  HashSet<int> HSums = new HashSet<int>();
  for (int sum = first; sum <= last; ++sum)
    HSums.Add(sum);

  int N = sortedSet.Length;

  int jLow = 0;
  int jScanStart = sortedSet.Length - 1;

  while (HSums.Count > 0)
  {
    long xLow = sortedSet[jLow];
    long xScan = sortedSet[jScanStart];

    // decrease jScanStart to get maxSum or less.
    while (jScanStart > jLow + 1 && xScan + xLow > last)
      xScan = sortedSet[--jScanStart];

    // scan until we get below minSum.
    int jScan = jScanStart;
    while (jScan > jLow && xLow + xScan >= first)
    {
      int sum = (int)(xLow + xScan);
      if (HSums.Contains(sum))
      {
        HSums.Remove(sum);

        if (sum == last)
          while (last >= first && !HSums.Contains(last))
            last--;

        if (sum == first)
          while (first <= last && !HSums.Contains(first))
            first++;

        if (last < first)
          return sumCountOriginal;
      }

      xScan = sortedSet[--jScan];
    } // scan

    // next xLow.
    if (++jLow == N - 1 || jScanStart <= jLow)
      return sumCountOriginal - HSums.Count;

  } // there are still unresolved sums.

  // unexpected fall-thru - we should never get here.
  return sumCountOriginal - HSums.Count;
}
private int-CalcSlide(int-first、int-last、HashSet-number)
{
如果(最后一个<第一个)
返回0;
//战略:
//从下面按顺序浏览输入的数字,我们将扫描一个“适当的”滑动窗口
//在同一个集合中,看看我们是否找到了以前没有找到的金额。
//我们从最低的数字开始作为“锚”,并确定可能相关的最高数字,
//也就是说,这不会超过“最后一次”。
//将其单独保存,以便以后使用,我们向下扫描,直到求和(始终使用“锚”)到“第一”下方。
//
//我们保留要在散列中找到的总和。
//一旦我们的扫描找到一个尚未找到的和,我们就从散列中删除它,并根据需要更新first/last。
//完成此循环后,我们增加定位点,重新确定最高的相关数字以开始新的扫描,
//(此重新确定从保存的上一个值开始,向下移动),然后再次扫描。
//当明显的条件被填满时,出口就会出现:没有什么可搜索的了,我们没有什么可扫描的了。
//
//时间复杂性:
//给定一个输入哈希集,我们首先从以下内容构造一个排序数组:O(nLogn)
//如果我们观察N(输入点的数量)大得多的scnario,则随后的滑动窗口扫描程序是O(N)
//更准确地说,最坏情况的行为是O(N M logM),其中M是范围