从阵列中获得最接近的数字
我有一个从减去1000到1000的数字,我有一个数组在其中。像这样:从阵列中获得最接近的数字
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
我希望我的数字已经变化到最接近的数组数。
比如我得到80
的号码,我想让它变得82
。
对数组稍作修改的二进制搜索将起作用。
啧啧,这是奇怪的 - 显然有人不喜欢二分搜索。 (即使认为这是最好的通用解决方案) – 2011-12-21 04:28:02
对于一个小范围来说,最简单的事情就是创建一个地图数组,其中例如第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代码:
而且,如果你真的在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
如上所述,该不应该对小数据集或不需要需要非常快速的事情有所作为,但这是您可能需要考虑的一个选项。
@micha,我已经添加了eqivalent JS代码到答案,它只是花了一段时间才从一个我更习惯于更改语言:-) – paxdiablo 2011-12-21 04:46:10
该算法的运行时间太差如果你有大的数据集。 – 2015-04-21 08:14:44
@ ylun.ca,因为在数据排序的问题中没有_explicit_语句(该例子被排序但可能是巧合的),所以你不能得到比O(n)更好的效率。无论如何,对于数据集的大小来说,效率大都是无关紧要的。但是你的观点是有效的,所以我会为此添加注释以希望使答案更加完整。 – paxdiablo 2015-04-25 23:14:26
工作的代码如下:
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));
越多越好。 – 2011-12-21 13:19:04
我认为这是一个更好的解决方案,因为它只使用JavaScript。接受的答案使用了jQuery,这在原始问题中没有提到,以及其他人在看这个问题时可能没有使用。 – 2014-04-14 22:42:18
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这里添加我的解决方案&。
懒惰,我简直不敢相信,对于这个问题的解决将是一个循环,所以我搜索了一点,与filter 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);
这一切!
那么下界是什么?你总是使用下一个更高的值。但是你的方法让我想起了'goog.math.clamp'(谷歌关闭)只是数组而不关心下限。 – noob 2014-04-06 08:55:19
您的解决方案从阵列中返回下一个更高的数字。无论是最近的还是确切的价值都没有找到。 – 2014-08-01 22:44:24
对于排序的阵列(线性搜索)
所有答案为止集中通过整个阵列搜索。 考虑到你的数组已经排序,你真的只想最接近的数字这可能是最快的解决方案:
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));
注意,该算法可以大大提高例如使用二叉树。
虽然这里的这个策略很好,但有几个错别字。例如'a [i]'或'i [0]'。 – 2017-03-09 16:10:50
谢谢,@WesleyWorkman。只是修复它们。我希望我得到了一切。顺便说一句,你也可以编辑他人的答案。 – 2017-03-25 08:14:28
我需要在上面的回答代码中进行修正以获得更高的最接近的值吗? 例如:[110,111,120,140,148,149,155,177,188,190]如果我搜索150,我应该得到155而不是149.我试过但发现难以解决这个问题。能否请你帮忙。谢谢 – user1199842 2018-01-03 14:44:13
我的回答类似的问题被占的关系也是一样,它是在普通的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);
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或咖喱功能包)。这给你很大的灵活性,取决于你需要什么:
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]);
我喜欢融合的方法,但它有一个小错误。这是正确的:
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是一种灵活的语言,让我们工具许多不同的方式来解决问题。当然,这一切都归结于你的风格。如果你的代码具有更多的功能,你会发现reduce variation合适的,即:
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
相反other approaches发现使用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)
中找到的最小值的索引。
我创建了一个bin来演示它。
Downvoters,请解释为什么这不是一个好的答案或为什么你不觉得它合适。谢谢。 – 2016-10-09 09:52:16
我不知道,但我想你可能会有性能怀疑,因为这实际上是'3n',它是ES5,即使你在2016年回答,其他解决方案也很好,即使这个清醒地问这个问题的noob不是程序员当时。 – noob 2016-10-10 17:50:06
恕我直言,我不明白ES6版本的答案如何使它更好,除了使用'const'(箭头函数的作用是什么?)。有些人可能会认为它更不可读。最重要的是,我怀疑ES6在一般的atm中表现不错,请记住,我们仍然运行大多数ES5并使用Babel来转换ES6代码。最后,我认为表演的论点很荒谬。我挑战你对所有方法进行基准测试,并查看浏览器中哪些方法最慢。我不知道你在你的生活中做了多少基准测试,但是从它的外观来看 - 你很惊喜。 – 2016-10-10 18:32:12
这是一个建议与ES5 existential quantifierArray#some
,它允许停止迭代,如果满足条件。
对Array#reduce
,它不需要迭代一个结果的所有元素。
在回调内部,搜索值与实际item
之间的绝对值为delta
,并与上一个增量值进行比较。如果大于或等于,则迭代停止,因为其所有其他值与他们的增量值都大于实际值。
如果回调中的delta
较小,则将实际项目分配给结果,delta
保存为das lastDelta
。
在结果中,采用等于三角形的较小值,如下面的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
具有较小值的优先级码
所以你认为给定的数组是排序的...这可以节省你很多时间。 – Redu 2017-01-08 21:45:31
op的数组看起来排序了,所以是的:) – 2017-01-08 22:03:25
另一种变型是,我们将头部连接到脚趾的圆形范围内,只接受给定输入的最小值。这帮助我获得了一种加密算法的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;
});
}
只有当你没有尝试任何东西时,这是不可能的。你有什么尝试? – 2011-12-21 03:57:57
不可能简单:预留一个变量'x',逐个检查数组,比较'i'到数组中的当前数字,如果它和'i'之间的差值小于'x中的当前值',将'x'设置为当前的阵列号。完成后,'x'的数字最接近'i'。 – deceze 2011-12-21 04:00:57