поиск ближайшего значения в массиве
int[] array = new int[5]{5,7,8,15,20};
int TargetNumber = 13;
Для целевого числа я хочу найти ближайший номер в массиве. Например, когда целевое число равно 13, ближайший к нему в приведенном выше массиве равен 15. Как бы я достиг этого программным путем в С#?
Ответы
Ответ 1
РЕДАКТИРОВАТЬ: скорректировать приведенные ниже запросы для преобразования в long
арифметику, чтобы избежать проблем с переполнением.
Я бы, вероятно, использовал метод MoreLINQ MinBy
:
var nearest = array.MinBy(x => Math.Abs((long) x - targetNumber));
Или вы можете просто использовать:
var nearest = array.OrderBy(x => Math.Abs((long) x - targetNumber)).First();
... но это отсортирует всю коллекцию, которая вам действительно не нужна. По общему признанию, это не будет иметь большого значения для небольшого массива... но это просто не совсем правильно по сравнению с описанием того, что вы на самом деле пытаетесь сделать: найти элемент с минимальным значением в соответствии с какой-то функцией.
Обратите внимание, что оба из них потерпят неудачу, если массив пуст, поэтому вам следует сначала проверить это.
Ответ 2
Если вы используете .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);
Ответ 3
Оба Jon and Rich дали отличные ответы MinBy
и ClosestTo
. Но я бы никогда не рекомендовал использовать OrderBy
, если вы намерены найти один элемент. Это слишком неэффективно для таких задач. Это просто неправильный инструмент для работы.
Здесь метод, который работает чуть лучше MinBy, уже включен в .NET framework, но менее изящный, чем MinBy: Aggregate
var nearest = array.Aggregate((current, next) => Math.Abs((long)current - targetNumber) < Math.Abs((long)next - targetNumber) ? current : next);
Как я уже сказал, не такой элегантный, как метод Джона, но жизнеспособный.
Производительность на моем компьютере:
- Для (каждый) Loops = fastest
- Агрегат = 2,5 раза медленнее, чем циклы
- MinBy = 3.5x медленнее, чем циклы
- OrderBy = 12x медленнее, чем циклы
Ответ 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);
}
Ответ 5
Эффективный пользовательский код будет более полезен.
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;
}
Ответ 6
Если вам нужно найти наиболее близкое значение к среднему
очень открытый стиль
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;
}
}