4

毎日、以下をカプセル化するデータ構造の約 50,000 インスタンス (これは最終的にはさらに大きくなる可能性があります) があります。

DateTime AsOfDate;
int key;
List<int> values; // list of distinct integers

これはおそらく関係ありませんが、リストは、 の特定の値に対して、 のすべての値の和集合が異なる整数のリストを生成するvaluesというプロパティを持つ個別の整数のリストです。つまり、同じ日に 2 つの異なるリストに整数が表示されることはありません。AsOfDatevalueskeyvalues

通常、リストには非常に少ない要素 (1 ~ 5) が含まれますが、場合によっては 50 要素にもなることがあります。

隣接する日を考慮して、2 日間の値が異なるこれらのオブジェクトのインスタンスを見つけようとしていますkeyが、リストvaluesには同じ整数が含まれています。

以下のアルゴリズムを使用しています。valuesリストを文字列に変換します

string signature = String.Join("|", values.OrderBy(n => n).ToArray());

次にsignature、整数にハッシュし、結果のハッシュ コードのリスト (毎日 1 つのリスト) を並べ替え、2 つのリストを調べて一致するものを探し、関連付けられたキーが異なるかどうかを確認します。(関連するリストもチェックして、ハッシュの衝突がないことを確認してください。)

より良い方法はありますか?

4

6 に答える 6

5

String を使用する代わりに、リスト自体をハッシュすることもできます。

それとは別に、あなたのアルゴリズムはほぼ最適だと思います。ハッシュの衝突がないと仮定すると、O(n log n + m log m) になります。ここで、n と m は、比較する 2 日間のそれぞれのエントリ数です。(ソートがボトルネックです。)

ハッシュをプラグインするバケット配列 (基本的にはハッシュテーブル) を使用する場合は、O(n + m) でこれを行うことができます。長さを仮定して、O(max(n, m)) で 2 つのバケット配列を比較できます。エントリの数に依存します (妥当な負荷係数を取得するため)。

HashSet.IntersectWith() を使用して適切な比較関数を作成することにより、ライブラリにこれを実行させることができるはずです (.NET を使用しているようです)。

すべてのエントリを少なくとも 1 回は訪問する必要があるため、O(n + m) よりもうまくいくことはありません。

編集:誤読、修正。

于 2009-02-27T02:04:06.223 に答える
4

他の回答に加えて、各リストのすべての要素間で XOR で単純に構築された低コストのハッシュを作成することで、プロセスを高速化できます。リストを並べ替える必要はなく、取得するのはint文字列よりも簡単かつ高速に格納できる だけです。

次に、結果の XOR された数値を Hashtable のキーとして使用し、挿入する前にキーの存在を確認するだけです。既存のキーが既に存在する場合にのみ、対応するリストを並べ替えて比較します。

単純な XOR を使用して衝突が発生する可能性があるため、一致が見つかった場合でもそれらを比較する必要があります。
結果は、配列を並べ替えて文字列に変換するよりもはるかに高速で、メモリフットプリントがはるかに少ないと思いました。

を独自に実装する場合はList<>、その中に XOR キーの生成を作成して、リストの各操作で再計算されるようにすることができます。
これにより、重複リストをチェックするプロセスがさらに高速になります。

コード

以下は、これを実装するための最初の試みです。

Dictionary<int, List<List<int>>> checkHash = new Dictionary<int, List<List<int>>>();

public bool CheckDuplicate(List<int> theList) {
    bool isIdentical = false;
    int xorkey = 0;
    foreach (int v in theList) xorkey ^= v;

    List<List<int>> existingLists;
    checkHash.TryGetValue(xorkey, out existingLists);
    if (existingLists != null) {
        // Already in the dictionary. Check each stored list
        foreach (List<int> li in existingLists) {
            isIdentical = (theList.Count == li.Count);
            if (isIdentical) {
                // Check all elements
                foreach (int v in theList) {
                    if (!li.Contains(v)) {
                        isIdentical = false;
                        break;
                    }
                }
            }
            if (isIdentical) break;
        }
    }
    if (existingLists == null || !isIdentical) {
        // never seen this before, add it
        List<List<int>> newList = new List<List<int>>();
        newList.Add(theList);
        checkHash.Add(xorkey, newList);
    }
    return isIdentical;
}

一見したところ、最もエレガントでも読みやすくもありません。むしろ「ハッキー」であり、Guffa のよりエレガントなバージョンよりも優れたパフォーマンスを発揮するかどうかさえわかりません。
ただしList<int>、ディクショナリにリストを格納することにより、XOR キーの衝突を処理します。

重複するキーが見つかった場合、不一致が見つかるまで、以前に保存された各リストをループします。

このコードの良い点は、おそらくほとんどの場合に得られる速度と同じくらい速く、衝突が発生したときに文字列をコンパイルするよりもさらに高速であることです。

于 2009-02-27T03:08:04.320 に答える
2

List の IEqualityComparer を実装すると、リストを辞書のキーとして使用できます。

リストがソートされている場合、次のように簡単になります。

IntListEqualityComparer : IEqualityComparer<List<int>> {

   public int GetHashCode(List<int> list) {
      int code = 0;
      foreach (int value in list) code ^=value;
      return code;
   }

   public bool Equals(List<int> list1, List<int> list2) {
      if (list1.Count != list2.Coount) return false;
      for (int i = 0; i < list1.Count; i++) {
        if (list1[i] != list2[i]) return false;
      }
      return true;
   }

}

これで、IEqualityComparer を使用する辞書を作成できます。

Dictionary<List<int>, YourClass> day1 = new Dictionary<List<int>, YourClass>(new IntListEqualityComparer());

1 日目のすべての項目をディクショナリに追加し、2 日目の項目をループして、キーがディクショナリに存在するかどうかを確認します。IEqualityComprarer はハッシュ コードと比較の両方を処理するため、誤った一致は発生しません。

ハッシュコードを計算するいくつかの異なる方法をテストしたい場合があります。例にあるものは機能しますが、特定のデータに対して最高の効率が得られない場合があります。ディクショナリが機能するためのハッシュ コードに関する唯一の要件は、同じリストが常に同じハッシュ コードを取得することです。目標は、ディクショナリ内のキーに対してできるだけ多くの異なるハッシュ コードを取得して、各バケットに (同じハッシュ コードを持つ) アイテムができるだけ少なくなるようにすることです。

于 2009-02-27T03:10:12.443 に答える
0

これを SQL データベースに配置する価値があるかもしれません。本格的な DBMS を使いたくない場合は、sqlite を使用できます。

これにより、一意性チェックとユニオン、およびこれらのタイプの操作が非常に単純なクエリになり、非常に効率的になります。また、情報が再び必要になった場合に備えて、情報を簡単に保存することもできます。

于 2009-02-27T04:02:00.090 に答える
0

値のリストを合計して、異なるリストに同じ値のセットが含まれているかどうかの事前チェックとして使用できる整数を取得することを検討しますか?

より多くの衝突が発生しますが (同じ合計が必ずしも同じ値のセットを意味するとは限りません)、まず大部分で必要な比較のセットを減らすことができると思います。

于 2009-02-27T04:02:48.667 に答える
0

順番は関係ありますか?つまり、1 日目の [1,2] と 2 日目の [2,1] は等しいですか? そうである場合、ハッシュはうまく機能しない可能性があります。代わりに、並べ替えられた配列/ベクトルを使用して、比較を支援できます。

また、どんな鍵ですか?明確な範囲 (0 ~ 63 など) はありますか? それらを大きな整数 (64 ビットを超える精度が必要な場合があります) に連結し、文字列に変換する代わりにハッシュすることができる場合があります。これには時間がかかる場合があるためです。

于 2009-02-27T02:36:45.987 に答える