파이썬에서 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, Decimal및 Fraction객체와 그 x == y의미 hash(x) == hash(y)심지어 때 x와 y다소 심각한 제약을 부과하는 다른 유형이있다. 다른 유형에 대해 걱정하지 않고 정수에 대한 해시 함수를 작성하는 문제라면 완전히 다른 문제가 될 것입니다.
— 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**200에 n시간이 더 큽니다 . 위의 코드 스 니펫과 같이 해당 범위의 일반 정수에서 멈출 때까지 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.