web-dev-qa-db-ja.com

一意性保証と制御可能なエントロピーを備えたハッシュ関数

次の仕様を満たすハッシュ関数のクラスはありますか?

  • 上限と下限を指定できます
  • 入力が上限と下限の間にある限り、一意性が保証されます
  • エントロピーの量は制御可能であるか、少なくとも高く、均等に分散されています

一意の結果を生成し、上限を指定できるようにする低エントロピーハッシュ関数の例は次のとおりです。

int hash(int x,int upperBound) {
    return x - (upperBound * (x \ upperBound));
}

これにより、[0、upperBound)の間の数値が生成され、数値をupperBoundで除算できる場合は0にリセットされます。

つまり、上限が20 ^ 3であるとすると、46656の数値が得られると思います。 0〜46655の数値を入力すると、固有の結果が生成されます。数字を超えると衝突が発生します。同じ番号を指定すると、常に同じ結果が得られます。エントロピーを制御できることはプラスですが、それが均等に分散されていて高い場合は、それでも問題なく機能します。

最終的な目標は、番号を英数字表現に変換することです。この英数字表現をすばやく確認して、最後に番号が要求されてから変更されているかどうかを判断できます。すべての番号が使用されるまで、同じ番号を受け取るべきではありません。

3
Justin

OK、それで私は少なくとも物事を有益な方向に動かし始めるのに十分な研究をしたと感じます。

一般に、探しているものの用語は「 完全なハッシュ関数 」であり、さらに、ある程度の潜在的に管理可能なランダム性が必要です。

一般に、最先端の技術では、マッピングを生成するアルゴリズム手法を使用して、その最終的なマッピングを保存します。 これを行うには興味深い方法がたくさんありますおよびそれ以上 )ですが、問題は、任意のランダムなメソッドとの衝突の確率が高くなる可能性が高いという事実に帰着します入力スペースを使い果たす前に衝突を保証するため。この例をC#コードで提供します(コピー/貼り付け可能なコードだと思います)。

  System.Text.StringBuilder Sb = new System.Text.StringBuilder();

  System.Collections.Generic.HashSet<string> results = new System.Collections.Generic.HashSet<string>();

  using (System.Security.Cryptography.SHA512 hash = System.Security.Cryptography.SHA512Managed.Create() ) {
    System.Text.Encoding enc = System.Text.Encoding.UTF8;

    for (int input = 0; input < 10000; input++) {
      Byte[] result = hash.ComputeHash(enc.GetBytes(input.ToString()));

      foreach (Byte b in result) {
        Sb.Append(b.ToString("x2"));
      }

      results.Add(Sb.ToString().Substring(Sb.Length - 3));
    }

  }

私が行っているのは、0〜9999の入力値を指定し、それをSHA512ハッシュに変換してから、最後の3桁の英数字のみを取得することです。次に、HashSetのサイズを入力と比較して、重複の数を判断できます。

結果:10000の入力では、3735の一意の結果しか得られません。痛い-それはたくさんの衝突です!マッピングを4桁に変更する(上記の最後のコード行をSb.Length-4に変更する)と、9303が返されます-悪くありません! 5桁の出力を許可すると、9955が得られますが、それでも衝突が発生します。許容出力を大幅に拡張し、10000入力のみを許可します。

したがって、このような方法を使用した場合は、最大入力を大幅に制限する必要があり、出力のサイズも大きくなる可能性があります。

ランダム性をあまり気にしない場合は、(x + 18) % 46656を使用して、最後の行を次のように置き換えることができます。

results.Add(((input + 18) % 46656).ToString());

これにより、衝突がゼロになり、実行速度も大幅に向上します。もちろん、特に順番に上に移動した場合、出力はまったくランダムではありません。

さて、いくつかの手作業で調整して、この小さなおかしなものを思いつくことができました。

results.Add(((input * (40001) + 11) % 46656).ToString());

したがって、入力を取得し、40001を掛け、11を加算してから、最大入力に「ラップアラウンド」します。 0-46656を入力として使用すると、衝突は発生しませんが、f(1)-> 400012、f(2)-> 33357のすべての場所でジャンプします。最初のいくつかの入力(0〜2)の使用をスキップすると、これらの数値を生成する関数はさらに不透明になり、これは非線形であるため、このセットを生成する関数を見つけるのは簡単ではありません。奇数を掛けると、これを関数の一種の「種」として使用できます。素数の方がさらに良い選択かもしれません。小さい数値は結果としてジャンプ性が少なくなりますが、modを取っている同じ数値を掛けると...まあ、常に0になります。中/高側のどこかが理想的だと思います。

次に、整数出力を目的の文字列形式に解析します。タダ、ボブはあなたの叔父です!

ここで、測定可能なエントロピーを備えたよりランダムなものが必要であり、リバースエンジニアリングに非常に長い時間がかかる場合は、まあ、それをはるかに調整するのは難しくなります。

これが、少なくとも行きたい場所に近づくのに役立つことを願っています。

2
BrianH