我有一组常数数字,需要在这些数字中找到最接近x的数字。你们有什么实现该算法的想法吗?
好的,你不能比 O(N)
更快地完成这个任务,因为你必须检查所有数字以确保你找到了最接近的一个。话虽如此,为什么不使用一种简单的变体来寻找最小值,即寻找与 x 的绝对差最小的那个数呢?
如果你能够确认列表从一开始就是有序的(并且它允许随机访问,比如数组),那么更好的方法是使用二分查找。当你在索引 i 处结束搜索(没有找到 x)时,只需从该元素及其相邻元素中选择最佳的一个。
我认为数组是无序的。如果有序,它会更快。
我认为最简单和最快的方法是使用线性算法查找最小或最大值,但是与其比较值,您将比较此和 needle 之间差的绝对值。
在 C++ 中(我不会 C#,但类似),代码可能如下所示:
// 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;
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();
}
Null表示没有最接近的数字。如果有两个差距相同的数字,它将选择最靠近零的数字。如果两个数字距离零相同,则选择正数。
根据Eric的评论进行编辑:
这里有一个具有相同语义但使用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);
}
Haskell:
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
Performance wise custom code will be more use full.
List<int> results;
int targetNumber = 0;
int nearestValue=0;
if (results.Any(ab => ab == targetNumber ))
{
nearestValue= results.FirstOrDefault<int>(i => i == targetNumber );
}
else
{
int greaterThanTarget = 0;
int lessThanTarget = 0;
if (results.Any(ab => ab > targetNumber ))
{
greaterThanTarget = results.Where<int>(i => i > targetNumber ).Min();
}
if (results.Any(ab => ab < targetNumber ))
{
lessThanTarget = results.Where<int>(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;
}
}
可以使用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))