2

ナレッジベースサイトを構築しています。ナレッジベースの各ページについて、次の統計を表示できるようにしたいと思います。Y人の異なるユーザーがX回表示した

ビュー数の計算は簡単です

ビューの総数をカウントするために、ページが読み込まれるたびにページビューカウンターをインクリメントするだけです(ユーザーがログインしているため、スパムの問題などはありません)。

ユニークなページビューへの素朴なアプローチ-すべてのビューアIDを保存します

新たな訪問を新しいユニークな訪問者としてカウントするか、以前の訪問者の1人が単に再び戻ってきたものとしてカウントするかを決定するには、誰がそのページを既に見たかの記録が必要です。

これは、各ページに以前の訪問者IDのストアを保持することを意味します。訪問者が到着するたびに、そのIDがストアに存在するかどうかを確認します。もしそうなら私は何もせず、そうでなければ私はそれを追加します。その間、私は一意のIDの総数を集計しています。

単一の合計を計算するためにすべてのIDを保存して検索すると、ぎこちなく感じます

これはプログラムで行うのは非常に簡単ですが、醜い感じがします。多くのIDを保存し、新しいIDごとにルックアップを実行して単一の整数を計算するというアイデアは、私がコンパクトな解決策を見つけたよりも賢い頭脳のように感じます。

これは標準的な問題パターンですか?

新しい観測が一意であるか既存であるかを判断するための最も効率的な方法は何ですか?

これがハッシュなどを含むコンパクトなソリューションの標準的な問題であるかどうかに興味がありますか?

注意:私の興味は、これを行うための賢い数学やアルゴリズムがあるかどうかです。私はそれを解決することができます私はもっと賢い方法があると思うだけです...

4

2 に答える 2

3

ここで大声で考えただけですが、これがアルゴリズム的に意味があるかどうか見てみましょう。

すべてのページについて、次の 3 つのフィールドを保存します。

  • すでにお持ちのように、1 つのビュー カウンター

  • 1 つの「ユニーク ビューアー」カウンター

  • 1 つの「ブルーム フィルター」 (基本的には大きなビット フィールドですが、実装の詳細はググってください)

ユーザーがページにアクセスすると、そのユーザーのハッシュが生成されます。そのハッシュがすでにブルーム フィルターにある場合は、ビュー カウンターを上げてください。

ブルーム フィルターにない場合は、両方のカウンターをバンプし、そのハッシュをフィルターに追加します。ただし、ハッシュによっては、ブルーム フィルター メンバーシップ チェックで誤検出が発生する可能性があります (ただし、誤検出は発生しません)。そのため、ハッシュ アルゴリズムの選択方法には注意してください。

3 つのフィールド。悪くない。:)

参照:ウィキペディアのブルーム フィルター

編集:私はこのコードをしばらくの間浮かんでいました-コアが最初にどこから来たのかわかりませんが、何年にもわたって微調整しました-LINQPadの準備ができました:

void Main()
{
    var estimatedCount = 100000;
    var falsePositiveProbability = 0.001;
    var falsePositiveCount = 0;
    var memberCount = 0;
    var bloom = BloomFilter<char>.Create(
        estimatedCount, 
        falsePositiveProbability,
        c => c.GetHashCode(),
        c => (int)c);
    var allChars = Enumerable.Range(0, 0xffff).Select(i => (char)i).ToList();
    foreach(var c in allChars)
    {
        var alreadyIn = bloom.Test(c);
        if(alreadyIn)
        {
            falsePositiveCount++;
        }
        bloom.Add(c);
        memberCount++;
    }
    Console.WriteLine("Predicted count: {0} Predicted false positive: {1:p} ", estimatedCount, falsePositiveProbability);
    Console.WriteLine("Actual false positive count: {0} Actual member count: {1} ", falsePositiveCount, memberCount);
    Console.WriteLine("False positive rate: {0:p}", ((double)falsePositiveCount / memberCount));
}

// Define other methods and classes here
public class BloomFilter<TValue>
{
    private BitArray hashbits;
    private int numKeys;

    private Func<TValue,int> _hashFunc1;
    private Func<TValue,int> _hashFunc2;

    public static BloomFilter<TValue> Create(int estimateCount, double falsePositiveRate, Func<TValue,int> hash1, Func<TValue,int> hash2)
    {
        // formulae courtesy of http://hur.st/bloomfilter
        var tableSize = Math.Ceiling((estimateCount * Math.Log(falsePositiveRate)) / Math.Log(1.0 / (Math.Pow(2.0, Math.Log(2.0)))));
        var keyCount = Math.Round(Math.Log(2.0) * tableSize / estimateCount);
        return new BloomFilter<TValue>((int)tableSize, (int)keyCount)
        {
            _hashFunc1 = hash1,
            _hashFunc2 = hash2
        };
    }

    private BloomFilter(int tableSize, int nKeys)
    {
        numKeys = nKeys;
        hashbits = new BitArray(tableSize);
    }

    public bool Test(TValue val)
    {
        var hashKeys = GenerateHashes(val);
        foreach (int hash in hashKeys)
        {
            if (!hashbits[hash])
                return false;
        }
        return true;
    }

    public bool Add(TValue val)
    {
        bool rslt = true;
        var hashKeys = GenerateHashes(val);
        foreach (int hash in hashKeys)
        {
            if (!hashbits[hash])
            {
                rslt = false;
                hashbits[hash] = true;
            }
        }
        return rslt;
    }

    private int[] GenerateHashes(TValue val)
    {
        int hash1 = _hashFunc1(val);
        int hash2 = _hashFunc2(val);

        var hashKeys = new int[numKeys];

        hashKeys[0] = Math.Abs(hash1 % hashbits.Count);
        if (numKeys > 1)
        {
            for (int i = 1; i < numKeys; i++)
            {
                hashKeys[i] = Math.Abs((hash1 + (i * hash2)) %
                    hashbits.Count);
            }
        }
        return hashKeys;
    }
}
于 2013-01-06T17:32:20.027 に答える
0

この場合、リレーショナル データベースは、これを解決するための最も効率的または最もスケーラブルな方法ではない可能性があります。おそらくRedisのようなキーと値のストアを見ることをお勧めします-http ://redis.ioで、キーはページ識別子になり、値はハッシュテーブルに裏打ちされたセットになります-http://を参照してくださいredis.io/commands#set

于 2013-01-06T16:53:04.940 に答える