两和算法变量 - 优化

时间:2015-08-19 22:26:30

标签: c# algorithm

我需要在区间[-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万个数字的数据集。我试图想出一种优化我的代码的方法,但无法找到正确的想法,并希望得到一些帮助。

5 个答案:

答案 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 > finishnumber.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]