파이썬의 무한대 해시 숫자가 π 인 이유는 무엇입니까?


241

파이썬에서 무한의 해시는 pi와 일치하는 숫자를 갖습니다 .

>>> inf = float('inf')
>>> hash(inf)
314159
>>> int(math.pi*1e5)
314159

우연의 일치 일까, 아니면 의도적 인 것일까?


9
확실하지는 않지만 제 생각은 고의적 인 hash(float('nan'))0입니다.
cs95

1
흠,에 대한 언급은 없습니다 sys.hash_info. 이스터 에그?
wim

123
팀 피터스에게 물어보십시오. 다음은 그가 19 년 전에이 상수를 도입 어디 커밋의 github.com/python/cpython/commit/...을 . 내가있는 숫자 해시를 재 작업 할 때 나는 그 특별한 가치를 유지 bugs.python.org/issue8188
마크 디킨슨에게

8
@MarkDickinson 감사합니다. Tim이 원래 -inf 해시에 e 의 숫자를 사용한 것 같습니다 .
wim

17
@wim 아 그래, 맞아. 그리고 분명히 나는로 변경했습니다 -314159. 나는 그것을 잊었다.
Mark Dickinson

답변:


47

_PyHASH_INF되고 상수로 정의는 동일 314159.

이에 대한 토론이나 이유를 알려주는 의견을 찾을 수 없습니다. 나는 그것이 다소 임의로 선택되었다고 생각합니다. 다른 해시에 동일한 의미의 값을 사용하지 않는 한 중요하지 않다고 생각합니다.


6
작은 nitpick : 다른 해시에도 같은 값이 사용될 것이라는 정의에 따르면 거의 불가피합니다. 예를 들어이 경우 hash(314159)도 마찬가지 314159입니다. 또한 파이썬 3에서, 시도 hash(2305843009214008110) == 314159(이 입력이 314159 + sys.hash_info.modulus등)
ShreevatsaR

3
@ShreevatsaR 저는 방금 정의에 의해이 값을 다른 값의 해시로 선택하지 않는 한, 이와 같은 의미있는 값을 선택해도 해시 충돌의 가능성이 증가하지 않습니다.
Patrick Haugh

220

요약 : 우연의 일치가 아닙니다. 파이썬의 기본 CPython 구현에서 _PyHASH_INF314159하드 코딩 되었으며 2000 년 Tim Peters에 의해 임의의 값 (π의 숫자에서 분명히 선택됨)으로 선택 되었습니다 .


의 값은 hash(float('inf'))숫자 유형의 내장 해시 함수의 시스템에 의존하는 매개 변수 중 하나이며, 도 사용할 수 있습니다sys.hash_info.inf파이썬 3 :

>>> import sys
>>> sys.hash_info
sys.hash_info(width=64, modulus=2305843009213693951, inf=314159, nan=0, imag=1000003, algorithm='siphash24', hash_bits=64, seed_bits=128, cutoff=0)
>>> sys.hash_info.inf
314159

( PyPy와 동일한 결과 도 있습니다.)


코드 측면 hash에서 내장 함수입니다. 그 포인터으로 제공되는 기능 파이썬 플로트 객체를 호출합니다 호출 tp_hash속성 내장 된 플로트 타입 (의 PyTypeObject PyFloat_Type) 이다float_hash 기능, 정의return _Py_HashDouble(v->ob_fval)차례로, 이는

    if (Py_IS_INFINITY(v))
        return v > 0 ? _PyHASH_INF : -_PyHASH_INF;

여기서 314159 _PyHASH_INF정의됩니다 .

#define _PyHASH_INF 314159

역사의 측면에서의 첫 번째 언급 314159파이썬 코드에서이 맥락에서은 (당신이 이것을 찾을 수 있습니다 git bisect또는 git log -S 314159 -p추가되었다) 팀 피터스 (Tim Peters) 지금 커밋 무엇에, 2000 년 8 월 39dce293을cpython자식 저장소.

커밋 메시지는 다음과 같이 말합니다.

http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470 수정되었습니다 . 이것은 오해의 소지가있는 버그였습니다. 진정한 "버그"는 무한대 일 hash(x)때 오류를 반환했습니다 x. 고쳤다. 에 새 Py_IS_INFINITY매크로를 추가 했습니다 pyport.h. 부동 소수점과 복소수의 해싱에서 중복되는 중복을 줄이기 위해 코드를 재정렬하여 Trent의 초기 단계를 논리적 결론으로 ​​옮겼습니다. 오류가없는 경우에도 플로트 해시가 -1을 반환 할 수있는 매우 드문 버그가 수정되었습니다 (테스트 사례를 작성하는 데 시간을 낭비하지 않았을 때 발생 하는 코드에서 간단히 알 수 있음 ). 더 이상 hash(complex(x, y))체계적으로 동일하지 않도록 복잡한 해시를 개선했습니다 hash(complex(y, x)).

특히, 그가의 코드 찢어 커밋 static long float_hash(PyFloatObject *v)의를 Objects/floatobject.c그냥 그것을 만든 return _Py_HashDouble(v->ob_fval);, 그리고 정의에 long _Py_HashDouble(double v)에서 Objects/object.c그가 선을 추가 :

        if (Py_IS_INFINITY(intpart))
            /* can't convert to long int -- arbitrary */
            v = v < 0 ? -271828.0 : 314159.0;

언급했듯이 임의의 선택이었습니다. 271828은 e 의 처음 몇 십진수로 구성 됩니다.

관련 커밋 :


44
-Inf에 대해 -271828을 선택하면 pi 협회가 우연이라는 의심이 사라집니다.
Russell Borogove

24
@RussellBorogove 아니요.하지만 약 100 만 배나 줄어 듭니다.)
pipe

8
@cmaster :에, 즉 문서 섹션이 2010 년 5 월 말한다하는 위 부분을 참조, 숫자 유형의 해싱문제 8188 - 아이디어는 우리가 원하는 것입니다 hash(42.0)와 동일하게 hash(42)또한, 같은 hash(Decimal(42))hash(complex(42))hash(Fraction(42, 1)). Mark Dickinson의 해결책은 우아한 IMO입니다. 모든 유리수에 사용할 수있는 수학 함수를 정의하고 부동 소수점 숫자도 유리수라는 사실을 사용합니다.
ShreevatsaR

1
@ShreevatsaR 아, 감사합니다. 이러한 평등을 보장하지는 않았지만 복잡해 보이는 코드에 대해 훌륭하고 견고하며 논리적 인 설명이 있음을 아는 것이 좋습니다. :-)
cmaster-복원 monica

2
@cmaster 정수의 해시 함수는 단순히 hash(n) = n % MM = (2 ^ 61-1 )입니다. 이것은 hash(p/q) = (p/q) mod M분할 n이 모듈로 M로 해석되는 합리적인 n에 대해 일반화됩니다 (즉, hash(p/q) = (p * inverse(q, M)) % M). 우리가 이것을 원하는 이유 : 만약 d우리 가 dict에 넣고 d[x] = foo우리가 가지고 있지만 x==y(예 : 42.0 == 42) d[y]와 같지 않다면, d[x]우리는 문제가있을 것입니다. 겉보기에 복잡한 코드의 대부분은 부동 소수점 형식 자체의 특성에서 비롯되어 분수를 올바르게 복구하고 inf 및 NaN 값에 특별한 경우가 필요합니다.
ShreevatsaR

12

과연,

sys.hash_info.inf

을 반환합니다 314159. 값은 생성되지 않으며 소스 코드에 내장되어 있습니다. 사실로,

hash(float('-inf'))

-271828파이썬 2에서, 또는 대략 -e를 반환합니다 ( 지금 -314159입니다). ).

가장 유명한 두 가지 비이성적 인 숫자가 해시 값으로 사용된다는 사실은 이것이 우연의 일치가 아닐 수 있습니다.

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