파이썬에서 hash (n) == n은 언제입니까?


100

저는 파이썬의 해시 함수를 가지고 놀았습니다 . 작은 정수의 경우 hash(n) == n항상 나타납니다 . 그러나 이것은 많은 수로 확장되지 않습니다.

>>> hash(2**100) == 2**100
False

놀랍지 않습니다. 해시가 유한 한 범위의 값을 취한다는 것을 이해합니다. 그 범위는 무엇입니까?

이진 검색 을 사용 하여 가장 작은 숫자를 찾으려고했습니다.hash(n) != n

>>> import codejamhelpers # pip install codejamhelpers
>>> help(codejamhelpers.binary_search)
Help on function binary_search in module codejamhelpers.binary_search:

binary_search(f, t)
    Given an increasing function :math:`f`, find the greatest non-negative integer :math:`n` such that :math:`f(n) \le t`. If :math:`f(n) > t` for all :math:`n \ge 0`, return None.

>>> f = lambda n: int(hash(n) != n)
>>> n = codejamhelpers.binary_search(f, 0)
>>> hash(n)
2305843009213693950
>>> hash(n+1)
0

2305843009213693951의 특별한 점은 무엇입니까? 나는 그것이보다 적다는 것을 알아sys.maxsize == 9223372036854775807

편집 : 저는 Python 3을 사용하고 있습니다. Python 2에서 동일한 이진 검색을 실행했는데 다른 결과 2147483648이 나타났습니다. sys.maxint+1

나는 또한 [hash(random.random()) for i in range(10**6)]해시 함수의 범위를 추정하기 위해 놀았습니다 . 최대 값은 지속적으로 n 위보다 낮습니다. 최소값을 비교하면 Python 3의 해시는 항상 양의 값을 갖는 반면 Python 2의 해시는 음의 값을 취할 수 있습니다.


9
숫자의 이진 표현을 확인 했습니까?
John Dvorak

3
'0b1111111111111111111111111111111111111111111111111111111111111'호기심! 그래서 n+1 == 2**61-1
대령 패닉

2
시스템에 따라 다릅니다. 내 파이썬에서는 해시가 n전체 64 비트 int 범위에 해당합니다.
Daniel

1
해시 값의 명시된 목적에 유의하십시오 . 사전 조회 중에 사전 키를 빠르게 비교하는 데 사용됩니다. 즉, 구현 정의되고 해시 값을 가질 수있는 많은 값보다 짧기 때문에 합리적인 입력 공간에서도 충돌이 발생할 수 있습니다.
CVn

2
음, 2147483647같지 않습니다 sys.maxint(아님 sys.maxint+1), 'n = 0b1111111111111111111111111111111111111111111111111111111111111'이면 같지 n+1 == 2**61않거나 n == 2**61-1(아님 n+1 == 2**61-1)?
phoog

답변:


73

pyhash.c파일의 파이썬 문서를 기반으로 :

숫자 형의 경우 숫자 x의 해시는 x modulo the prime 감소를 기반으로합니다 P = 2**_PyHASH_BITS - 1. hash(x) == hash(y)x와 y가 서로 다른 유형을 가지더라도 x와 y가 수치 적으로 동일 할 때마다 설계되었습니다 .

따라서 64/32 비트 머신의 경우 감소는 2 _PyHASH_BITS -1이됩니다.하지만 무엇 _PyHASH_BITS입니까?

pyhash.h64 비트 머신의 경우 61로 정의 된 헤더 파일 에서 찾을 수 있습니다 ( pyconfig.h파일 에서 자세한 설명을 읽을 수 있음 ).

#if SIZEOF_VOID_P >= 8
#  define _PyHASH_BITS 61
#else
#  define _PyHASH_BITS 31
#endif

따라서 먼저 64 비트 Linux 플랫폼에서 사용자의 플랫폼을 기반으로합니다. 감소는 2 61 -1입니다 2305843009213693951.

>>> 2**61 - 1
2305843009213693951

또한 64 비트 시스템의 경우 최대 int가 2 63 임을 나타내는 math.frexp가수와 지수를 얻기 위해 사용할 수 있습니다 .sys.maxint

>>> import math
>>> math.frexp(sys.maxint)
(0.5, 64)

간단한 테스트를 통해 차이를 확인할 수 있습니다.

>>> hash(2**62) == 2**62
True
>>> hash(2**63) == 2**63
False

Python 해싱 알고리즘에 대한 전체 문서 읽기 https://github.com/python/cpython/blob/master/Python/pyhash.c#L34

주석에서 언급했듯이 sys.hash_info해시 계산에 사용되는 매개 변수의 구조체 시퀀스를 제공하는 (python 3.X에서) 사용할 수 있습니다 .

>>> 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)
>>> 

이전 줄에서 설명한 모듈러스와 함께 inf다음과 같은 값을 얻을 수도 있습니다 .

>>> hash(float('inf'))
314159
>>> sys.hash_info.inf
314159

3
sys.hash_info완전성 을 위해 언급하는 것이 좋을 것 입니다.
Mark Dickinson

78

2305843009213693951입니다 2^61 - 1. 64 비트에 맞는 가장 큰 메르 센 프라임입니다.

값 mod를 가져 와서 해시를 만들어야한다면 큰 Mersenne 소수를 선택하는 것이 좋습니다. 계산이 쉽고 가능성의 균등 한 분포를 보장합니다. (개인적으로는 이런 식으로 해시를 만들지 않지만)

부동 소수점 숫자에 대한 계수를 계산하는 것이 특히 편리합니다. 그들은 정수에를 곱하는 지수 성분을 가지고 있습니다 2^x. 때문에 2^61 = 1 mod 2^61-1, 당신은 단지를 고려할 필요가있다 (exponent) mod 61.

참조 : https://en.wikipedia.org/wiki/Mersenne_prime


8
이런 식으로 해시를 만들지 않을 것이라고 말합니다. int, float, Decimals, Fractions를 계산하는 것이 합리적으로 효율적 이고 유형간에 보증을 x == y보장 하는 방식으로 수행 할 수있는 방법에 대한 대체 제안이 hash(x) == hash(y)있습니까? (숫자는 좋아 Decimal('1e99999999')예를 들어, 특히 문제가 있습니다 : 당신이 해싱 전에 해당 정수로 그들을 밖으로 확장해야하고 싶지 않아요.)
마크 디킨슨

@MarkDickinson 나는 그가이 간단한 번개 빠른 해시와 출력을 무작위로 보이게하는 데 관심이있는 암호화 해시를 구별하려고 시도하고 있다고 생각합니다.
Mike Ounsworth 2016-06-03

4
@MarkDickinson 모듈러스는 좋은 시작이지만, 나는 그것을 더 섞을 것입니다. 특히 높은 비트를 낮은 비트로 혼합합니다. 2의 거듭 제곱으로 나눌 수있는 정수 시퀀스를 보는 것은 드문 일이 아닙니다. 또한 2의 거듭 제곱 인 용량을 가진 해시 테이블을 보는 것도 드문 일이 아닙니다. Java에서 예를 들어 16으로 나눌 수있는 정수 시퀀스가있는 경우 HashMap에서 키로 사용하면 버킷의 1/16 만 사용할 것입니다 (적어도 제가보고있는 소스 버전에서는)! 나는 해시가 조금 무작위로 보이는이 problerms을 방지하기 위해 비트 이상이어야한다고 생각
매트 Timmermans

예, 비트 믹싱 스타일 해시는 수학에서 영감을받은 해시보다 훨씬 우수합니다. 비트 믹싱 명령어는 너무 저렴해서 같은 비용으로 여러 개를 가질 수 있습니다. 또한 실제 데이터 에는 비트 믹싱에서 잘 작동 하지 않는 패턴이없는 것 같습니다 . 그러나 계수에 대해 끔찍한 패턴이 있습니다.
usr

9
@usr : 물론,하지만 약간 혼합 해시 여기 불가능하다 : 요구 사항에 대한 해시 작업 것을 int, float, DecimalFraction객체와 그 x == y의미 hash(x) == hash(y)심지어 때 xy다소 심각한 제약을 부과하는 다른 유형이있다. 다른 유형에 대해 걱정하지 않고 정수에 대한 해시 함수를 작성하는 문제라면 완전히 다른 문제가 될 것입니다.
Mark Dickinson

9

해시 함수는 반환 일반 INT 값을 반환 수단보다 크 -sys.maxint와보다 sys.maxint당신이 통과하면 어떤 수단 sys.maxint + x이 될 결과에를 -sys.maxint + (x - 2).

hash(sys.maxint + 1) == sys.maxint + 1 # False
hash(sys.maxint + 1) == - sys.maxint -1 # True
hash(sys.maxint + sys.maxint) == -sys.maxint + sys.maxint - 2 # True

그 사이 2**200n시간이 더 큽니다 . 위의 코드 스 니펫과 같이 해당 범위의 일반 정수에서 멈출 때까지 sys.maxint해시가 범위 -sys.maxint..+sys.maxintn 번을 넘어가는 것 같습니다.

따라서 일반적으로 n <= sys.maxint의 경우 :

hash(sys.maxint*n) == -sys.maxint*(n%2) +  2*(n%2)*sys.maxint - n/2 - (n + 1)%2 ## True

참고 : 이것은 python 2에 해당됩니다.


8
이것은 Python 2에 해당 될 수 있지만 Python 3에는 해당되지 않습니다 (가없고 sys.maxint다른 해시 함수를 사용함).
interjay 2012-06-03

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