我需要在区间[-10000,10000](包括)中计算目标值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;
}
这是一项任务,我用满分完成了它。我的代码花费了30分钟来对抗100万个数字的数据集。我试图想出一种优化我的代码的方法,但无法找到正确的想法,并希望得到一些帮助。
答案 0 :(得分:2)
有一种经典的线性时间算法,用于在排序数组A
中查找两个数字,这些数字总和到指定目标。将i
初始化为第一个索引,将j
初始化为最后一个索引。如果A[i] + A[j]
小于目标,请增加i
。如果它小于目标,则减少j
。在i >= j
时停止。
使用比较排序对数组进行排序渐远地比散列更昂贵,但是此设置成本可以忽略不计,并且阵列的改进内存布局将显着提高性能。
为了进一步优化,你可以反转循环结构,并且对于数组中的每个数字,找出它所组成的组合。
为了进一步优化(至少渐近),使用傅里叶变换将阵列的(部分)相互卷积。由于你的数字不同,我不相信这会在实践中有所改善。
答案 1 :(得分:1)
请查看以下解决方案:
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();
}
答案 2 :(得分:0)
通过使用更好的数据结构,可以对此代码进行大量优化。使用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;
}
请注意,此代码可能无法编译,因为我已经很久没有使用过c#了。您甚至可以通过忽略满足number.min() + n > finish
或number.max() + n < start
的值来进一步优化此代码。
答案 3 :(得分:0)
获得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;
}
编辑甚至更好:
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;
}
答案 4 :(得分:0)
我认为它基本上可以在O(NlogN)中完成,其中N是构造总和的点的数量,并且假设M,相比之下,求和的范围很小。
print row[3]