Чому хеш нескінченності Python має цифри π?


241

Хеш нескінченності в Python має цифри, відповідні pi :

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

Це просто збіг чи це навмисне?


9
Не певне, але я гадаю, що це так само навмисно, як і hash(float('nan'))буття 0.
cs95

1
Хм, жодної згадки про це в sys.hash_info. Пасхальне яйце?
Вім

123
Запитайте Тіма Пітерса. Ось комітет, де він представив цю константу 19 років тому: github.com/python/cpython/commit/… . Я зберігав ці особливі значення, коли переробляв числовий хеш у bugs.python.org/issue8188
Марк Дікінсон

8
@MarkDickinson Дякую Схоже, Тім, можливо, спочатку використовував цифри е для хеш -інф.
Вім

17
@wim Ага так, правда. І, мабуть, я це змінив -314159. Я забув про це.
Марк Дікінсон

Відповіді:


47

_PyHASH_INFбуде визначено як константа , яка дорівнює314159 .

Я не можу знайти жодної дискусії з цього приводу або коментарів, що дають причину. Я думаю, що його обирали більш-менш довільно. Я гадаю, що поки вони не використовують однакове значення для інших хешів, це не має значення.


6
Маленький нітпік: за визначенням майже неминуче те саме значення буде використано і для інших хешей, наприклад, у цьому випадку hash(314159)також 314159. Також спробуйте, в Python 3, hash(2305843009214008110) == 314159(цей вхід є 314159 + sys.hash_info.modulus) тощо
ShreevatsaR

3
@ShreevatsaR Я просто мав на увазі, що доки вони не виберуть це значення як хеш інших значень за визначенням, то вибір значущого значення, як це, не збільшує шансів хеш-зіткнень
Патрік

220

Резюме: Це не випадковість; у впровадженні CPython Python за замовчуванням _PyHASH_INFжорстко кодується як 314159 , і Тим Петерс у 2000 році був обраний як довільне значення (очевидно, з цифр π) .


Значення hash(float('inf'))є одним із системно-залежних параметрів вбудованої хеш-функції для числових типів, а також доступне, як sys.hash_info.infу Python 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це вбудована функція. Виклик цього об'єкта флоат Python запуститися функція, покажчик задається 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;

де _PyHASH_INFбуде визначена як 314159:

#define _PyHASH_INF 314159

З точки зору історії, перше згадування 314159цього контексту в коді Python (ви можете знайти це за допомогою git bisectабо git log -S 314159 -p) було додано Тімом Петерсом у серпні 2000 року в тому, що зараз фіксується 39dce293 у cpythonсховищі git.

У повідомленні про виконання зазначено:

Виправити http://sourceforge.net/bugs/?func=detailbug&bug_id=111866&group_id=5470 . Це була помилкова помилка - справжня "помилка" була тим, що hash(x)дала помилку повернення, коли xце нескінченність. Виправлено це. Додано новий Py_IS_INFINITYмакрос pyport.h. Впорядкований код для зменшення дублювання у хешировании float і комплексних чисел, підштовхуючи попередній удар 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 утворюється з перших кількох десяткових цифр е .

Пов’язані пізніші зобов’язання:


44
Вибір -271828 для -Inf усуває будь-які сумніви в тому, що пі асоціація була випадковою.
Рассел Борогов

24
@RussellBorogove Ні, але це робить це приблизно в мільйон разів менше;)
труба

8
@cmaster: Дивіться частину вище, де написано травень 2010 року, а саме розділ документації щодо хешування числових типів та номер 8188 - ідея полягає в тому, що ми хочемо hash(42.0)бути такими ж hash(42), також такими ж, як hash(Decimal(42))і hash(complex(42))і hash(Fraction(42, 1)). Рішення (за Марком Дікінсоном) - це елегантний ІМО: визначення математичної функції, яка працює для будь-якого раціонального числа, та використання факту, що числа з плаваючою комою є і раціональними числами.
ShreevatsaR

1
@ShreevatsaR Ах, дякую. Хоча я б не піклувався про те, щоб гарантувати ці рівності, добре знати, що існує гарне, міцне і логічне пояснення, здавалося б, складного коду :-)
cmaster - відновити

2
@cmaster Хеш-функція для цілих чисел просто hash(n) = n % Mтам, де M = (2 ^ 61 - 1). Це узагальнено для раціонального n до hash(p/q) = (p/q) mod Mтого, як інтерпретується поділ за модулем M (іншими словами hash(p/q) = (p * inverse(q, M)) % M:). Причина, яку ми хочемо так: якщо dми поставимо в дікт 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, або приблизно -e, в python 2 ( зараз -314159 ).

Той факт, що два найвідоміші ірраціональні числа усіх часів використовуються як хешові значення, робить його малоймовірним випадковістю.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.