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

IT技术 javascript arrays
2021-01-10 18:26:23

我有一个从负 1000 到正 1000 的数字,并且我有一个包含数字的数组。像这样:

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

我希望我得到的数字更改为最接近的数组数字。

例如,我得到的80数字是我希望它得到的82

6个回答

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

@7yl4r 不是真的......你可以使用绑定来实现这个...... ---------- // reducer.js function reducer(goal, prev, curr) { return (Math.abs(curr -目标) < Math.abs(prev - 目标) ? curr : prev); } // main.js var counts = [4, 9, 15, 6, 2],goal = 5; counts.reduce(reducer.bind(null,goal)); ---------- 不知道怎么在评论里放代码哈哈哈。
2021-03-15 18:26:23
@7yl4r 还是将其包装在一个函数中?;)
2021-03-23 18:26:23
缺点是它仅在从与声明的变量相同的范围调用 reduce 的回调时才有效。由于您无法传递goal给reduce,因此您必须从全局范围内引用它。
2021-03-24 18:26:23
如果列表是有序的,但对于小列表来说还可以,这将迭代每个不是最佳的项目。即使没有二进制搜索,如果下一个数字更远,循环也可能退出。
2021-03-26 18:26:23
也可以使用高阶函数来做到这一点。
2021-04-04 18:26:23

这是应该可以转换为任何程序语言的伪代码:

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

如前所述,对于小型数据集或不需要非常快的事物,这应该没有太大区别,但这是您可能想要考虑的一个选项。

谢谢我希望我能不止一次投票!更多关于堆栈溢出的答案应该投入这种努力。
2021-03-16 18:26:23
如果您有大型数据集,则此算法的运行时间很短。
2021-03-27 18:26:23
@micha,我已经在答案中添加了等效的 JS 代码,我花了一段时间才将语言从我更习惯的语言更改为 :-)
2021-03-28 18:26:23
您在这个答案中付出的努力非常棒,非常感谢您提供这些图表,它使您更容易理解
2021-03-30 18:26:23
@ylun.ca,由于问题中没有明确声明数据已排序(示例已排序但可能是巧合),因此您无法获得比 O(n) 更好的效率。无论如何,对于这么大的数据集,效率几乎无关紧要。但你的观点是有效的,所以我会为此添加注释,希望能让答案更完整。
2021-04-01 18:26:23

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>

工作代码如下:

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。接受的答案使用 jQuery,它在原始问题中没有提到,而其他看这个问题的人可能没有使用。
2021-03-29 18:26:23
如果您5000在数组中搜索最接近的数字[1, 2, 3],您会感到非常震惊。
2021-04-10 18:26:23

适用于未排序的数组

虽然这里发布了一些很好的解决方案,但 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进行排序我们不需要关心索引或事先对其进行排序。

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

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

我创建了一个展示它垃圾箱

我不知道,但我想人们可能会怀疑性能,因为这实际上3n是 ES5,即使您在 2016 年回答,其他解决方案也很好,即使这个菜鸟当时显然不是程序员。
2021-03-11 18:26:23
您可以让 findClosest 返回一个 reduce 回调函数,使其可在所有数组上重用: const findClosest = goal => (a,b) => Math.abs(a - goal) < Math.abs(b - goal) ? a : b;
2021-03-15 18:26:23
简单解决方案的道具。非常适合我的用例(我没有像 noob 那样拥有预先排序的数组的奢侈。)
2021-03-16 18:26:23
例子: [2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
2021-03-25 18:26:23
是的,很高兴看到你学习!顺便说一下,我只谈到了性能怀疑,并没有争辩说它实际上表现得更糟,但我为您计算了数字,您的O(n)解决方案O(log n)在随机数上的执行速度比 @paxdiablo 少约 100k ops/sec 他们说在设计算法时总是先排序。(除非您知道自己在做什么并且有基准支持。)
2021-04-01 18:26:23