it-swarm.com.ru

Когда hash (n) == n в Python?

Я играл с Python хэш-функцией . Для маленьких целых чисел, это появляется 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)], чтобы оценить диапазон хэш-функции. Макс постоянно ниже п выше. Сравнивая min, кажется, что хэш Python 3 всегда имеет положительную ценность, тогда как хэш Python 2 может принимать отрицательные значения.

97
Colonel Panic

Основано на документации по Python в pyhash.c file:

Для числовых типов хеш числа x основан на сокращении x по модулю простого P = 2**_PyHASH_BITS - 1. Он спроектирован так, что hash(x) == hash(y) всякий раз, когда x и y численно равны, даже если х и у имеют разные типы.

Таким образом, для 64/32-битной машины сокращение составит 2 _PyHASH_BITS  - 1, но что такое _PyHASH_BITS?

Вы можете найти его в pyhash.h заголовочном файле, который для 64-битной машины был определен как 61 (более подробное объяснение можно прочитать в файле pyconfig.h).

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

Итак, в первую очередь это основано на вашей платформе, например, на моей 64-битной платформе Linux, сокращение составляет 261-1, это 2305843009213693951:

>>> 2**61 - 1
2305843009213693951

Также Вы можете использовать math.frexp для того, чтобы получить мантиссу и экспоненту sys.maxint, которая для 64-битной машины показывает, что max int равно 263

>>> 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
70
Kasrâmvd

2305843009213693951 - это 2^61 - 1. Это самое большое простое число Мерсенна, которое умещается в 64 бита.

Если вам нужно создать хеш, просто взяв значение mod некоторого числа, тогда большое простое число Мерсенна - хороший выбор - его легко вычислить и обеспечить равномерное распределение возможностей. (Хотя лично я бы никогда не сделал хэш таким способом)

Особенно удобно вычислять модуль для чисел с плавающей запятой. У них есть экспоненциальный компонент, который умножает целое число на 2^x. Поскольку 2^61 = 1 mod 2^61-1, вам нужно учитывать только (exponent) mod 61.

Смотрите: https://en.wikipedia.org/wiki/Mersenne_prime

77
Matt Timmermans

Хэш-функция возвращает plain 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.maxint n раз, пока не остановится на обычном целом числе в этом диапазоне, как в фрагментах кода выше ..

Так в общем случае для любого n <= sys.maxint :

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

Примечание: это верно для Python 2.

9
Andriy Ivaneyko

Реализацию для типа int в cpython можно найти здесь.

Он просто возвращает значение, за исключением -1, чем возвращает -2:

static long
int_hash(PyIntObject *v)
{
    /* XXX If this is changed, you also need to change the way
       Python's long, float and complex types are hashed. */
    long x = v -> ob_ival;
    if (x == -1)
        x = -2;
    return x;
}
0
Jieter