分析比較アルゴリズムのKnockoutJS配列
開発のフロントエンドは、データ構造の有効な配列です。このブログは、使用されるデータ比較アルゴリズムのKnockoutJSの実装を説明し、分析します。
汎用アルゴリズム
KnockoutJSは、配列要素のMVVM使用が更新UIを駆動するように、ユーザ入力要求または背景が、データ変更のアレイが発生するデータ・アレイ内のビューモデル・データ・モデルに対応するであろうと思いました。このようなカートにカテゴリページショッピングとして、ユーザーはいくつかのボタンをクリックすると、ショッピングカートに保存されている/削除項目を追加することができます。ショッピングカートはアイテムの配列要素の変化に応じて詳細のリストがリアルタイムで更新されますが表示されます。別の例では、キューページショーを待っているショーケースのレストランでは、ゲストの参加/退出キューと、サーバーは、フロントページに現在のキュー条件の最新のリアルタイム更新をデータをプッシュし続けることができました。したがって、我々は、アルゴリズムを必要とする、更新前のデータと更新されたデータアレイのアレイによれば、動作のシーケンスは、DOM、DOMの結合は自動的にデータモデルの変更のデータ要素を更新できるようにDOM要素を計算します。
クラシック編集距離の問題
分析の公式の開始前に、古典的なアルゴリズムの問題を見てみましょう。できるように、2つの文字列を考えると、別の文字列に1つの文字列を変換するために、操作の数を計算する方法を、文字「置き換え」「削除」または、「追加」。私たちは、簡単に問題を見つけることができると我々は議論する前に、この古典的な問題は非常に似ていますが、いくつかの相違点は、以下の点があります。
- DOM要素とうまくサポートしていない、操作を「置き換え」。ブラウザでJavaScriptのAPIは、効率的に「追加」および「削除」を組み合わせ操作で同等の操作「置き換え」で行う必要があり、DOM要素のDOMに別の要素に変換されていません。
- DOM要素は、「移動」操作をサポートすることができます。元編集距離で言及したが、すでにブラウザに存在するDOM要素の使用は非常に合理的なアプローチではないが。
- 元の問題は、操作のみの最小数を計算する必要があり、我々は運用DOMの順序で問題を把握する必要があります。
これはよく、動的プログラミング、必要用いて古典的編集距離アルゴリズムが知られているO(m*n)
時間とO(m*n)
空間的複雑度(その二つの文字列の長さをそれぞれ仮定m
とをn
)。
編集距離アルゴリズムを使用KnockoutJS
最初のステップKnockoutJS配列比較アルゴリズムは、編集距離アルゴリズムの変形であり、具体的な問題の特殊性に基づいて、いくつかの調整を行いました。動的プログラミングアルゴリズムを使用して(Mと呼ばれる)編集距離の2次元マトリクス、二つの配列+ 1つのアレイの最小編集距離に対応する各要素を計算することが必要です。例えば、二つの配列が呼び出されると仮定arr1
し、arr2
行列値のi番目の行は、j番目の列であり、arr1[:i]
かつarr2[:j]
最小編集距離+ 1。
(i、j)のから任意のペアを見つけることは難しいことではありません、我々は以下の再帰的な関係を持つことができます。
- ARR1 [I-1] === ARR2 [J-1]、我々は、単一の動作をカットすることができ、M [i]は[J] = M [I-1] [J-1]
- ARR1 [I-1]!== arryou2 [J-1]、その後、我々は2つの選択肢があり、最小を取ります
- ARR1削除[I-1]、比較が続け、その後、M [i]は[J] = M [I-1]〜[J] + 1
- ARR1に[I-1]の場合M [I] [J] = M [I] [J-1] + 1、等しいARR2 [J-1]の要素を追加する比較を継続した後
この漸化式によると、私たちはより良い1行1列目を初期化する方法を知ることができます。アルゴリズム自体は、私たちが再帰スタックの余分なオーバーヘッドがもたらす保存することができ、達成するためにボトムアップのサイクルを使用しています。
次のように編集距離の特定のコードをコンピューティング:
// ... preprocess such that arr1 is always the shorter array
var myMin = Math.min,
myMax = Math.max,
editDistanceMatrix = [],
smlIndex, smlIndexMax = smlArray.length,
bigIndex, bigIndexMax = bigArray.length,
compareRange = (bigIndexMax - smlIndexMax) || 1,
maxDistance = smlIndexMax + bigIndexMax + 1,
thisRow, lastRow,
bigIndexMaxForRow, bigIndexMinForRow;
for (smlIndex = 0; smlIndex <= smlIndexMax; smlIndex++) {
lastRow = thisRow;
editDistanceMatrix.push(thisRow = []);
bigIndexMaxForRow = myMin(bigIndexMax, smlIndex + compareRange);
bigIndexMinForRow = myMax(0, smlIndex - 1);
for (bigIndex = bigIndexMinForRow; bigIndex <= bigIndexMaxForRow; bigIndex++) {
if (!bigIndex)
thisRow[bigIndex] = smlIndex + 1;
else if (!smlIndex) // Top row - transform empty array into new array via additions
thisRow[bigIndex] = bigIndex + 1;
else if (smlArray[smlIndex - 1] === bigArray[bigIndex - 1])
thisRow[bigIndex] = lastRow[bigIndex - 1]; // copy value (no edit)
else {
var northDistance = lastRow[bigIndex] || maxDistance; // not in big (deletion)
var westDistance = thisRow[bigIndex - 1] || maxDistance; // not in small (addition)
thisRow[bigIndex] = myMin(northDistance, westDistance) + 1;
}
}
}
// editDistanceMatrix now stores the result
アルゴリズムは、特定の問題の特性を利用し、それは頭と尾クロスそうペアリングに最適な状況のサブシーケンスです。例えば、アレイ及びABC EFGため、ペアリングABC eは最適解の内部に表示されないことができます。したがってのみ第二層ループアルゴリズムの必要性は、長さ、配列の長さの差とアレイの代わりに、配列の長い長さの短い長さを横断します。アルゴリズムの時間複雑さが軽減されるまでO(m*(n-m))
。JavaScriptのArrayオブジェクトベースの実現ので、インデックスは、未使用のメモリを占有しないので、スペースの複雑さもに減少していますO(m*(n-m))
。
それは、これは非常に効率的なアルゴリズムで、このシナリオでアプリケーションを見つけるでしょう考えてみてください。理論の複雑さにもかかわらず、それは最悪平方クラスのままですが、フロントエンドアプリケーションのためのシーン、小型高周波数データの変更に直面し、ほとんどの時間。言い換えれば、ほとんどの場合で、n
かつm
ほとんどの場合、このアルゴリズムは、複雑さのレベルの二乗に比べ、線形時間と空間の複雑さを達成することができ非常に近く、とても巨大なアップグレードです。
編集距離行列取得動作のシーケンスを取得した後、最初の行または最初の列の前の規則に従って割り当てにだけバックエンドから、非常に簡単です。
次のように動作シーケンスの特定のコードをコンピューティング:
// ... continue from the edit distance computation
var editScript = [], meMinusOne, notInSml = [], notInBig = [];
for (smlIndex = smlIndexMax, bigIndex = bigIndexMax; smlIndex || bigIndex;) {
meMinusOne = editDistanceMatrix[smlIndex][bigIndex] - 1;
if (bigIndex && meMinusOne === editDistanceMatrix[smlIndex][bigIndex-1]) {
notInSml.push(editScript[editScript.length] = { // added
'status': statusNotInSml,
'value': bigArray[--bigIndex],
'index': bigIndex });
} else if (smlIndex && meMinusOne === editDistanceMatrix[smlIndex - 1][bigIndex]) {
notInBig.push(editScript[editScript.length] = { // deleted
'status': statusNotInBig,
'value': smlArray[--smlIndex],
'index': smlIndex });
} else {
--bigIndex;
--smlIndex;
if (!options['sparse']) {
editScript.push({
'status': "retained",
'value': bigArray[bigIndex] });
}
}
}
// editScript has the (reversed) sequence of actions
モバイルに最適化された要素
前述したように、既存の要素の使用は、DOM操作の不要な重複を減らすことができ、具体的な方法は、すべての「追加」、「削除」操作で、同じ要素のためのチェックが追加され、同時に削除された、非常に簡単ですA。このプロセスは、最悪の場合の必要O(m*n)
アルゴリズムは、オプションのパラメータを提供するように、連続的に、従来の最適化の破壊までの時間複雑性を10*m
ペアリングアルゴリズムは、可動要素の出口は、全体アルゴリズムを確保する場合には見られませんほぼ線形時間複雑。
次のように可動子特定コードを確認してください。
// left is all the operations of "Add"
// right is all the operations of "Delete
if (left.length && right.length) {
var failedCompares, l, r, leftItem, rightItem;
for (failedCompares = l = 0; (!limitFailedCompares || failedCompares < limitFailedCompares) && (leftItem = left[l]); ++l) {
for (r = 0; rightItem = right[r]; ++r) {
if (leftItem['value'] === rightItem['value']) {
leftItem['moved'] = rightItem['index'];
rightItem['moved'] = leftItem['index'];
right.splice(r, 1); // This item is marked as moved; so remove it from right list
failedCompares = r = 0; // Reset failed compares count because we're checking for consecutive failures
break;
}
}
failedCompares += r;
}
}
// Operations that can be optimized to "Move" will be marked with the "moved" property
関連するコードはここで見つけることができます完全に
ノックアウト/ SRC /結合/ editDetection / compareArrays.js