回答:
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);
goal
reduceに渡すことはできないため、グローバルスコープから参照する必要があります。
以下は、手続き型言語に変換できる疑似コードです。
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
与えられた数と各配列要素の間の絶対差を計算し、最小の差を持つものの1つを返します。
値の例:
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>
ここで、たとえば、データアイテムがソートされている場合(サンプルデータから推測できるが明示的に述べていない場合)は、効率を向上させる余地があることに注意してください。たとえば、バイナリ検索を使用して最も近いアイテムを見つけることができます。
また、1秒間に何回も実行する必要がない限り、データセットが大幅に大きくならない限り、効率の向上はほとんど気付かれないことにも留意してください。
あなたがいる場合かそのように試してみたい(と配列を昇順にソートされて保証することができます)、これは出発点は良いです。
<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(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 closestTo5 = getClosest(_, 5);
const closestTo = getClosest([4, 9, 15, 6, 2]);
次として働くコード:
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
をソートする必要がありません。インデックスを気にする必要も、事前にソートする必要もありません。
わかりやすくするために、コードを1行ずつ説明します。
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)
ます。それを示すビンを作成しました。
3n
、2016年に答えたとしても実際にはES5であり、この質問をしたこのnoobが明らかにプログラマではなかったとしても、他のソリューションで問題ないので、パフォーマンスに疑問があるかもしれません。
O(n)
ソリューションではO(log n)
、乱数に対して@paxdiabloよりも100k ops / sec程度少なくなっています。アルゴリズムを設計するときは常に最初にソートします。(あなたがあなたが何をしているかを知っていて、あなたをバックアップするためのベンチマークを持っている場合を除いて。)
const findClosest = goal => (a,b) => Math.abs(a - goal) < Math.abs(b - goal) ? a : b;
[2, 42, 82, 122, 162, 202, 242, 282, 322, 362].reduce(findClosest(80))
これまでのすべての答えは、配列全体を検索することに集中しています。あなたの配列がすでにソートされており、最も近い数だけが本当に必要であることを考えると、これがおそらく最も速い解決策です:
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]
。
すべてのソリューションは過剰に設計されています。
それは次のように簡単です:
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);
});
// 5
このソリューションでは、条件が満たされた場合に反復を停止できるES5 存在量指定子 Array#some
を使用します。
とは逆にArray#reduce
、1つの結果に対してすべての要素を繰り返す必要はありません。
コールバック内delta
で、検索された値と実際の値の間の絶対値item
が取得され、最後のデルタと比較されます。差分がある他のすべての値は実際の値よりも大きいため、これ以上の場合、反復は停止します。
場合はdelta
、コールバックには小さく、実際のアイテムが結果に割り当てられ、delta
中に保存されます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
closestValue([ 2, 2, 42, 80 ], 50) === 2
ES6
/**
* 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
古い質問に答えるのかどうかはわかりませんが、この投稿はGoogle検索で最初に表示されるため、ここに私のソリューションと2cを追加することをお許しください。
怠惰な私は、この質問の解決策がLOOPであるとは信じられなかったので、もう少し検索してフィルター機能を使用しました。
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
、配列だけで下限を気にせずに(Googleクロージャー)を思い出させます。
同様の質問への私の答えは関係も説明し、それは単純なJavascriptですが、バイナリ検索を使用していないため、O(logN)ではなくO(N)です。
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);
Fusionからのアプローチは好きですが、小さなエラーがあります。そのようにそれは正しいです:
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()
が、他の関数よりもわずかに高速です。
improved for loop
んか?逆ループは、常にパフォーマンスが向上するとは限りません。
.length
宣言するときi
、このループでは、1回だけ評価します。しかし、私はvar i = arr.length;while (i--) {}
もっと速くなると思います
while
。今ではさらに高速です。
ここでのもう1つのバリアントは、頭からつま先に接続する循環範囲があり、指定された入力に最小値のみを受け入れます。これは、暗号化アルゴリズムの1つの文字コード値を取得するのに役立ちました。
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;
});
}
#include <algorithm>
#include <iostream>
#include <cmath>
using namespace std;
class CompareFunctor
{
public:
CompareFunctor(int n) { _n = n; }
bool operator()(int & val1, int & val2)
{
int diff1 = abs(val1 - _n);
int diff2 = abs(val2 - _n);
return (diff1 < diff2);
}
private:
int _n;
};
int Find_Closest_Value(int nums[], int size, int n)
{
CompareFunctor cf(n);
int cn = *min_element(nums, nums + size, cf);
return cn;
}
int main()
{
int nums[] = { 2, 42, 82, 122, 162, 202, 242, 282, 322, 362 };
int size = sizeof(nums) / sizeof(int);
int n = 80;
int cn = Find_Closest_Value(nums, size, n);
cout << "\nClosest value = " << cn << endl;
cin.get();
}
最も効率的な方法は、バイナリ検索です。ただし、次の数値が現在の数値とさらに一致する場合、単純なソリューションでも終了できます。ここでのほぼすべてのソリューションは、配列が順序付けされて全体が繰り返されることを考慮していません:/
const closest = (orderedArray, value, valueGetter = item => item) =>
orderedArray.find((item, i) =>
i === orderedArray.length - 1 ||
Math.abs(value - valueGetter(item)) < Math.abs(value - valueGetter(orderedArray[i + 1])));
var data = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362];
console.log('21 -> 2', closest(data, 21) === 2);
console.log('22 -> 42', closest(data, 22) === 42); // equidistant between 2 and 42, select highest
console.log('23 -> 42', closest(data, 23) === 42);
console.log('80 -> 82', closest(data, 80) === 82);
これは非プリミティブでも実行できます。 closest(data, 21, item => item.age)
に変更find
しfindIndex
て、配列のインデックスを返します。
配列内で最も近い2つの数値を検索するには
function findTwoClosest(givenList, goal) {
var first;
var second;
var finalCollection = [givenList[0], givenList[1]];
givenList.forEach((item, firtIndex) => {
first = item;
for (let i = firtIndex + 1; i < givenList.length; i++) {
second = givenList[i];
if (first + second < goal) {
if (first + second > finalCollection[0] + finalCollection[1]) {
finalCollection = [first, second];
}
}
}
});
return finalCollection;
}
var counts = [2, 42, 82, 122, 162, 202, 242, 282, 322, 362]
var goal = 80;
console.log(findTwoClosest(counts, goal));
以下は、複雑度O(nlog(n))の配列から数値に最も近い要素を見つけるためのコードスニペットです。
入力:-{1,60,0、-10,100,87,56}要素:-56配列内の最も近い数:-60
ソースコード(Java):
package com.algo.closestnumberinarray;
import java.util.TreeMap;
public class Find_Closest_Number_In_Array {
public static void main(String arsg[]) {
int array[] = { 1, 60, 0, -10, 100, 87, 69 };
int number = 56;
int num = getClosestNumber(array, number);
System.out.println("Number is=" + num);
}
public static int getClosestNumber(int[] array, int number) {
int diff[] = new int[array.length];
TreeMap<Integer, Integer> keyVal = new TreeMap<Integer, Integer>();
for (int i = 0; i < array.length; i++) {
if (array[i] > number) {
diff[i] = array[i] - number;
keyVal.put(diff[i], array[i]);
} else {
diff[i] = number - array[i];
keyVal.put(diff[i], array[i]);
}
}
int closestKey = keyVal.firstKey();
int closestVal = keyVal.get(closestKey);
return closestVal;
}
}
x
、配列を1つずつi
調べて、配列内の現在の数値と比較します。その差i
がの現在の値よりも小さい場合は、現在の配列番号にx
設定x
します。終了x
するi
と、配列から最も近い番号になります。