在数字列表中找到最接近的数字

时间:2010-01-01 16:01:50

标签: c# algorithm

我有一个常数列表。我需要在数字列表中找到最接近x的数字。关于如何实现这个算法的任何想法?

8 个答案:

答案 0 :(得分:8)

嗯,你不能比O(N)更快地做到这一点,因为你必须检查所有数字,以确保你有最接近的数字。也就是说,为什么不使用一个简单的变量找到最小值,寻找与 x 具有最小绝对差值的变量?

如果你可以说列表是从头开始排序的(它允许随机访问,就像数组一样),那么更好的方法是使用二进制搜索。当您在索引 i 结束搜索时(没有找到 x ),只需选择该元素及其邻居中的最佳元素。

答案 1 :(得分:5)

我认为数组是无序的。按顺序,它可以更快

我认为最简单和最快的方法是使用线性算法来查找最小值或最大值,但不是比较值,而是比较它与针之间的差值的绝对值。

在C ++中(我不能用C#但它会类似)代码看起来像这样:

// array of numbers is haystack
// length is length of array
// needle is number which you are looking for ( or compare with )

int closest = haystack[0];
for ( int i = 0; i < length; ++i ) {
  if ( abs( haystack[ i ] - needle ) < abs( closest - needle ) ) closest = haystack[i];
}
return closest;

答案 2 :(得分:3)

一般来说,本网站上的人不会为您做功课。由于您没有发布代码,我也不会发布代码。但是,这是一种可能的方法。

循环列表,从x中减去列表中的数字。取这个差值的绝对值,并将其与您获得的最佳结果进行比较,如果当前差异小于之前的最佳结果,请从列表中保存当前数字。在循环结束时,你会得到答案。

答案 3 :(得分:1)

private int? FindClosest(IEnumerable<int> numbers, int x)
{
    return
        (from number in numbers
        let difference = Math.Abs(number - x)
        orderby difference, Math.Abs(number), number descending
        select (int?) number)
        .FirstOrDefault();
}

Null表示没有最接近的号码。如果有两个具有相同差异的数字,它将选择最接近零的数字。如果两个数字的距离与零相同,则将选择正数。

编辑以回应Eric的评论:

这是一个具有相同语义但使用Min运算符的版本。它需要IComparable<>的实现,因此我们可以使用Min,同时保留每个距离的数字。我还把它作为一种易于使用的扩展方法:

public static int? FindClosestTo(this IEnumerable<int> numbers, int targetNumber)
{
    var minimumDistance = numbers
        .Select(number => new NumberDistance(targetNumber, number))
        .Min();

    return minimumDistance == null ? (int?) null : minimumDistance.Number;
}

private class NumberDistance : IComparable<NumberDistance>
{
    internal NumberDistance(int targetNumber, int number)
    {
        this.Number = number;
        this.Distance = Math.Abs(targetNumber - number);
    }

    internal int Number { get; private set; }

    internal int Distance { get; private set; }

    public int CompareTo(NumberDistance other)
    {
        var comparison = this.Distance.CompareTo(other.Distance);

        if(comparison == 0)
        {
            // When they have the same distance, pick the number closest to zero
            comparison = Math.Abs(this.Number).CompareTo(Math.Abs(other.Number));

            if(comparison == 0)
            {
                // When they are the same distance from zero, pick the positive number
                comparison = this.Number.CompareTo(other.Number);
            }
        }

        return comparison;
    }
}

答案 4 :(得分:0)

可以使用SortedList完成:
Blog post on finding closest number
如果您正在寻找的复杂性只计算搜索的复杂性 O(log(n))。列表构建将花费 O(n * log(n))

如果您要将项目插入列表的次数比查询最接近的数字要多得多,那么最好的选择是使用List并使用朴素算法查询它最近的号码。每次搜索都将花费 O(n),但插入时间将缩短为 O(n)

一般复杂性:如果集合中有 n 个数字且搜索 q 次 - 列表O(n+q*n)
排序列表O(n*log(n)+q*log(n))

意思是,从某些 q 排序列表将提供更好的复杂性。

答案 5 :(得分:0)

懒惰我没有检查过这个但是不应该这样做

private int FindClosest(IEnumerable<int> numbers, int x)
{
    return
        numbers.Aggregate((r,n) => Math.Abs(r-x) > Math.Abs(n-x) ? n
                                 : Math.Abs(r-x) < Math.Abs(n-x) ? r
                                 : r < x ? n : r);
}

答案 6 :(得分:0)

Haskell中:

import Data.List (minimumBy)
import Data.Ord (comparing)

findClosest :: (Num a, Ord a) => a -> [a] -> Maybe a
findClosest _ [] = Nothing
findClosest n xs = Just $ minimumBy (comparing $ abs . (+ n)) xs

答案 7 :(得分:0)

                Performance wise custom code will be more use full. 

                List<int> results;
                int targetNumber = 0;
                int nearestValue=0;
                if (results.Any(ab => ab == targetNumber ))
                {
                    nearestValue= results.FirstOrDefault<int>(i => i == targetNumber );
                }
                else
                {
                    int greaterThanTarget = 0;
                    int lessThanTarget = 0;
                    if (results.Any(ab => ab > targetNumber ))
                    {
                        greaterThanTarget = results.Where<int>(i => i > targetNumber ).Min();
                    }
                    if (results.Any(ab => ab < targetNumber ))
                    {
                        lessThanTarget = results.Where<int>(i => i < targetNumber ).Max();
                    }

                    if (lessThanTarget == 0 )
                    {
                        nearestValue= greaterThanTarget;
                    }
                    else if (greaterThanTarget == 0)
                    {
                        nearestValue= lessThanTarget;
                    }
                    else if (targetNumber - lessThanTarget < greaterThanTarget - targetNumber )
                    {
                        nearestValue= lessThanTarget;
                    }
                    else
                    {
                            nearestValue= greaterThanTarget;
                    }
                }