37
int[] array = new int[5]{5,7,8,15,20};

int TargetNumber = 13;

对于目标数字,我想在数组中找到最接近的数字。例如,当目标数字是 13 时,上面数组中最接近它的数字是 15。我将如何在 C# 中以编程方式完成该操作?

4

6 回答 6

71

编辑:已调整以下查询以转换为使用long算术,以避免溢出问题。

我可能会使用MoreLINQMinBy方法:

var nearest = array.MinBy(x => Math.Abs((long) x - targetNumber));

或者你可以只使用:

var nearest = array.OrderBy(x => Math.Abs((long) x - targetNumber)).First();

...但这将对整个集合进行排序,而您确实不需要。诚然,对于一个小数组来说,这不会有太大的不同......但与描述你实际尝试做的事情相比,它感觉不太正确:根据某个函数找到具有最小值的元素。

请注意,如果数组为空,这两种方法都会失败,因此您应该先检查一下。

于 2012-04-12T09:32:37.217 回答
31

如果您使用的是 .Net 3.5 或更高版本,LINQ 可以在这里为您提供帮助:

var closest = array.OrderBy(v => Math.Abs((long)v - targetNumber)).First();

或者,您可以编写自己的扩展方法:

public static int ClosestTo(this IEnumerable<int> collection, int target)
{
    // NB Method will return int.MaxValue for a sequence containing no elements.
    // Apply any defensive coding here as necessary.
    var closest = int.MaxValue;
    var minDifference = int.MaxValue;
    foreach (var element in collection)
    {
        var difference = Math.Abs((long)element - target);
        if (minDifference > difference)
        {
            minDifference = (int)difference;
            closest = element;
        }
    }

    return closest;
}

可以这样使用:

var closest = array.ClosestTo(targetNumber);
于 2012-04-12T09:32:46.110 回答
21

Jon 和 Rich 都用MinBy和给出了很好的答案ClosestToOrderBy但是,如果您的意图是找到单个元素,我永远不会推荐使用。对于这类任务来说,效率太低了。这只是工作的错误工具。

这是一种性能略优于 MinBy 的技术,已包含在 .NET 框架中,但不如 MinBy 优雅:Aggregate

var nearest = array.Aggregate((current, next) => Math.Abs((long)current - targetNumber) < Math.Abs((long)next - targetNumber) ? current : next);

正如我所说,不像乔恩的方法那样优雅,但可行。

在我的电脑上的表现:

  1. For(each) 循环 = 最快
  2. 聚合 = 比循环慢 2.5 倍
  3. MinBy = 比循环慢 3.5 倍
  4. OrderBy = 比循环慢 12 倍
于 2012-12-15T22:21:38.450 回答
4

几年前,我在 Math.NET Numerics https://numerics.mathdotnet.com/中发现了这种非常性感的方法,它与数组中的 BinarySearch 一起使用。这对准备插值和工作到 .Net 2.0 很有帮助:

public static int LeftSegmentIndex(double[] array, double t)
{
    int index = Array.BinarySearch(array, t);
    if (index < 0)
    {
        index = ~index - 1;
    }
    return Math.Min(Math.Max(index, 0), array.Length - 2);
}
于 2017-08-08T15:00:54.280 回答
0

如果您需要找到最接近平均值的值

非常开放的风格

public static double Miidi(double[] list)
{
    bool isEmpty = !list.Any();
    if (isEmpty)
    {
        return 0;
    }
    else
    {
        double avg = list.Average();
        double closest = 100;
        double shortest = 100;
        {
            for ( int i = 0; i < list.Length; i++)
            {
                double lgth = list[i] - avg;
                if (lgth < 0)
                {
                    lgth = lgth - (2 * lgth);
                }
                else
                    lgth = list[i] - avg;

                if (lgth < shortest)
                {
                    shortest = lgth;
                    closest = list[i];
                }
            }
        }

        return closest;
    }
}
于 2019-02-23T21:37:09.897 回答
-1

性能方面的自定义代码将更有用。

public static int FindNearest(int targetNumber, IEnumerable<int> collection) {
    var results = collection.ToArray();
    int nearestValue;
    if (results.Any(ab => ab == targetNumber))
        nearestValue = results.FirstOrDefault(i => i == targetNumber);
    else{
        int greaterThanTarget = 0;
        int lessThanTarget = 0;
        if (results.Any(ab => ab > targetNumber)) {
            greaterThanTarget = results.Where(i => i > targetNumber).Min();
        }
        if (results.Any(ab => ab < targetNumber)) {
            lessThanTarget = results.Where(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;
        }
    }
    return nearestValue;
}
于 2014-01-07T07:38:51.423 回答