대략적인 동등성을 가진 float 해싱을 구현하는 방법


15

다음과 같은 파이썬 클래스가 있다고 가정 해 봅시다 (문제는 Java와 equalsand와 동일합니다 hashCode)

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

여기서 degreesfloat로서 켈빈 온도이다. 지금, 나는 평등 테스트 및 해싱을 구현하고자하는 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?

파이썬 문서는 해싱 숫자 에 대해 조금 이야기 hash(2) == hash(2.0)하지만 이것이 같은 문제는 아닙니다.

내가 올바른 길을 가고 있습니까? 그렇다면이 상황에서 해싱을 구현하는 표준 방법은 무엇입니까?

업데이트 : 지금은 수레를 테스트 평등이 유형의 이행 성을 제거 이해 ==하고 equals. 그러나 이것이 어떻게 수레를 직접 비교해서는 안되는 "공통 지식"과 함께 진행됩니까? float를 비교하여 항등 연산자를 구현하면 정적 분석 도구가 불평합니다. 그들은 그렇게 할 권리가 있습니까?


9
왜 질문에 Java 태그가 있습니까?
Laiv

8
업데이트 정보 : 해싱 플로트는 일반적으로 의심의 여지가 있다고 말합니다. 부동 소수점을 키 또는 세트 요소로 사용하지 마십시오.
J. Fabian Meier

6
@Neil : 동시에, 소리가 정수처럼 반올림되지 않습니까? 즉, 천분의 일 정도로 반올림하면 온도를 천분의 일로 표현하는 정수인 고정 소수점 표현을 사용할 수 있습니다. 사용하기 쉽도록 게터 / 세터를 투명하게 플로트에서 플로트로 변환 할 수 있습니다.
Matthieu M.

4
켈빈은 더 이상 각도가 아닙니다. 학위도 모호합니다. 왜 그냥 전화하지 kelvin?
Solomon Ucko

5
파이썬은 다소 뛰어난 고정 소수점 지원 을 가지고 있습니다.
Jonas Schäfer

답변:


41

직접 등식 테스트 대신 부동 소수점을 엡실론 차이와 비교하는 방식으로 온도에 대한 등식 테스트 및 해싱을 구현합니다.

퍼지 평등 자바가에 배치하는 요구 사항을 위반하는 equals방법, 즉 이행 성 이있는 경우, 즉 x == y하고 y == z, 다음 x == z. 그러나 엡실론 0.1과 같은 퍼지 평등을 수행하면 0.1 == 0.2and 0.2 == 0.3이지만 0.1 == 0.3보유하지는 않습니다.

파이썬은 그러한 요구 사항을 문서화하지는 않지만, 전이가 아닌 평등을 갖는 것은 여전히 ​​나쁜 생각입니다. 이러한 유형에 대한 추론은 두통을 유발합니다.

그래서 나는 당신이 그렇게하지 않는 것이 좋습니다.

정확한 평등을 제공하고 명백한 방식으로 해시를 기반으로하고 퍼지 매칭을 수행하는 별도의 방법을 제공하거나 Kain이 제안한 동등성 클래스 접근 방식을 사용하십시오. 후자의 경우에는 생성자의 등가 클래스의 대표 멤버에 값을 고정 한 다음 나머지에 대해 간단한 정확한 평등과 해시를 사용하는 것이 좋습니다. 이런 식으로 유형을 추론하는 것이 훨씬 쉽습니다.

(그러나 그렇게하면 부동 소수점 대신 고정 소수점 표현을 사용할 수도 있습니다. 즉, 정수를 사용하여 천분의 1도 또는 필요한 정밀도를 계산할 수 있습니다.)


2
흥미로운 생각. 따라서 수백만 엡실론을 축적하고 과도 성을 가짐으로써 어떤 것도 다른 것과 같다는 결론을 내릴 수 있습니다. 그러나이 수학적인 제약은 부동 소수점의 이산 적 기초를 인정 하는가?
Christophe

@Christophe 재미있는 질문. 당신이 그것에 대해 생각한다면,이 접근법은 해상도가 엡실론 (물론 0을 중심으로 함)보다 큰 수레에서 하나의 큰 동등성 클래스를 만들고 다른 수레를 각각 자체 클래스에 남겨 두는 것을 알 수 있습니다. 그러나 중요한 것은 아닙니다. 실제 문제는 두 숫자가 같다고 결론을 내릴지 여부는 세 번째 숫자가 비교되는지 여부와 수행 순서에 달려 있다는 것입니다.
Ordous

@OP의 편집을 언급하면서, 부동 소수점의 부정확성이 부동 소수점을 포함하는 유형을 =="감염"시켜야한다고 덧붙였다 ==. 즉, 정확한 평등을 제공하라는 조언을 따르는 경우 평등이에 사용될 때 경고하도록 정적 분석 도구를 추가로 구성해야합니다 Temperature. 정말 당신이 할 수있는 유일한 일입니다.
HTNW

@HTNW : 너무 간단합니다. 비율 클래스 float approximation에는에 참여하지 않는 필드 가있을 수 있습니다 ==. 또한 정적 분석 도구는 ==비교되는 멤버 중 하나가 유형 인 경우 클래스 구현 내부에 이미 경고를 표시 float합니다.
MSalters

@MSalters? 아마도 충분히 구성 가능한 정적 분석 도구는 내가 제안한 것을 잘 수행 할 수 있습니다. 클래스에에 float참여하지 않는 필드 가 있으면 해당 클래스 ==에 대해 경고하도록 도구를 구성하지 마십시오 ==. 클래스가 그렇다면 클래스 ==를 "너무 정확"으로 표시하면 도구가 구현 내에서 이러한 종류의 오류를 무시하게됩니다. 예를 들면 자바, 경우 @Deprecated void foo(), 다음 void bar() { foo(); }경고하지만, @Deprecated void bar() { foo(); }하지 않습니다. 많은 도구가이를 지원하지 않지만 일부 도구는이를 지원할 수 있습니다.
HTNW

16

행운을 빕니다

해시로 멍청하거나 엡실론을 희생시키지 않고는 그것을 달성 할 수 없습니다.

예:

각 포인트가 고유 한 해시 값으로 해시한다고 가정합니다.

부동 소수점 숫자는 순차적이므로 주어진 부동 소수점 값 이전에 최대 k 개의 숫자가 주어지며 주어진 부동 소수점 값 뒤에 주어진 k의 일부 엡실론 내에있는 최대 k 개의 숫자가 있습니다.

  1. 동일한 해시 값을 공유하지 않는 서로 다른 엡실론 내의 두 점마다.

    • 이 두 점이 같은 값으로 해시되도록 해싱 구성표를 조정하십시오.
  2. 이러한 모든 쌍에 대해 부동 소수점 숫자의 전체 시퀀스는 단일 값을 갖도록 축소됩니다.

이것이 사실이 아닌 몇 가지 경우가 있습니다.

  • 포지티브 / 네거티브 무한대
  • NaN
  • 주어진 엡실론의 기본 범위에 연결되지 않을 수있는 비정규 화 된 범위.
  • 아마도 몇 가지 다른 형식의 특정 인스턴스

그러나 부동 소수점 범위의> = 99 %는 주어진 부동 소수점 값 위 또는 아래에 하나 이상의 부동 소수점 값을 포함하는 엡실론 값에 대해 단일 값으로 해시됩니다.

결과

> = 99 % 전체 부동 소수점 범위 해시는 단일 값으로 해시 값의 의도를 심각하게 손상시킵니다 (그리고 상당히 분산 된 저 충돌 해시에 의존하는 모든 장치 / 컨테이너).

또는 엡실론은 정확히 일치하는 항목 만 허용됩니다.

세분화

물론 세분화 된 접근 방식으로 갈 수 있습니다.

이 방법에서는 정확한 버킷을 특정 해상도로 정의합니다. 즉 :

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

각 버킷에는 고유 한 해시가 있으며 버킷 내의 부동 소수점은 동일한 버킷의 다른 부동 소수점과 동일합니다.

불행히도 두 개의 수레가 엡실론 거리에 떨어져 있고 두 개의 분리 된 해시가 여전히 가능합니다.


2
OP의 요구 사항에 맞는 경우 세분화 된 접근 방식이 최선일 것입니다. OP가 +/- 0.1 % 유형 요구 사항을 좋아한다는 것이 두렵습니다. 즉, 세분화 할 수는 없습니다.
Neil

4
@DocBrown "불가능"부분이 맞습니다. 엡실론 기반 평등이 해시 코드가 동일하다는 것을 의미한다면, 모든 해시 코드가 자동으로 동일하므로 해시 함수는 더 이상 유용하지 않습니다. 버킷 접근 방식은 유익 할 수 있지만 임의로 서로 가까운 해시 코드가 다른 숫자가 있습니다.
J. Fabian Meier

2
버킷 접근 방식은 정확한 해시 키가있는 버킷뿐만 아니라 해당 콘텐츠에 대한 두 개의 인접 버킷 (또는 적어도 하나)도 확인하여 수정할 수 있습니다. 이를 통해 실행 시간을 최대 2 배 (올바르게 구현 한 경우)만큼 증가시키는 비용에 대한 에지 사례의 문제를 최소화합니다. 그러나 일반적인 실행 시간 순서는 변경되지 않습니다.
Doc Brown

당신이 정신적으로 올바른 동안 모든 것이 무너지는 것은 아닙니다. 작은 엡실론이 고정되어 있으면 대부분의 숫자는 자기 자신과 같습니다. 물론, 엡실론은 쓸모가 없으므로 다시는 정신적으로 맞습니다.
Carsten S

1
@CarstenS 예, 단일 해시에 대한 범위 해시의 99 %가 실제로 전체 부동 범위를 다루지 않는다는 진술. 자신의 고유 한 버킷에 해시되는 엡실론 이상으로 구분 된 많은 범위의 값이 있습니다.
Kain0_0

7

후드 아래에서 온도를 정수로 모델링 할 수 있습니다. 온도는 자연적으로 하한 (-273.15 ℃)입니다. 따라서 double (-273.15는 기본 정수의 경우 0과 같습니다). 두 번째로 필요한 요소는 매핑의 세분성입니다. 이미이 세분성을 암시 적으로 사용하고 있습니다. 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 배 많은 항목을 테이블에 보유해야합니다. 2D 공간에서 객체를 찾으려면 X 방향과 Y 방향에 대해 하나의 접근 방식을 사용하는 것이 유용 할 수 있습니다. 따라서 각 항목을 한 번만 저장하는 대신 조회마다 4 개의 쿼리 작업이 필요하거나 한 번의 조회를 사용하여 항목을 찾을 수 있지만 각 항목을 4 번 저장해야합니다.


0

가수의 마지막 8 비트를 삭제 한 다음 비교 또는 해싱을 통해 "거의 동일"을 정의 할 수 있습니다. 문제는 서로 매우 가까운 숫자 가 다를 있다는 것입니다.

여기에는 약간의 혼동이 있습니다. 두 개의 부동 소수점 숫자가 동일하게 비교되면 동일합니다. 이들이 같은지 확인하려면“==“를 사용하십시오. 때때로 당신은 평등을 확인하고 싶지 않지만, 그렇게 할 때“==“가가는 길입니다.


0

이것은 답변이 아니지만 도움이 될 수있는 확장 된 설명입니다.

MPFR (GNU MP 기반) 을 사용하는 동안 비슷한 문제를 겪고 있습니다. @ Kain0_0에 의해 요약 된 "버킷"접근법은 수용 가능한 결과를 제공하는 것으로 보이지만 해당 답변에서 강조된 한계를 알고 있어야합니다.

Mathematica와 같은 "정확한" 컴퓨터 주의 대수 시스템을 사용하면 수행하려는 작업에 따라 부정확 한 수치 프로그램을 보완하거나 확인하는 데 도움이 될 수 있습니다. 이것은 당신이 예를 들어, 반올림에 대한 걱정없이 결과를 산출 할 수 있습니다 7*√2 - 5*√2얻을 것 2대신에 2.00000001또는 이와 유사한. 물론 이것은 가치가 있거나 없을 수도있는 추가 합병증을 유발할 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.