近似等式を使用したフロートハッシュの実装方法


15

次のPythonクラスがあるとしましょう(問題はJavaにもequalsandと同じように存在しますhashCode

class Temperature:
    def __init__(self, degrees):
        self.degrees = degrees

ここでdegrees、フロートとしてのケルビンの温度です。今、私はのための平等のテストやハッシュを実装したいTemperatureという方法で、

  • 直接等価テストではなく、イプシロンの差までフロートを比較します。
  • a == b意味する契約を尊重しhash(a) == hash(b)ます。
def __eq__(self, other):
    return abs(self.degrees - other.degrees) < EPSILON

def __hash__(self):
    return # What goes here?

Pythonのドキュメントでは、数値ハッシュすることについて少し説明してhash(2) == hash(2.0)いますが、これはまったく同じ問題ではありません。

私は正しい軌道に乗っていますか?もしそうなら、この状況でハッシュを実装する標準的な方法は何ですか?

更新:現在、フロートのこのタイプの等価テストでは==、との推移性が排除されていることを理解していequalsます。しかし、フロートを直接比較するべきではないという「常識」とどのように結びついているのでしょうか?浮動小数点数を比較して等値演算子を実装すると、静的解析ツールが文句を言います。彼らはそうする権利がありますか?


9
質問にJavaのタグがあるのはなぜですか?
ライヴ

8
アップデートについて:ハッシュフロートは一般的に疑わしいものだと思います。キーまたはセット要素としてフロートを使用しないようにしてください。
J.ファビアンマイヤー

6
@Neil:同時に、整数のように丸めることはしないのですか?つまり、たとえば、1000分の1度に丸めることができる場合は、固定小数点表現、つまり1000分の1度で温度を表す整数を使用できます。使いやすさのために、あなたがしたい場合はセッター透過...フロートへ/からの変換/ゲッターを持つことができます
マシューM.

4
ケルビンはもはや学位ではありません。度もあいまいです。なぜそれを呼ぶだけではありkelvinませんか?
ソロモンウッコ

回答:


41

直接の同等性テストではなく、フロートをイプシロンの差まで比較する方法で、温度の同等性テストとハッシュを実装します。

ファジー等式は、Javaがequalsメソッドに課す要件、すなわち推移性、つまりif x == yおよびy == z、thenに違反しますx == z。しかし、例えば、0.1のイプシロンでファジー等式を行う場合、0.1 == 0.2and 0.2 == 0.30.1 == 0.3成立しますが、成立しません。

Pythonはそのような要件を文書化していませんが、非推移的な平等を持つことの意味合いは、それを非常に悪い考えにします。そのようなタイプについての推論は頭痛を誘発します。

ですから、そうしないことを強くお勧めします。

正確な等式を提供し、明白な方法でハッシュに基づいて、ファジーマッチングを行う別の方法を提供するか、Kainが提案する同等クラスアプローチを使用します。後者の場合は、コンストラクターで等価クラスの代表メンバーに値を修正し、残りについては単純な完全な等価性とハッシュを使用することをお勧めします。この方法で型について推論する方がはるかに簡単です。

(しかし、それを行う場合、浮動小数点の代わりに固定小数点表現を使用することもできます。つまり、整数を使用して1000分の1度、または必要な精度をカウントします。)


2
興味深い考え。したがって、数百万のイプシロンを累積し、推移性を使用すると、何かが他の何かと等しいと結論付けることができます:
クリストフ

@Christophe興味深い質問。考えてみると、このアプローチでは、解像度がイプシロン(もちろん0を中心とする)よりも大きい浮動小数点数から単一の大きな等価クラスを作成し、他の浮動小数点数をそれぞれ独自のクラスのままにすることがわかります。しかし、それはポイントではありません。実際の問題は、2つの数値が等しいと結論付けるかどうかは、3番目の数値が比較されるかどうか、およびそれが行われる順序に依存することです。
オーダス

@OPの編集に対処して、浮動小数点の不正確さは浮動小数点を含む型に==「感染」するはずだと付け加え==ます。つまり、正確な同等性を提供するというあなたのアドバイスに従う場合、静的分析ツールは、で同等性が使用されたときに警告するようにさらに構成する必要がありますTemperature。本当にできるのはそれだけです。
HTNW

@HTNW:それは単純すぎるでしょう。比率クラスには、にfloat approximation参加しないフィールドがある場合があり==ます。さらに、静的分析ツール==は、比較されるメンバーの1つが型である場合、クラスの実装内で既に警告を出しfloatます。
–MSalters

@MSalters?おそらく、十分に構成可能な静的分析ツールは、私がうまく提案したことを実行できます。クラスににfloat参加しないフィールドがある場合、そのクラス==について警告するようにツールを構成しないでください==。クラスがそうなら、おそらくクラス==を「あまりにも正確」とマークすると、ツールは実装内のその種のエラーを無視します。たとえば、Javaの場合@Deprecated void foo()void bar() { foo(); }は警告ですが、そうで@Deprecated void bar() { foo(); }はありません。多くのツールはこれをサポートしていませんが、一部のツールはサポートしていません。
HTNW

16

幸運を

ハッシュで愚かであるか、イプシロンを犠牲にすることなく、それを達成することはできません。

例:

各ポイントが独自の一意のハッシュ値にハッシュすると仮定します。

浮動小数点数は連続しているため、指定された浮動小数点値の前に最大k個、指定された浮動小数点値の後に最大k個の番号があり、それらは指定されたポイントのイプシロン内にあります。

  1. 同じハッシュ値を共有しない、イプシロン内の2つのポイントごと。

    • これら2つのポイントが同じ値にハッシュされるように、ハッシュスキームを調整します。
  2. このようなすべてのペアに対して、浮動小数点数のシーケンス全体が単一の値を持つように崩壊します。

これが当てはまらない場合がいくつかあります。

  • 正/負の無限大
  • ナン
  • 特定のイプシロンのメイン範囲にリンクできない可能性のあるいくつかの非正規化範囲。
  • おそらく他のいくつかの形式固有のインスタンス

ただし、浮動小数点範囲の> = 99%は、特定の浮動小数点値の上または下に少なくとも1つの浮動小数点値を含むイプシロンの値に対して、単一の値にハッシュされます。

結果

浮動小数点範囲全体の99%以上が単一値にハッシュされ、ハッシュ値の意図を著しく損なっています(および、かなり分散された低衝突ハッシュに依存するデバイス/コンテナー)。

または、イプシロンは、完全一致のみが許可されるようなものです。

粒状

もちろん、代わりに詳細なアプローチを選択することもできます。

このアプローチでは、特定の解像度まで正確なバケットを定義します。すなわち:

[0.001, 0.002)
[0.002, 0.003)
[0.003, 0.004)
...
[122.999, 123.000)
...

各バケットには一意のハッシュがあり、バケット内の浮動小数点はすべて、同じバケット内の他の浮動小数点と同等です。

残念ながら、2つのフロートがイプシロン距離離れている可能性があり、2つの別々のハッシュを持っています。


2
OPの要件に適合する場合、ここでのきめ細かなアプローチがおそらく最善であることに同意します。OPには+/- 0.1%のタイプ要件があるのではないかと思いますが、これはきめ細かくすることはできません。
ニール

4
@DocBrown「不可能」な部分は正しいです。イプシロンベースの等式がハッシュコードが等しいことを意味する場合、すべてのハッシュコードが自動的に等しくなるため、ハッシュ関数はもはや役に立ちません。バケットアプローチは実り多いものですが、異なるハッシュコードを持つ数字があり、それらは互いに任意に近いものになります。
J.ファビアンマイヤー

2
バケットアプローチは、正確なハッシュキーを持つバケットだけでなく、コンテンツの隣接する2つのバケット(または少なくとも1つ)もチェックすることで変更できます。これにより、これらのエッジケースの問題が解消され、実行時間を最大2倍に増やすことができます(正しく実装した場合)。ただし、一般的な実行時間の順序は変更されません。
Doc Brown

あなたが精神的に正しい間、すべてが崩壊するわけではありません。固定された小さなイプシロンを使用すると、ほとんどの数字は自分自身に等しくなります。もちろん、それらの場合、イプシロンは役に立たないので、再び精神の中であなたは正しいです。
カーステンS

1
@CarstenSはい、範囲ハッシュの99%が1つのハッシュにハッシュ化されているという私の声明は、実際にはフロート範囲全体をカバーしていません。独自のバケットにハッシュするイプシロン以上で区切られた多くの高範囲値があります。
Kain0_0

7

温度をフードの下の整数としてモデル化できます。温度には自然な下限(摂氏-273.15)があります。したがって、double(基になる整数の場合、-273.15は0に等しくなります)。必要な2番目の要素は、マッピングの粒度です。すでにこの粒度を暗黙的に使用しています。それはあなたのEPSILONです。

温度をEPSILONで除算し、その底を取ります。これで、ハッシュと同等が同期して動作します。Python 3では、整数は無制限です。必要に応じてEPSILONを小さくできます。

用心 あなたはEPSILONの値を変更した場合、あなたは彼らがいない互換性がありますオブジェクトをシリアライズしています!

#Pseudo code
class Temperature:
    def __init__(self, degrees):
        #CHECK INVALID VALUES HERE
        #TRANSFORM TO KELVIN HERE
        self.degrees = Math.floor(kelvin/EPSILON)

1

特定のキーに「ほぼ等しい」ものを見つけることができる浮動小数点ハッシュテーブルを実装するには、いくつかのアプローチまたはその組み合わせを使用する必要があります。

  1. 各値を「ファジー」範囲よりやや大きい増分に丸めてからハッシュテーブルに保存し、値を見つけようとするときは、求めた値の上下の丸められた値についてハッシュテーブルを確認します。

  2. 求める値の上下にあるキーを使用して、ハッシュテーブル内に各アイテムを保存します。

どちらのアプローチを使用する場合でも、ハッシュテーブルエントリはアイテムではなくリストを識別する必要があります。これは、各キーに複数のアイテムが関連付けられる可能性があるためです。上記の最初のアプローチでは、必要なハッシュテーブルのサイズが最小になりますが、テーブルにないアイテムを検索するたびに2つのハッシュテーブルルックアップが必要になります。2番目の方法では、アイテムがテーブルにないことをすぐに識別できますが、通常はテーブルに必要なエントリの約2倍のエントリを保持する必要があります。2D空間でオブジェクトを見つけようとしている場合、X方向に1つのアプローチを使用し、Y方向に1つのアプローチを使用すると、各アイテムを1回保存する代わりに、ルックアップごとに4つのクエリ操作が必要になるか、 1つのルックアップを使用してアイテムを見つけることができますが、各アイテムを4回保存する必要があります。


0

もちろん、仮数の最後の8ビットを削除してから比較またはハッシュすることで、「ほぼ等しい」と定義できます。問題は、互いに非常に近い数字が異なるがあることです。

ここでいくつかの混乱があります:2つの浮動小数点数が等しい場合、それらは等しいです。それらが等しいかどうかを確認するには、「==」を使用します。平等をチェックしたくない場合もありますが、その場合は「==」を使用します。


0

これは答えではありませんが、役に立つコメントになる可能性があります。

MPFR(GNU MPベース)を使用している間に、私は同様の問題に取り組んでいます。@ Kain0_0によって概説された「バケット」アプローチは許容できる結果を与えるようですが、その答えで強調された制限に注意してください。

あなたがしようとしていることに応じて-Mathematicaのような「正確な」(警告emptor)コンピュータ代数システムを使用すると、不正確な数値プログラムを補完または検証するのに役立つかもしれません。これにより、丸めを心配することなく結果を計算できます。たとえば、代わりに、または同様の結果7*√2 - 5*√2が得られます。もちろん、これは価値があるかもしれないしそうでないかもしれない追加の合併症をもたらします。22.00000001

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.