概要
Ted Jaspers が賢明にも指摘したように、私が 2012 年の最初の提案で説明した方法論は、実際には指数移動平均の特殊なケースです。このアプローチの優れた点は、再帰的に計算できることです。つまり、オブジェクトごとに単一の人気値を保存するだけでよく、イベントが発生したときにこの値を再帰的に調整できます。すべてのイベントを記録する必要はありません。
この単一の人気値は、過去のすべてのイベント (使用されているデータ型の制限内) を表しますが、新しいイベントが考慮されるにつれて、古いイベントは指数関数的に重要ではなくなります。このアルゴリズムは、さまざまな時間スケールに適応し、さまざまなトラフィック量に対応します。 . イベントが発生するたびに、次の式を使用して新しい人気値を計算できます。
(a * t) + ((1 - a) * p)
a
— 0 から 1 の間の係数 (値が大きいほど、古いイベントが早く割引されます)t
— 現在のタイムスタンプp
— 現在の人気値 (例: データベースに保存)
willの適切な値はa
、アプリケーションによって異なります。ここa=2/(N+1)
で、N
は結果に大きく影響するイベントの数です。たとえば、イベントがページ ビューであるトラフィックの少ない Web サイトでは、数日間で数百のページ ビューが予想される場合があります。N=100
( ) を選択a≈0.02
するのが妥当な選択です。トラフィックの多い Web サイトの場合、数日間で数百万のページ ビューが予想される場合があります。その場合、N=1000000
( a≈0.000002
) の方が合理的です。の値はa
、時間をかけて徐々に調整する必要があります。
このポピュラリティ アルゴリズムがいかに単純であるかを説明するために、2 行の Twig マークアップで Craft CMS に実装する方法の例を次に示します。
{% set popularity = (0.02 * date().timestamp) + (0.98 * entry.popularity) %}
{% do entry.setFieldValue("popularity", popularity) %}
人気度を計算するために、新しいデータベース テーブルを作成したり、無限のイベント レコードを保存したりする必要がないことに注意してください。
覚えておくべき 1 つの注意点は、指数移動平均にはスピンアップ間隔があるため、値が正確であると見なされるまでに数回の再帰が必要なことです。これは、初期条件が重要であることを意味します。たとえば、新しいアイテムの人気が現在のタイムスタンプを使用して初期化されている場合、そのアイテムは、最終的により正確な位置に落ち着く前に、セット全体ですぐに最も人気のあるアイテムになります。これは、新しいコンテンツを宣伝したい場合に適しています。または、コンテンツを下から順に処理する場合、アプリケーションが最初に起動されたときのタイムスタンプでコンテンツを初期化できます。データベース内のすべての人気値の平均で値を初期化することで、満足のいく中間を見つけることもできるため、中間から開始します。
元の提案
アイテムの年齢と、アイテムが受け取る投票数、クリック数、または購入数に基づいて人気を計算するための提案されたアルゴリズムはたくさんあります。しかし、私が見たより堅牢な方法では、過度に複雑な計算と、データベースを乱雑にする複数の格納値が必要になることがよくあります。私は、変数 (人気値自体以外) を格納する必要がなく、1 つの単純な計算のみを必要とする、非常に単純なアルゴリズムを考えています。それはばかげて簡単です:
p = (p + t) / 2
ここで、p はデータベースに保存されている人気の値で、t は現在のタイムスタンプです。アイテムが最初に作成されるとき、pを初期化する必要があります。次の 2 つの初期化方法があります。
- 現在のタイムスタンプtでpを初期化します
- データベース内のすべてのp値の平均でpを初期化します
初期化方法 (1) は、最近追加されたアイテムに過去のアイテムよりも明らかな利点を与えることに注意してください。したがって、関連性の要素が追加されます。一方、初期化方法 (2) は、新しいアイテムを過去のアイテムと比較して同等に扱います。
初期化メソッド (1) を使用し、現在のタイムスタンプでpを初期化するとします。アイテムが最初の投票を受け取ると、pは作成時間と投票時間の平均になります。したがって、人気値pは引き続き有効なタイムスタンプを表します (最も近い整数に丸めると仮定します) が、それが表す実際の時間は抽象化されます。
この方法では、単純な計算が 1 つだけ必要であり、データベース ( p ) に格納する必要があるのは 1 つの値だけです。この方法は、特定のアイテムの人気が現在の時間を超えることは決してないため、値の暴走も防ぎます。
1 日間にわたって動作するアルゴリズムの例: http://jsfiddle.net/q2UCn/
1 年間にわたって動作するアルゴリズムの例: http://jsfiddle.net/tWU9y/
投票が 1 秒未満の間隔で着実に流れ込むことが予想される場合は、PHPmicrotime()
関数などのマイクロ秒のタイムスタンプを使用する必要があります。それ以外の場合は、PHP 関数などの標準の UNIX タイムスタンプが機能しtime()
ます。
私の質問ですが、このアプローチに大きな欠陥はありますか?