从数组中获得最接近的数字

我有一个从零下1000到1000的数字,我有一个数组在其中。 喜欢这个:

[2, 42, 82, 122, 162, 202, 242, 282, 322, 362] 

我想要的数字我已经改变到最接近的数组数量。

例如,我得到80 ,我想要它得到82

下面是可以转换成任何过程语言的伪代码:

 array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362] number = 112 print closest (number, array) def closest (num, arr): curr = arr[0] foreach val in arr: if abs (num - val) < abs (num - curr): curr = val return curr 

它只是计算给定数字和每个数组元素之间的绝对差异,并给你一个最小的差异。

对于示例值:

 number = 112 112 112 112 112 112 112 112 112 112 array = 2 42 82 122 162 202 242 282 322 362 diff = 110 70 30 10 50 90 130 170 210 250 | +-- one with minimal absolute difference. 

作为一个概念certificate,下面是我用来展示这个实践的Python代码:

 def closest (num, arr): curr = arr[0] for index in range (len (arr)): if abs (num - arr[index]) < abs (num - curr): curr = arr[index] return curr array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362] number = 112 print closest (number, array) 

而且,如果您确实需要使用Javascript,请参阅下面的完整HTML文件,其中演示了该function的实际操作:

 <html> <head></head> <body> <script language="javascript"> function closest (num, arr) { var curr = arr[0]; var diff = Math.abs (num - curr); for (var val = 0; val < arr.length; val++) { var newdiff = Math.abs (num - arr[val]); if (newdiff < diff) { diff = newdiff; curr = arr[val]; } } return curr; } array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; number = 112; alert (closest (number, array)); </script> </body> </html> 

现在请记住,例如,如果您的数据项目已sorting(可以从样本数据中推断出来,但没有明确说明),那么可能会有提高效率的空间。 例如,您可以使用二进制search来查找最近的项目。

您还应该记住,除非您每秒钟需要多次执行这些操作,否则效率提升将大部分是不可知的,除非您的数据集变得更大。

如果你确实想这样做(并且可以保证数组按升序sorting),这是一个很好的起点:

 <html> <head></head> <body> <script language="javascript"> function closest (num, arr) { var mid; var lo = 0; var hi = arr.length - 1; while (hi - lo > 1) { mid = Math.floor ((lo + hi) / 2); if (arr[mid] < num) { lo = mid; } else { hi = mid; } } if (num - arr[lo] <= arr[hi] - num) { return arr[lo]; } return arr[hi]; } array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; number = 112; alert (closest (number, array)); </script> </body> </html> 

它基本上使用括号和检查中间值来减less每次迭代的解空间一半,一个典型的O(log N)algorithm,而上面的顺序search是O(N)

 0 1 2 3 4 5 6 7 8 9 <- indexes 2 42 82 122 162 202 242 282 322 362 <- values LMHL=0, H=9, M=4, 162 higher, H<-M LMHL=0, H=4, M=2, 82 lower/equal, L<-M LMHL=2, H=4, M=3, 122 higher, H<-M LHL=2, H=3, difference of 1 so exit ^ | H (122-112=10) is closer than L (112-82=30) so choose H 

如上所述,对于小数据集或者不需要太快的事情,这不应该有太大的区别,但是这是一个您可能要考虑的选项。

ES5版本:

 var counts = [4, 9, 15, 6, 2], goal = 5; var closest = counts.reduce(function(prev, curr) { return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); }); console.log(closest); 

ES6(2015)版本:

 const counts = [4, 9, 15, 6, 2]; const goal = 5; counts .reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); 

对于可重用性,您可以包装在一个支持占位符的咖喱函数中( http://ramdajs.com/0.19.1/docs/#curry或https://lodash.com/docs#curry )。 这取决于你所需要的具有很大的灵活性:

 const getClosest = curry((counts, goal) => { return counts .reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); }); const closestTo5 = getClosest(_, 5); const closestTo = getClosest([4, 9, 15, 6, 2]); 

工作代码如下:

 var array = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; function closest(array,num){ var i=0; var minDiff=1000; var ans; for(i in array){ var m=Math.abs(num-array[i]); if(m<minDiff){ minDiff=m; ans=array[i]; } } return ans; } alert(closest(array,88)); 

对于sorting的数组(线性search)

到目前为止所有的答案都集中在search整个数组。 考虑到你的数组已经sorting,你真的只想要最接近的数字这可能是最快的解决scheme:

 var a = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; var target = 90000; /** * Returns the closest number from a sorted array. **/ function closest(arr, target) { if (!(arr) || arr.length == 0) return null; if (arr.length == 1) return arr[0]; for (var i=1; i<arr.length; i++) { // As soon as a number bigger than target is found, return the previous or current // number depending on which has smaller difference to the target. if (arr[i] > target) { var p = arr[i-1]; var c = arr[i] return Math.abs( p-target ) < Math.abs( c-target ) ? p : c; } } // No number in array is bigger so return the last. return arr[arr.length-1]; } // Trying it out console.log( closest(arr, target) ); 

请注意,该algorithm可以大大改善,例如使用二叉树。

适用于未sorting的数组

虽然在这里发布了一些很好的解决scheme,但JavaScript是一种灵活的语言,它为我们提供了以多种不同方式解决问题的工具。 当然,这一切都归结于你的风格。 如果你的代码更实用,你会发现合适的减less变化 ,即:

  arr.reduce(function (prev, curr) { return (Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev); }); 

然而,有些人可能会发现很难阅读,这取决于他们的编码风格。 所以我提出了一个解决问题的新方法:

  var findClosest = function (x, arr) { var indexArr = arr.map(function(k) { return Math.abs(k - x) }) var min = Math.min.apply(Math, indexArr) return arr[indexArr.indexOf(min)] } findClosest([2, 42, 82, 122, 162, 202, 242, 282, 322, 362], 80) // Outputs 82 

与使用Math.min.applyfind最小值的其他方法相反, 这不需要对input数组arr进行sorting 我们不需要关心索引或预先分类。

为了清晰起见,我将逐行解释代码:

  1. arr.map(function(k) { return Math.abs(k - x) })创build一个新的数组,基本上存储给定数字的绝对值(以arr )减去input数字( x )。 我们将寻找最小的数字(这也是最接近input数字)
  2. Math.min.apply(Math, indexArr)这是在我们以前创build的数组中find最小数字的合法方法(没有更多的)
  3. arr[indexArr.indexOf(min)]这也许是最有趣的部分。 我们find了最小的数字,但我们不确定是否应该加上或减去初始数字( x )。 这是因为我们使用Math.abs()来找出差异。 然而, array.map创build(逻辑)input数组的地图,保持索引在同一个地方。 因此,要找出最接近的数字,我们只返回给定数组indexArr.indexOf(min)find的最小值的索引。

我已经创build了一个展示它的bin 。

这是一个ES5 存在量词 Array#some的提议,如果条件满足,它允许停止迭代。

Array#reduce ,它不需要迭代一个结果的所有元素。

在callback内部,search值与实际item之间的绝对delta将与最后一个差值进行比较。 如果大于或等于,则迭代停止,因为所有其他具有delta值的值都大于实际值。

如果callback中的delta较小,则将实际项目分配给结果, delta将保存到das lastDelta

在结果中,采用等于delta的较小值,如下面的22中的例子所示,结果为2

如果有一个优先级较高的值,则增量检查必须从中更改

 if (delta >= lastDelta) { 

 if (delta > lastDelta) { // ^^^ without equal sign 

这会得到22 ,结果42 (优先级更高的值)。

这个函数需要数组中的sorting值。


具有较小值的优先级的代码:

 function closestValue(array, value) { var result, lastDelta; array.some(function (item) { var delta = Math.abs(value - item); if (delta >= lastDelta) { return true; } result = item; lastDelta = delta; }); return result; } var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; console.log(21, closestValue(data, 21)); // 2 console.log(22, closestValue(data, 22)); // 2 smaller value console.log(23, closestValue(data, 23)); // 42 console.log(80, closestValue(data, 80)); // 82 

我对类似问题的回答也是关系式的,它使用的是简单的Javascript,尽pipe它不使用二进制search,所以它是O(N)而不是O(logN):

 var searchArray= [0, 30, 60, 90]; var element= 33; function findClosest(array,elem){ var minDelta = null; var minIndex = null; for (var i = 0 ; i<array.length; i++){ var delta = Math.abs(array[i]-elem); if (minDelta == null || delta < minDelta){ minDelta = delta; minIndex = i; } //if it is a tie return an array of both values else if (delta == minDelta) { return [array[minIndex],array[i]]; }//if it has already found the closest value else { return array[i-1]; } } return array[minIndex]; } var closest = findClosest(searchArray,element); 

https://stackoverflow.com/a/26429528/986160

我不知道我是否应该回答一个老问题,但是由于这篇文章首先出现在Googlesearch上,我希望你原谅我在这里添加我的解决scheme和我的2c。

懒惰,我不能相信这个问题的解决scheme是一个循环,所以我search了一下,回来了过滤function :

 var myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]; var myValue = 80; function BiggerThan(inArray) { return inArray > myValue; } var arrBiggerElements = myArray.filter(BiggerThan); var nextElement = Math.min.apply(null, arrBiggerElements); alert(nextElement); 

就这样 !

我喜欢融合的方法,但是它有一个小错误。 像这样是正确的:

  function closest(array, number) { var num = 0; for (var i = array.length - 1; i >= 0; i--) { if(Math.abs(number - array[i]) < Math.abs(number - array[num])){ num = i; } } return array[num]; } 

它也快一点,因为它使用改进的for循环。

最后我写了这样的函数:

  var getClosest = function(number, array) { var current = array[0]; var difference = Math.abs(number - current); var index = array.length; while (index--) { var newDifference = Math.abs(number - array[index]); if (newDifference < difference) { difference = newDifference; current = array[index]; } } return current; }; 

我用console.time()testing它,它比其他函数稍微快一点。

对数组稍作修改的二进制search将起作用。

对于一个小范围,最简单的事情是有一个地图数组,例如,在第80个条目中将有值82,以使用您的示例。 对于一个更大,稀疏的范围,可能要走的路是一个二分查找。

使用查询语言,您可以查询input数字任意一侧的值,然后对结果缩小的列表进行sorting。 但SQL没有“下一个”或“之前”的好概念,给你一个“干净”的解决scheme。

另一个变体是这里我们有连接头到脚趾的圆形范围,只接受给定input的最小值。 这帮助我获得了一个encryptionalgorithm的char代码值。

 function closestNumberInCircularRange(codes, charCode) { return codes.reduce((p_code, c_code)=>{ if(((Math.abs(p_code-charCode) > Math.abs(c_code-charCode)) || p_code > charCode) && c_code < charCode){ return c_code; }else if(p_code < charCode){ return p_code; }else if(p_code > charCode && c_code > charCode){ return Math.max.apply(Math, [p_code, c_code]); } return p_code; }); }