キーでコレクションにアクセスする方法を確認します。ただし、ハッシュ関数自体には、背後で多くの操作がありますよね?
非常に効率的なNiceハッシュ関数があると仮定すると、それでも多くの操作が必要になる場合があります。
これは説明できますか?
HashFunc
自体には、背後で多くの操作があります
確かにそうです。ただし、これらの操作の数は、キーが挿入されるハッシュテーブルのサイズではなく、キーのサイズに依存します:計算する操作の数ハッシュ関数は、10個または1万個のエントリがあるテーブルのキーと同じです。
それが、ハッシュ関数の呼び出しがしばしばO(1)と見なされる理由です。これは、固定サイズのキー(整数値と固定長の文字列)に対して正常に機能します。また、実用的な上限を持つ可変サイズのキーの適切な近似を提供します。
ただし、一般的に、ハッシュテーブルのアクセス時間はO(k)です。ここで、k
はハッシュキーのサイズの上限です。
O(1)
はインスタントを意味しません。 O(1)
は定数データのサイズに関係なくを意味します。ハッシュ関数には一定の時間がかかりますが、その時間はコレクションのサイズに比例しません。
つまり、コレクションのサイズに関係なく、メンバーを取得するのにほぼ同じ時間がかかります。
つまり、5人のメンバーがいる辞書では、coudがそのうちの1つにアクセスするのに約0.002ミリ秒かかり、25人のメンバーの辞書も同様のものを使用するとします。 Big Oは、実行される実際のステートメントまたは関数ではなく、コレクションサイズに対するアルゴリズムの複雑さを意味します
辞書/マップがHashMap
として実装されている場合、ベストケースの複雑さ of O(1)
になります。キーの衝突がない場合、取得のためのキー要素のコード。
hash-mapには、最悪の場合の実行時の複雑さのO(n)
が含まれる場合があります。多くのキーの衝突または非常に悪いハッシュ関数がある場合は、この場合、データを保持する配列全体の線形スキャンになります。
また、O(1)
は瞬時にを意味するわけではなく、定数の量を意味します。そのため、辞書に適切な実装を選択することは、コレクション内の要素の数にも依存する可能性があります。関数の定数コストが非常に高くなるのは、エントリが少ない場合はさらに悪いからです。
そのため、辞書/マップはシナリオごとに異なる方法で実装されます。 Javaには複数の異なる実装があり、C++はレッド/ブラックツリーなどを使用します。データの数とベスト/平均/ワーストケースのランタイム効率に基づいて選択しました。 。
理論的にはまだO(n)です。最悪の場合、すべてのデータが同一のハッシュを持ち、一緒にバンドルされる可能性があり、その場合はすべてを直線的に処理する必要があります。
投稿を参照してください 「O(1)アクセス時間」とはどういう意味ですか?
ハッシュ関数内の操作の数は、コレクション内のすべての要素に対して同じ(一定の)時間を要する限り、関係ありません。たとえば、2つの要素のコレクション内の1つの要素へのアクセスには.001ミリ秒かかりますが、2,000,000,000の要素のコレクション内の1つの要素へのアクセスには.001ミリ秒かかります。ただし、ハッシュ関数には何百ものifステートメントと複数の計算を含めることができます。
ドキュメントから:
T:System.Collections.Generic.Dictionary`2クラスはハッシュテーブルとして実装されているため、キーを使用して値を取得するのは非常に高速で、O(1)に近くなります。
だからO(1)かもしれないが、もっと遅いかもしれない。ハッシュテーブルのパフォーマンスに関する別のスレッドを見つけることができる: Hash table-なぜ配列よりも速いのか? ?
より大きな辞書がより多くのメモリを消費し、キャッシュ階層をさらに下って最終的にディスク上のスワップスペースを遅くするという事実を考慮すると、それが本当にO(1)であると主張するのは困難です。辞書のパフォーマンスは大きくなるにつれて遅くなり、おそらくO(log N)時間の複雑さが生じます。信じられない? 1、100、1000、10000などの辞書要素(最大1,000億)で試してみて、実際に要素を検索するのにかかる時間を測定します。
ただし、システム内のすべてのメモリがランダムアクセスメモリであり、一定の時間でアクセスできるという単純な仮定を立てる場合、辞書はO(1)であると主張できます。ディスクスワップスペースを備えたマシンには当てはまらないにもかかわらず、この仮定は一般的であり、CPUキャッシュのさまざまなレベルを考えると、いずれにしてもかなり議論の余地があります。