【发布时间】:2014-01-07 17:09:59
【问题描述】:
我有一个常数列表。我需要在数字列表中找到最接近 x 的数字。关于如何实现这个算法的任何想法?
【问题讨论】:
-
不要忘记考虑不寻常的情况。 (1) 如果没有最接近的数字怎么办? (2) 如果有两个不同的最接近的数字怎么办?
我有一个常数列表。我需要在数字列表中找到最接近 x 的数字。关于如何实现这个算法的任何想法?
【问题讨论】:
嗯,你不能比O(N) 更快地做到这一点,因为你必须检查所有数字以确保你有最接近的数字。也就是说,为什么不使用一个简单的变体来寻找最小值,寻找与 x 的绝对差值最小的那个?
如果您可以说列表是从头开始排序的(并且它允许随机访问,就像数组一样),那么更好的方法是使用二进制搜索。当您在索引 i 处结束搜索时(没有找到 x),只需从该元素及其相邻元素中挑选出最好的。
【讨论】:
我认为数组是无序的。按顺序可以更快
我认为最简单和最快的方法是使用线性算法来找到最小值或最大值,但不是比较值,而是比较 this 和 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;
【讨论】:
一般来说,这个网站上的人不会为你做功课。由于您没有发布代码,因此我也不会发布代码。但是,这是一种可能的方法。
遍历列表,从 x 中减去列表中的数字。取此差值的绝对值,并将其与您获得的最佳先前结果进行比较,如果当前差值小于先前最佳结果,则保存列表中的当前数字。在循环结束时,您将得到答案。
【讨论】:
homework 标签。他不是想对你拉一个快的。
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;
}
}
【讨论】:
number descending 订购以确保选择正数(在没有行为要求的情况下,这是最合理的决定)。我将研究Min 解决方案。感谢您的反馈埃里克!
可以使用SortedList:
Blog post on finding closest number
如果您要查找的复杂性仅计算搜索,则复杂性为 O(log(n))。列表构建将花费 O(n*log(n))
如果您要向列表中插入项目的次数远多于查询最接近的数字,那么最好的选择是使用List 并使用朴素算法来查询最接近的数字.每次搜索将花费 O(n),但插入时间将减少到 O(n)。
一般复杂性:如果集合有 n 个数字并搜索 q 次 -
列表:O(n+q*n)
排序列表:O(n*log(n)+q*log(n))
意思是,从某些q来看,排序后的列表会提供更好的复杂性。
【讨论】:
懒惰我没有检查这个但不应该这个工作
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);
}
【讨论】:
哈斯克尔:
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;
}
}
【讨论】: