【问题标题】:Find the closest number in a list of numbers在数字列表中查找最接近的数字
【发布时间】:2014-01-07 17:09:59
【问题描述】:

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

【问题讨论】:

  • 不要忘记考虑不寻常的情况。 (1) 如果没有最接近的数字怎么办? (2) 如果有两个不同的最接近的数字怎么办?

标签: c# algorithm


【解决方案1】:

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

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

【讨论】:

  • 如果您的搜索空间已排序,您可以击败 O(N)。
  • 是的,使用二叉搜索树逻辑应该可以让您在 O(log n) 内完成。
  • 或者插值搜索,O(log log n) average
  • 也许我遗漏了一些东西,但你不能只列出绝对数字 abs(yourNum - needle),将其弹出到数组中,然后排序吗?第一个索引应该是最接近的?
  • @mardala 这是 O(N log N) 因为排序。
【解决方案2】:

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

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

在 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;

【讨论】:

  • “needle”是我不知道的一些特殊命名法吗?
  • “大海捞针”:D
  • “needle”和“haystack”是搜索算法常用的表达式。
  • 虽然我们经常使用“大海捞针”这样的表达方式,但我们并不指望能找到针!
  • @R.MartinhoFernandes 成就了我的一天 :)
【解决方案3】:

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

遍历列表,从 x 中减去列表中的数字。取此差值的绝对值,并将其与您获得的最佳先前结果进行比较,如果当前差值小于先前最佳结果,则保存列表中的当前数字。在循环结束时,您将得到答案。

【讨论】:

  • OP 确实使用了homework 标签。他不是想对你拉一个快的。
【解决方案4】:
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&lt;&gt;,因此我们可以使用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;
    }
}

【讨论】:

  • LINQ 的迷人用法。但是,我注意到这是时间上的 O(n lg n) 和额外空间中的 O(n)。如果使用 Min 序列运算符而不是排序,则可以在 O(n) 时间和 O(1) 空间内完成。
  • 另外,您还没有完全消除问题陈述的歧义。如果有两个相同的数字并且它们都同样接近于零怎么办?
  • 好点。我按number descending 订购以确保选择正数(在没有行为要求的情况下,这是最合理的决定)。我将研究Min 解决方案。感谢您的反馈埃里克!
  • 整洁。小错误: numbers.Select(number => new NumberDistance(x, number) 应该是 numbers.Select(number => new NumberDistance(targetNumber, number) 不应该吗?
  • @JohnKaster:你是对的。随意编辑,否则我会的。
【解决方案5】:

可以使用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来看,排序后的列表会提供更好的复杂性。

【讨论】:

    【解决方案6】:

    懒惰我没有检查这个但不应该这个工作

    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);
    }
    

    【讨论】:

      【解决方案7】:

      哈斯克尔:

      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
      

      【讨论】:

        【解决方案8】:
                        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;
                            }
                        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-02
          • 1970-01-01
          • 2015-04-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多