Подтвердить что ты не робот

поиск ближайшего значения в массиве

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

int TargetNumber = 13;

Для целевого числа я хочу найти ближайший номер в массиве. Например, когда целевое число равно 13, ближайший к нему в приведенном выше массиве равен 15. Как бы я достиг этого программным путем в С#?

4b9b3361

Ответ 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;
    }
}