C#在数组中查找最接近的值

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

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

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

我可能会使用MoreLINQ的MinBy方法:

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

或者您可以使用:

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

…但是这将对整个系列进行排序,这是您真正不需要的。 对于一个arrays来说,这并没有太大的区别,不可否认……但与描述你实际上要做的事情相比,它感觉不太正确:根据某些function找到具有最小值的元素。

请注意,如果数组为空,这两个都将失败,因此您应首先检查它。

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

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

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

 public static int ClosestTo(this IEnumerable 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); 

Jon和Rich都为MinByClosestTo提供了很好的答案。 但如果您的目的是找到一个元素,我绝不会建议使用OrderBy 。 对于那些类型的任务来说效率太低了。 这只是工作的错误工具。

这是一种比MinBy稍微好一点的技术,已经包含在.NET框架中,但不如MinBy优雅: Aggregate

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

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

我的电脑上的表现:

  1. 对于(每个)循环=最快
  2. Aggregate =比循环慢2.5倍
  3. MinBy =比循环慢3.5倍
  4. OrderBy =比循环慢12倍

几年前我在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); } 

性能明智的自定义代码将更有用。

 public static int FindNearest(int targetNumber, IEnumerable 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; }