JavaScript
 Computer >> コンピューター >  >> プログラミング >> JavaScript

JavaScriptで+と-の割り当てにより目標の合計になるパターン数を求める方法


問題

整数の配列 arr を第1引数に、単一の整数 target を第2引数として受け取る JavaScript 関数を作成する必要があります。

配列内の各整数に対して、「+」または「-」のどちらか一方を割り当てることができます。

この関数の目的は、配列内のすべての整数の合計が目標値 target と一致するように「+」と「-」を割り当てる方法が、合計で何通り存在するかを求めることです。

たとえば、関数への入力が次のような場合を考えてみましょう。

const arr = [1, 1, 1, 1, 1];
const target = 3;

この場合の出力は次のようになります。

const output = 5;

出力の解説

答えが 5 になるのは、以下の 5 通りの割り当て方が存在するためです。

-1+1+1+1+1 = 3
+1-1+1+1+1 = 3
+1+1-1+1+1 = 3
+1+1+1-1+1 = 3
+1+1+1+1-1 = 3

アルゴリズムの考え方

各要素に対して「+」と「-」の2択があるため、すべての組み合わせを素朴に試すと O(2n) の計算量が必要になり、配列が長くなると現実的ではありません。そこで有効なのが、再帰処理とメモ化(キャッシュ)を組み合わせたアプローチです。

実装のポイントは次のとおりです。

  • 末尾の要素から順に処理し、残りの目標値に対して「その要素を足す場合」と「引く場合」の2通りに分岐しながら再帰的に探索します。
  • 「インデックスと目標値のペア」をキーとしたオブジェクトに計算結果をキャッシュすることで、同じ状態を繰り返し計算する無駄を省き、計算量を大幅に削減できます。
  • 先頭の要素まで到達したとき、目標値が 0 かつ先頭要素が 0 であれば「+0」と「-0」の2通りが成立するため 2 を返します。それ以外の場合は、先頭要素が目標値またはその符号反転と一致するときに 1 を返します。

コード例

実際のコードは次のようになります。

const arr = [1, 1, 1, 1, 1];
const target = 3;
const waysToSum = (arr = [], target = 1) => {
   const map = {};
   const find = (arr, target, i) => {
      let val = i + '->' + target;
      if(map[val] !== undefined){
         return map[val];
      };
      if(i === 0){
         if (target === 0 && arr[0] === 0) { return 2 }
         return arr[0] === target || arr[0] === -target ? 1 : 0
      };
      map[val] = find(arr, target + arr[i], i - 1) + find(arr, target - arr[i], i - 1);
      return map[val]
   };
   return find(arr, target, arr.length-1)
};
console.log(waysToSum(arr, target));

出力結果

コンソールには次のように表示されます。

5

  1. JavaScriptで配列を空にする4つの方法【サンプルコード付きで解説】

    JavaScriptで配列を空にする方法は、主に4つあります。それぞれ動作の仕組みや特徴が異なるため、目的に応じて適切な方法を選ぶことが大切です。配列を空にする4つの方法新しい空の配列を代入する:配列変数に、新しく作成した空の配列を代入します。最もシンプルな方法ですが、元の配列を参照している他の変数が残る点には注意が必要です。lengthプロパティを使う:配列のlengthプロパティに0を設定するだけで、すべての要素を削除できます。高速で、元の配列への参照も維持されるため、実務で最もよく使われる方法のひとつです。popメソッドを使う:配列の要素数が0になるまで、popメソッドで末尾の要素を繰り

  2. JavaScriptのnew.targetメタプロパティとは?使い方をわかりやすく解説

    JavaScriptのnew.targetとはnew.targetは、関数やコンストラクタが実行時にnewキーワードを使って呼び出されたかどうかを判定できるメタプロパティです。通常、関数をnewをつけずに呼び出すと、コンストラクタとして意図された関数でも単なる通常の関数として実行されてしまい、グローバルオブジェクトにプロパティが設定されるなどの予期しない動作を引き起こす可能性があります。new.targetを利用することで、このような誤用を検出し、エラーとして通知することができます。new演算子とともに呼び出された場合、new.targetは呼び出されたコンストラクタ自身への参照を返します。一