我有一个从负1000到正1000的数字,还有一个包含数字的数组。就像这样:
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
我希望获得的数字能够变为数组中最接近它的数字。
例如,如果我输入数字80
,我希望它变成82
。
我有一个从负1000到正1000的数字,还有一个包含数字的数组。就像这样:
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
我希望获得的数字能够变为数组中最接近它的数字。
例如,如果我输入数字80
,我希望它变成82
。
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);
以下是伪代码,可以转换成任何过程式语言:
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>
它基本上使用二分法搜索(bracketing)和检查中间值来每次减半解决方案空间,这是一个经典的 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
就像之前说的一样,这对于小数据集或不需要快速运行的任务来说没有太大影响,但这是您可能想考虑的一种选择。
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);
为了实现可重用性,您可以使用支持占位符的柯里化函数进行封装(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 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>
以下是可工作的代码:
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));
[1, 2, 3]
中寻找最接近 5000
的数字,那么你会大吃一惊。 - paxdiablo尽管这里发布了一些好的解决方案,但JavaScript是一种灵活的语言,它为我们提供了许多不同的解决问题的工具。
当然,这完全取决于您的编码风格。如果您的代码更加函数化,您会发现reduce方法很适合,例如:
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
是已排序的。我们不需要关心索引或者事先进行排序。
我会逐行解释代码以便更清晰明了:
arr.map(function(k) { return Math.abs(k - x) })
创建一个新的数组,实际上存储了给定数字(在arr
中的数字)减去输入数字(x
)的绝对值。我们接下来将寻找最小的数字(也是最接近输入数字的数字)。Math.min.apply(Math, indexArr)
这是一种合法的方法来查找我们刚创建的数组中最小的数字(没有更多含义)。arr[indexArr.indexOf(min)]
这可能是最有趣的部分。我们已经找到了最小的数字,但我们不确定是否应该添加或减去初始数字(x
)。那是因为我们使用Math.abs()
来查找差异。然而,array.map
创建了一个输入数组的映射,保持索引在同一位置。因此,为了找到最接近的数字,我们只需返回找到的最小值在给定数组中的索引indexArr.indexOf(min)
。const findClosest = goal => (a,b) => Math.abs(a - goal) < Math.abs(b - goal) ? a : b;
- Jakob E[2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
- Jakob E所有的解决方案都被过度设计了。
实际上很简单:
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
[...haystack].sort(...
- Andre Figueiredo到目前为止,所有的答案都集中在整个数组的搜索上。 考虑到您的数组已经排序并且您只想要最接近的数字,这可能是最简单的(但不是最快的)解决方案:
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));
请注意,算法可以通过使用二叉树等方式大大改进。
a[i]
或者i[0]
。 - Wesley WorkmanES6
/**
* 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
这个解决方案使用ES5 存在量词 Array#some
,它允许在满足条件时停止迭代。
与Array#reduce
相反,它不需要为一个结果迭代所有元素。
在回调函数中,将搜索值和实际项之间的绝对delta
进行比较,并与上一个delta
进行比较。如果大于或等于,则停止迭代,因为所有其他值及其delta都大于实际值。
如果回调中的delta
更小,则将实际项分配给结果,并将delta
保存在lastDelta
中。
最后,取相等delta的较小值,就像下面的22
示例中一样,结果为2
。
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
closestValue([ 2, 2, 42, 80 ], 50) === 2
。 - Sébastien Vercammen其他答案建议您需要遍历整个数组:
如果数组已经排序,这就没有意义了。没有必要计算所有偏差。例如,在一个包含1百万个元素的有序集合中,您只需要计算约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
x
,逐个遍历数组中的元素,将i
与当前数组元素进行比较,如果它们之间的差小于x
当前的值,就将x
设为当前的数组元素。当遍历完成后,x
就是数组中距离i
最近的数字。 - deceze