在JavaScript数组中查找最接近值的算法

时间:2019-02-06 13:20:59

标签: javascript arrays

我正在研究一种小型算法,用于在随机数字数组中找到给定数字的最接近值。在我的情况下,我试图检测由6位数字ID(“ 123456”,“ 0078965”,...)标识的已连接计算机,但是它对于例如找到我附近最接近地理位置的用户很有用。 >

我需要列出5个最接近的计算机,而不管它们的ID是高还是低。这段代码可以完美地运行,但是我正在寻找一种更聪明,更好的方式进行处理,我要处理很多循环和数组。

let n = 0; // counter
let m = 5; // number of final machines to find

// list of IDs founded (unordered: we can't decide)
const arr = ["087965","258369","885974","0078965","457896","998120","698745","399710","357984","698745","789456"] 
let NUM = "176789" // the random NUM to test

const temp = [];
const diff = {};
let result = null;

// list the [m] highest founded (5 IDs)
for(i=0 ; i<arr.length; i++) {
    if(arr[i] > NUM) {
        for(j=0 ; j<m; j++) {
            temp.push(arr[i+j]);
        } break;
    }
}

// list the [m] lowest founded (5 IDs)
for(i=arr.length ; i>=0; i--) {
    if(arr[i] < NUM) {
        for(j=m ; j>=0; j--) {
            temp.push(arr[i-j]);
        } break;
    }
}

// now we are certain to get at least 5 IDs even if NUM is 999999 or 000000

temp.sort(function(a, b){return a - b}); // increase order

for(i=0 ; i<(m*2); i++) {
    let difference = Math.abs(NUM - temp[i]);
    diff[difference] = temp[i]; // [ 20519 : "964223" ]
}

// we now get a 10-values "temp" array ordered by difference
// list the [m] first IDs:

for(key in diff){
    if(n < m){
        let add = 6-diff[key].toString().length; 
        let zer = '0'.repeat(add);
        let id = zer+diff[key]; // "5802" -> "005802"
        result += (n+1)+":"+ id +" ";
        n+=1;
    }
}

alert(result);

-> "1:0078965 2:087965 3:258369 4:357984 5:399710" for "176789"

3 个答案:

答案 0 :(得分:1)

实际上,您不需要这么多不同的迭代。您只需要循环两次:

  1. 第一次迭代尝试是使用.map()创建一个对象数组,该对象数组存储ID以及ID与num之间的绝对差值
  2. 第二次迭代尝试只是在步骤1中创建的对象数组中使用.sort(),将它们从最低到最高的差异进行排名

第二次迭代完成后,您只需使用.slice(0, 5)即可获取数组中的前5个对象,该数组现在包含最小的5个差异。如果您只想提取ID,请再次进行遍历:

const arr = ["087965","258369","885974","078965","457896","998120","698745","399710","357984","698745","789456"];
let num = "176789";
let m = 5; // number of final machines to find

// Create an array of objects storing the original arr + diff from `num`
const diff = arr.map(item => {
  return { id: item, diff: Math.abs(+item - +num) };
});

// Sort by difference from `num` (lowest to highest)
diff.sort((a, b) => a.diff - b.diff);

// Get the first m entries
const filteredArr = diff.slice(0, m).map(item => item.id).sort();

// Log
console.log(filteredArr);

// Completely optional, if you want to format it the way you have in your question
console.log(`"${filteredArr.map((v, i) => i + ": " + v).join(', ')}" for "${num}"`);

答案 1 :(得分:0)

您可以将一个数组作为结果集,用前n个元素填充它,然后按所需值的增量对其进行排序。

对于所有其他元素,请检查实际项目的绝对增量和该值是否小于结果集的最后一个值,然后将该值替换为实际项目。再次排序。重复直到所有元素都处理完。

通过使用目标值,结果集按最小增量到最大顺序排列。

const
    absDelta = (a, b) => Math.abs(a - b),
    sortDelta = v => (a, b) => absDelta(a, v) - absDelta(b, v),
    array = [087965, 258369, 885974, 0078965, 457896, 998120, 698745, 399710, 357984, 698745, 789456],
    value = 176789,
    n = 5,
    result = array.reduce((r, v) => {
        if (r.length < n) {
            r.push(v);
            r.sort(sortDelta(value));
            return r;
        }
        if (absDelta(v, value) < absDelta(r[n - 1], value)) {
            r[n - 1] = v;
            r.sort(sortDelta(value));
        }
        return r;
    }, []);

console.log(result); // sorted by closest value

答案 2 :(得分:0)

到目前为止,有一些不错的方法,但是我无法忍受。

这将测试数组排序版本中n个元素的滑动窗口,并返回其中点最接近您要查找的值的窗口。这是一种非常有效的方法(一种数组,然后一次遍历)–尽管它无法捕获存在多个正确答案的情况(请参见下面的最后一个测试用例)。

const closestN = function(n, target, arr) {
  // make sure we're not comparing strings, then sort:
  let sorted = arr.map(Number).sort((a, b) => a - b); 
  target = Number(target);

  let bestDiff = Infinity; // actual diff can be assumed to be lower than this
  let bestSlice = 0;       // until proven otherwise

  for (var i = 0; i <= sorted.length - n; i++) {
    let median = medianOf(sorted[i], sorted[i+n-1]) // midpoint of the group
    let diff = Math.abs(target - median);           // distance to the target
    if (diff < bestDiff) {  // we improved on the previous attempt
      bestDiff = diff;      // capture this for later comparisons                         
      bestSlice = i;
    }
    // TODO handle diff == bestDiff? i.e. more than one possible correct answer
  }
  return sorted.slice(bestSlice, bestSlice + n) 
}

// I cheated a bit here; this won't work if a > b:
const medianOf = function(a, b) { 
  return (Math.abs(b-a) / 2) + a
}

console.log(closestN(5, 176789, ["087965", "258369", "885974", "0078965", "457896", "998120", "698745", "399710", "357984", "698745", "789456"]))

// more test cases
console.log(closestN(3,   5, [1,2,5,8,9])) // should be 2,5,8
console.log(closestN(3,   4, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(1,   4, [1,2,5,8,9])) // should be 5
console.log(closestN(3,  99, [1,2,5,8,9])) // should be 5,8,9
console.log(closestN(3, -99, [1,2,5,8,9])) // should be 1,2,5
console.log(closestN(3,  -2, [-10, -5, 0, 4])) // should be -5, 0, 4
console.log(closestN(1,   2, [1,3]))  // either 1 or 3 would be correct...