简体   繁体   English

在数组中找到最接近的值

[英]finding closest value in an array

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

int TargetNumber = 13;

For a target number, I want to find the closest number in an array.对于目标数字,我想在数组中找到最接近的数字。 For example, when the target number is 13, the closest number to it in the array above is 15. How would I accomplish that programmatically in C#?例如,当目标数字是 13 时,上面数组中最接近它的数字是 15。我将如何在 C# 中以编程方式完成该操作?

EDIT: Have adjusted the queries below to convert to using long arithmetic, so that we avoid overflow issues.编辑:已调整以下查询以转换为使用long算术,以便我们避免溢出问题。

I would probably use MoreLINQ 's MinBy method:我可能会使用MoreLINQMinBy方法:

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

Or you could just use:或者你可以只使用:

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

... but that will sort the whole collection, which you really don't need. ...但这将对整个集合进行排序,而您确实不需要。 It won't make much difference for a small array, admittedly... but it just doesn't feel quite right, compared with describing what you're actually trying to do: find the element with the minimum value according to some function.诚然,对于一个数组来说,它不会有太大区别……但与描述您实际尝试做的事情相比,感觉不太正确:根据某个函数找到具有最小值的元素。

Note that both of these will fail if the array is empty, so you should check for that first.请注意,如果数组为空,这两种方法都会失败,因此您应该先检查一下。

If you're using .Net 3.5 or above LINQ can help you here:如果您使用 .Net 3.5 或更高版本,LINQ 可以在此处为您提供帮助:

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

Alternatively, you could write your own extension method:或者,您可以编写自己的扩展方法:

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

Useable like so:可以这样使用:

var closest = array.ClosestTo(targetNumber);

Both Jon and Rich gave great answers with MinBy and ClosestTo . Jon 和 Rich 都用MinByClosestTo给出了很好的答案。 But I would never recommend using OrderBy if your intent is to find a single element.但是,如果您的意图是查找单个元素,我永远不会推荐使用OrderBy It's far too inefficient for those kinds of tasks.对于这些类型的任务来说,它的效率太低了。 It's simply the wrong tool for the job.这只是工作的错误工具。

Here's a technique that performs marginally better than MinBy, is already included in the .NET framework, but less elegant than MinBy: Aggregate这是一种性能略好于 MinBy 的技术,已包含在 .NET 框架中,但不如 MinBy 优雅: Aggregate

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

As I said, not as elegant as Jon's method, but viable.正如我所说,不像 Jon 的方法那么优雅,但可行。

Performance on my computer:在我的电脑上的表现:

  1. For(each) Loops = fastest For(each) 循环 = 最快
  2. Aggregate = 2.5x slower than loops聚合 = 比循环慢 2.5 倍
  3. MinBy = 3.5x slower than loops MinBy = 比循环慢 3.5 倍
  4. OrderBy = 12x slower than loops OrderBy = 比循环慢 12 倍

I found this really sexy approach years ago in Math.NET Numerics https://numerics.mathdotnet.com/ which works with BinarySearch in the array.几年前我在 Math.NET Numerics https://numerics.mathdotnet.com/ 中发现了这种非常性感的方法,它与数组中的 BinarySearch 一起使用。 It was a good help in preparation for interpolations and works down to .Net 2.0:这对准备插值和适用于 .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);
}

Performance wise custom code will be more useful.性能明智的自定义代码将更有用。

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

If you need to find the closest value to the average如果您需要找到最接近平均值的值

very open style非常开放的风格

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM