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

我有一个从- 1000到+ 1000的数我有一个数组,里面都是数字。是这样的:

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

我想让我得到的数字变成数组中最接近的数字。

例如,我得到80作为数字,我想让它得到82

230928 次浏览

对于一个较小的范围,最简单的方法是有一个map数组,例如,用你的例子来说,第80个条目的值是82。对于一个更大、更稀疏的范围,可能的方法是二分搜索。

使用查询语言,您可以查询与输入数字任意一侧有一定距离的值,然后对结果减少的列表进行排序。但是SQL并没有一个“下一个”或“上一个”的好概念,来给你一个“干净”的解决方案。

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

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.

作为概念的证明,下面是我用来演示这一点的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文件,该文件演示了该函数的操作:

<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>

现在请记住,如果数据项已经排序(这可以从示例数据中推断出来,但您没有显式地说明),则可能存在提高效率的空间。例如,您可以使用二分搜索来查找最接近的项。

你还应该记住,除非你需要每秒做许多次,否则效率的提高基本上是不明显的,除非你的数据集变得更大。

如果你想尝试这种方式(并且可以保证数组是升序排序的),这是一个很好的起点:

<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>

它基本上使用夹叉射击和检查中间值来将每次迭代的解空间减少一半,这是一个经典的O(log N)算法,而上面的顺序搜索是O(N):

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

如上所述,对于小型数据集或需要不能盲目快速的东西,这应该没有太大区别,但这是你可能想要考虑的一个选项。

工作代码如下:

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;
}
console.log(closest(array, 88));

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);

我不知道我是否应该回答一个老问题,但由于这篇文章首先出现在谷歌搜索中,我希望你能原谅我添加我的解决方案&2c在这里。

由于懒惰,我无法相信这个问题的解决方案将是一个循环,所以我搜索了更多,并返回滤波函数:

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);

就这些!

对于排序数组(线性搜索)

到目前为止,所有的答案都集中在整个数组中搜索。 考虑到你的数组已经排序,你真的只想要最近的数字,这可能是最简单(但不是最快)的解决方案:

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(a, target));

请注意,该算法可以大大改进,例如使用二叉树。

我对一个类似问题的回答是考虑关系,它是在纯Javascript中,尽管它不使用二进制搜索,所以它是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 < a href = " https://stackoverflow.com/a/26429528/986160 " > < / >

ES6 (ECMAScript 2015)版本:

const counts = [4, 9, 15, 6, 2];
const goal = 5;


const output = counts.reduce((prev, curr) => Math.abs(curr - goal) < Math.abs(prev - goal) ? curr : prev);


console.log(output);

为了可重用性,可以封装一个支持占位符的curry函数(http://ramdajs.com/0.19.1/docs/#curryhttps://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 closestToFive = getClosest(_, 5);
const output = closestToFive([4, 9, 15, 6, 2]);


console.log(output);
<script src="https://cdn.jsdelivr.net/npm/lodash@4.17.20/lodash.min.js"></script>

我喜欢Fusion的方法,但其中有一个小错误。这样是正确的:

    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()测试了它,它比其他函数略快。

适用于无序数组

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

  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(80, [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]) // Outputs 82

其他方法相反,使用Math.min.apply找到最小值,此方法不需要输入数组arr排序。我们不需要关心索引或者事先排序。

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

  1. arr.map(function(k) { return Math.abs(k - x) })创建一个新数组,本质上存储给定数字(arr中的数字)减去输入数字(x)的绝对值。接下来我们将寻找最小的数字(这也是最接近输入的数字)
  2. Math.min.apply(Math, indexArr)这是在我们之前创建的数组中找到最小数字的合法方法(仅此而已)
  3. 这可能是最有趣的部分。我们已经找到了最小的数,但我们不确定是否应该加上或减去初始数(x)。这是因为我们使用Math.abs()来查找差值。然而,array.map创建输入数组的映射(逻辑上),将索引保持在相同的位置。因此,要找出最接近的数字,只需返回给定数组indexArr.indexOf(min)中最小值的下标。

我创建了一个箱子来演示它。

这个解决方案使用ES5 < em >存在量词< / em > Array#some,它允许在满足条件时停止迭代。

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

在回调函数中,将获取搜索值与实际item之间的绝对delta,并与最后的增量进行比较。如果大于或等于,迭代将停止,因为所有其他具有delta的值都大于实际值。

如果回调中的delta较小,则实际的项被分配给结果,并且delta保存在lastDelta中。

最后,取具有相等增量的较小值,如下面的22示例,结果为2

如果有更大的优先级值,delta检查必须从以下更改:

if (delta >= lastDelta) {

:

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

这将得到22,结果42(较大值的优先级)。

这个函数需要数组中排序的值。


优先级较小的代码:

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

优先级较高的代码:

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)); // 42 greater value
console.log(23, closestValue(data, 23)); // 42
console.log(80, closestValue(data, 80)); // 82

这里的另一个变体是圆形范围,从头到脚连接,只接受给定输入的最小值。这帮助我获得了一个加密算法的字符代码值。

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;
});
}

所有的解决方案都是过度设计的。

它是如此简单:

const needle = 5;
const haystack = [1, 2, 3, 4, 5, 6, 7, 8, 9];


haystack.sort((a, b) => {
return Math.abs(a - needle) - Math.abs(b - needle);
})[0];


// 5
#include <algorithm>
#include <iostream>
#include <cmath>


using namespace std;


class CompareFunctor
{


public:
CompareFunctor(int n) { _n = n; }
bool operator()(int & val1, int & val2)
{
int diff1 = abs(val1 - _n);
int diff2 = abs(val2 - _n);
return (diff1 < diff2);
}


private:
int _n;
};


int Find_Closest_Value(int nums[], int size, int n)
{
CompareFunctor cf(n);
int cn = *min_element(nums, nums + size, cf);
return cn;
}


int main()
{
int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
int size = sizeof(nums) / sizeof(int);
int n = 80;
int cn = Find_Closest_Value(nums, size, n);
cout << "\nClosest value = " << cn << endl;
cin.get();
}

ES6

适用于已排序和未排序数组

数字整数和浮点数,字符串欢迎

/**
* Finds the nearest value in an array of numbers.
* Example: nearestValue(array, 42)
*
* @param {Array<number>} arr
* @param {number} val the ideal value for which the nearest or equal should be found
*/
const nearestValue = (arr, val) => arr.reduce((p, n) => (Math.abs(p) > Math.abs(n - val) ? n - val : p), Infinity) + val


例子:

let values = [1,2,3,4,5]
console.log(nearestValue(values, 10)) // --> 5
console.log(nearestValue(values, 0)) // --> 1
console.log(nearestValue(values, 2.5)) // --> 2


values = [100,5,90,56]
console.log(nearestValue(values, 42)) // --> 56


values = ['100','5','90','56']
console.log(nearestValue(values, 42)) // --> 56


最有效的方法是二分查找。然而,即使是简单的解决方案也可以退出下一个数字是当前的进一步匹配。这里几乎所有的解决方案都没有考虑到数组是有序的,并且迭代整个:/

const closest = (orderedArray, value, valueGetter = item => item) =>
orderedArray.find((item, i) =>
i === orderedArray.length - 1 ||
Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));


var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];


console.log('21 -> 2', closest(data, 21) === 2);
console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
console.log('23 -> 42', closest(data, 23) === 42);
console.log('80 -> 82', closest(data, 80) === 82);

这也可以运行在非原语上,例如closest(data, 21, item => item.age)

find更改为findIndex以返回数组中的索引。

在数组中找到两个最接近的数字

function findTwoClosest(givenList, goal) {
var first;
var second;
var finalCollection = [givenList[0], givenList[1]];
givenList.forEach((item, firtIndex) => {
first = item;


for (let i = firtIndex + 1; i < givenList.length; i++) {
second = givenList[i];


if (first + second < goal) {
if (first + second > finalCollection[0] + finalCollection[1]) {
finalCollection = [first, second];
}
}
}
});


return finalCollection;
}


var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
var goal = 80;
console.log(findTwoClosest(counts, goal));

O(n)时间复杂度的一个更简单的方法是在数组的一次迭代中完成。此方法用于未排序的数组。

下面是一个javascript的例子,在这里我们从数组中找到最接近“58”的数字。

var inputArr = [150, 5, 200, 50, 30];
var search = 58;
var min = Math.min();
var result = 0;
for(i=0;i<inputArr.length;i++) {
let absVal = Math.abs(search - inputArr[i])
if(min > absVal) {
min=absVal;
result = inputArr[i];
}
}
console.log(result); //expected output 50 if input is 58

这也适用于积极的小数数字。

Math.min()将返回Infinity

result将存储离搜索元素最近的值。

其他答案表明你需要遍历整个数组:

  • 计算每个元素的偏差
  • 跟踪最小偏差及其元素
  • 最后,在遍历整个数组后,返回具有最小偏差的元素。

如果数组是已经排序,那么就有意义。没有必要计算所有的偏差。例如,在一个100万个元素的有序集合中,你只需要计算~19个偏差(最多)来找到你的匹配。你可以通过二分查找方法来实现:

function findClosestIndex(arr, element) {
let from = 0, until = arr.length - 1
while (true) {
const cursor = Math.floor((from + until) / 2);
if (cursor === from) {
const diff1 = element - arr[from];
const diff2 = arr[until] - element;
return diff1 <= diff2 ? from : until;
}


const found = arr[cursor];
if (found === element) return cursor;


if (found > element) {
until = cursor;
} else if (found < element) {
from = cursor;
}
}
}

结果:

console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
// output: 3


console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
// output: 4


console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
// output: 5


console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
// output: 0

如果数组像你的例子一样是排序,你可以使用二分查找来获得更好的时间复杂度O(log n)。

const myArray = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];


const binaryClosestIdx = (arr, target) => {
let start = 0;
let end = arr.length - 1;
let mid = Math.floor((start + end) / 2);


while (1) {
if (arr[mid] === target) {
return mid;
}
else if (start >= end) {
break;
}
else if (arr[mid] > target) {
end = mid - 1;
} else {
start = mid + 1;
}
        

mid = Math.floor((start + end) / 2);
}


// Return the closest between the last value checked and it's surrounding neighbors
const first = Math.max(mid - 1, 0);
const neighbors = arr.slice(first, mid + 2);
const best = neighbors.reduce((b, el) => Math.abs(el - target) < Math.abs(b - target) ? el : b);


return first + neighbors.indexOf(best);
}


const closestValue = myArray[binaryClosestIdx(myArray, 80)];
console.log(closestValue);

工作原理:

  1. 它将目标值与数组的中间元素进行比较。如果中间的元素更大,我们可以忽略它后面的每个元素,因为它们会更大。同样,如果中间的元素更小,我们可以忽略它之前的所有元素。

  2. 如果找到目标值,则返回它,否则将最后测试的值与其周围的相邻值进行比较,因为最近的值只能在这3个值之间。

你可以使用下面的逻辑找到最接近的数字,而不使用reduce函数

let arr = [0, 80, 10, 60, 20, 50, 0, 100, 80, 70, 1];
const n = 2;
let closest = -1;
let closeDiff = -1;


for (let i = 0; i < arr.length; i++) {
if (Math.abs(arr[i] - n) < closeDiff || closest === -1) {
closeDiff = Math.abs(arr[i] - n);
closest = arr[i];
}
}
console.log(closest);