У меня есть список постоянных чисел. Я должен найти самое близкое число к x в списке чисел. Какие-либо идеи о том, как реализовать этот алгоритм?
Ну, вы не можете сделать это быстрее, чем O(N)
, потому что вы должны проверить все числа, чтобы убедиться, что у вас есть ближайший. Тем не менее, почему бы не использовать простую вариацию по нахождению минимума, и не искать тот, который имеет минимальную абсолютную разницу с x?
Если вы можете сказать, что список упорядочен с самого начала (и он позволяет случайный доступ, как массив), то лучшим подходом будет использование бинарного поиска. Когда вы завершаете поиск по индексу i (не найдя x), просто выберете лучший из этого элемента и его соседей.
.Я полагаю, что массив неупорядочен. В упорядоченном виде он может быть быстрее
Я думаю, что самым простым и быстрым методом является использование линейного алгоритма нахождения минимума или максимума, но вместо сравнения значений вы будете сравнивать абсолютное значение разницы между этим и иглой.
На языке Си++ ( я не могу на Си#, но он будет похож ) код может выглядеть так:
// array of numbers is haystack
// length is length of array
// needle is number which you are looking for ( or compare with )
int closest = haystack[0];
for ( int i = 0; i < length; ++i ) {
if ( abs( haystack[ i ] - needle ) < abs( closest - needle ) ) closest = haystack[i];
}
return closest;
В общем люди на этом сайте не будут делать за вас домашнее задание. Так как вы не написали код, я тоже не буду писать код. Однако, вот один из возможных подходов.
Просмотрите список, вычитая число в списке из x. Возьмите абсолютное значение этой разницы и сравните его с лучшим полученным вами предыдущим результатом, а если текущая разница меньше лучшего предыдущего результата, сохраните текущее число из списка. В конце цикла вы получите ответ
. Это можно сделать с помощью SortedList:
Пост в блоге о нахождении ближайшего номера
Если искомая сложность имеет значение только для поиска, то сложность равна O(log(n)). Построение списка будет стоить O(n*log(n))
Если вы собираетесь вставить элемент в список намного больше раз, чем запросить для ближайшего номера, то лучший выбор - это использовать List и использовать наивный алгоритм для запроса ближайшего номера. Каждый поиск будет стоить O(n), но время для вставки сократится до O(n).
Общая сложность: Если коллекция имеет n номеров и искала q раз -
List: O(n+q*n)
Sorted List: O(n*log(n)+q*log(n))
Значит, из некоторого q отсортированный список обеспечит большую сложность.
.Хаскелл:
import Data.List (minimumBy)
import Data.Ord (comparing)
findClosest :: (Num a, Ord a) => a -> [a] -> Maybe a
findClosest _ [] = Nothing
findClosest n xs = Just $ minimumBy (comparing $ abs . (+ n)) xs
private int? FindClosest(IEnumerable<int> numbers, int x)
{
return
(from number in numbers
let difference = Math.Abs(number - x)
orderby difference, Math.Abs(number), number descending
select (int?) number)
.FirstOrDefault();
}
Нуль означает, что ближайшего числа не было. Если есть два числа с одинаковой разницей, то он выберет ближайшее к нулю. Если два числа находятся на одинаковом расстоянии от нуля, то будет выбрано положительное число.
Редактирование в ответ на комментарий Эрика:
Вот вариант, который имеет одинаковую семантику, но использует оператор Min
. Он требует реализации IComparable<>
, чтобы можно было использовать Min
, сохраняя при этом число, которое идет с каждого расстояния. Я также сделал его методом расширения для простоты использования:
public static int? FindClosestTo(this IEnumerable<int> numbers, int targetNumber)
{
var minimumDistance = numbers
.Select(number => new NumberDistance(targetNumber, number))
.Min();
return minimumDistance == null ? (int?) null : minimumDistance.Number;
}
private class NumberDistance : IComparable<NumberDistance>
{
internal NumberDistance(int targetNumber, int number)
{
this.Number = number;
this.Distance = Math.Abs(targetNumber - number);
}
internal int Number { get; private set; }
internal int Distance { get; private set; }
public int CompareTo(NumberDistance other)
{
var comparison = this.Distance.CompareTo(other.Distance);
if(comparison == 0)
{
// When they have the same distance, pick the number closest to zero
comparison = Math.Abs(this.Number).CompareTo(Math.Abs(other.Number));
if(comparison == 0)
{
// When they are the same distance from zero, pick the positive number
comparison = this.Number.CompareTo(other.Number);
}
}
return comparison;
}
}
Быть ленивым, я не проверяет это, но не должен эту работу
private int FindClosest(IEnumerable<int> numbers, int x)
{
return
numbers.Aggregate((r,n) => Math.Abs(r-x) > Math.Abs(n-x) ? n
: Math.Abs(r-x) < Math.Abs(n-x) ? r
: r < x ? n : r);
}