在List <int>中查找具有指定差异</int>的数字

时间:2014-10-03 14:37:54

标签: c# generic-collections

对于给定的空格分隔的数字列表,计算具有N个差异的总数字对的最有效方法是什么。

e.g。 put中的命令行是:

5 2

其中5是要遵循的数字计数,2是所需的差异

1 5 3 4 2

要考虑的5个数字

输出应该是 3 因为(5,3),(4,2)和(3,1)都具有2的差异

我可以使用这个算法,但是如果你有大量的数字可以使用,那么有没有更有效的方法呢?我已经包含了三个比较选项,第二个应该比第三个更好,但是有什么我忘记了哪些可以让它更快?

private static void Difference()
{
    string[] firstInput = SplitInput(Console.ReadLine());

    int numberOfNumbers = int.Parse(firstInput[0]);
    int diffOfNumbers = int.Parse(firstInput[1]);

    string[] secondInput = SplitInput(Console.ReadLine());

    List<int> numbers = secondInput.Select(x => Int32.Parse(x)).ToList();

    int possibleCombinations = 0;

    // Option 1
    foreach (int firstNumber in numbers)
    {
        List<int> compareTo = numbers.GetRange(numbers.IndexOf(firstNumber) + 1, numbers.Count - numbers.IndexOf(firstNumber) - 1);

        foreach (int secondNumber in compareTo)
        {
            int diff = firstNumber - secondNumber;

            if (Math.Abs(diff) == diffOfNumbers)
            {
                possibleCombinations++;
            }
        }
    }

    // Option 2
    foreach (int firstNumber in numbers)
    {
        if (numbers.Contains(firstNumber + diffOfNumbers))
        {
                possibleCombinations++;
        }
    }

    // Option 3
    foreach (int firstNumber in numbers)
    {
        foreach (int secondNumber in numbers)
        {
            int diff = firstNumber - secondNumber;

            if(Math.Abs(diff) == diffOfNumbers)
            {
                possibleOptions++;
            }
        }
    }

    Console.WriteLine(string.Format("Possible number of options are: {0}", possibleCombinations));
    Console.ReadLine();
}

private static string[] SplitInput(string input)
{
    return input.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
}

4 个答案:

答案 0 :(得分:4)

如果不允许或忽略重复的数字(仅计算唯一对),您可以使用HashSet<int>

HashSet<int> myHashSet = ...
int difference = ...
int count;
foreach (int number in myHashSet)
{
    int counterpart = number - difference;
    if (myHashSet.Contains(counterpart))
    {
        count++;
    }
}

答案 1 :(得分:2)

考虑到问题的限制,其中N是&#34;要跟随的数字的数量&#34; [1..N],M是差值(示例中N = 5,M = 2),为什么不返回N-M?

答案 2 :(得分:1)

使用LINQ可以轻松完成,允许重复:

var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
return dict.Keys.Where(n => dict.ContainsKey(difference-n)).Select(n => dict[difference - n]).Sum();

在第一行中,我们创建了一个字典,其中键是输入列表中的不同数字(numbers),值是它们出现的次数。

在第二个中,对于列表中的每个不同的数字(相当于文本的键),我们查看字典是否包含目标号码的键。如果是这样,我们将添加目标编号出现的次数,我们之前将其存储为该密钥的值。如果不是,我们加0.最后我们总结一下。


理论上注意,如果列表中的项目没有Int.MinValue和Int.MaxValue以外的界限,则可能导致算术溢出。为了解决这个问题,我们需要做一个安全的&#34;检查,首先确保差异在我们尝试计算之前不会超出范围。这可能看起来像:

int SafeGetCount(int difference, int number, Dictionary<int,int> dict)
{
    if(difference < 0 && number < 0 && int.MinValue - difference > number)
        return 0;
    if(difference > 0 && number > 0 && int.MaxValue - difference < number)
        return 0;
    return dict.ContainsKey(difference-number) ? dict[difference - number] : 0;
}

更新

有一些事情从你的问题中完全清楚,比如你是否真的想要多次计算重复对,并且将数字交换计为两个不同的对。例如如果(1,4)是一对,是(4,1)?我上面的回答假设这两个问题的答案都是肯定的。

如果您不想多次计算重复对,请使用其他答案中的HashSet解决方案。如果您确实想要计算重复对,但不想通过交换对中的值来计算两次,则必须稍微复杂一些。 E.g:

var dict = numbers.GroupBy(n => n).ToDictionary(g => g.Key, g => g.Count());
var sum = dict.Keys.Where(n => n*2 != difference)
      .Where(n => dict.ContainsKey(difference-n))
      .Select(n => dict[difference - n]).Sum()/2;
if(n%2 == 0)
{
    sum += dict.ContainsKey(n/2) ? dict[n/2] : 0
}    
return sum;

答案 3 :(得分:0)

如何排序列表然后迭代它。

int PairsWithMatchingDifferenceCount(
        IEnumerable<int> source,
        int difference)
{
    var ordered = source.OrderBy(i => i).ToList();
    var count = ordered.Count;
    var result = 0;
    for (var i = 0; i < count - 1; i++)
    {
        for (var j = i + 1; j < count; j++)
        {
            var d = Math.Abs(ordered[j] - ordered[i]);
            if (d == difference)
            {
                result++;
            }
            else if (d > difference)
            {
                break;
            }
        } 
    }

    return result;
}

所以,根据例子你可以这样称呼它,

PairsWithMatchingDifferenceCount(Enumerable.Range(1, 5), 2);

但是,如果序列生成很简单,问题就是为什么不仅仅是。

var m = 5;
var n = 2;

var result = Enumerable.Range(n + 1, m - n)
                 .Select(x => Tuple.Create(x, x - n)).Count();

或者确实,

var result = m - n;