我有一个从负 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>
它基本上使用括号和中间值检查来将每次迭代的解空间减少一半,这是一种经典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);
为了可重用性,您可以使用支持占位符的 curry 函数(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));
虽然这里发布了一些很好的解决方案,但 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)
。我创建了一个展示它的垃圾箱。