C# 变量优化的二和算法
我需要计算区间[-1000010000](包括在内)中目标值t的数量,以便在输入文件中存在满足x+y=t的不同数字x,y。我有代码的工作,并希望优化它运行得更快 此外,我们将只计算一次总和t。如果有两个x,y对相加到55,则只会增加一次结果。输入数字可以是正整数和负整数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
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是范围