Tại sao hàm băm vô cực của Python có các chữ số là π?


241

Hàm băm vô cực trong Python có các chữ số khớp với pi :

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

Đó chỉ là một sự trùng hợp ngẫu nhiên hay là cố ý?


9
Không chắc chắn, nhưng tôi đoán sẽ là rằng nó như cố ý như hash(float('nan'))0.
cs95

1
Hmm, không đề cập đến điều đó trong sys.hash_info. Trứng Phục Sinh?
wim

123
Hỏi Tim Peters. Đây là cam kết nơi ông đã giới thiệu hằng số này, 19 năm trước: github.com/python/cpython/commit/ mẹo . Tôi đã giữ các giá trị đặc biệt đó khi tôi làm lại hàm băm số trong bug.python.org/su8188
Mark Dickinson

8
@MarkDickinson Cảm ơn. Có vẻ như Tim cũng đã sử dụng các chữ số của e cho hàm băm của -inf ban đầu.
wim

17
@wim À đúng rồi. Và rõ ràng tôi đã thay đổi điều đó thành -314159. Tôi đã quên mất điều đó.
Đánh dấu Dickinson

Câu trả lời:


47

_PyHASH_INFđược định nghĩa là một hằng số bằng 314159.

Tôi không thể tìm thấy bất kỳ cuộc thảo luận về điều này, hoặc ý kiến ​​đưa ra một lý do. Tôi nghĩ rằng nó đã được lựa chọn ít nhiều tùy ý. Tôi tưởng tượng rằng miễn là chúng không sử dụng cùng một giá trị có ý nghĩa cho các giá trị băm khác, thì điều đó không thành vấn đề.


6
Định nghĩa nhỏ: gần như không thể tránh khỏi định nghĩa rằng cùng một giá trị sẽ được sử dụng cho các giá trị băm khác, ví dụ trong trường hợp hash(314159)này cũng vậy 314159. Cũng thử, trong Python 3, hash(2305843009214008110) == 314159(đầu vào này là 314159 + sys.hash_info.modulus) v.v.
ShreevatsaR

3
@ShreevatsaR Tôi chỉ có nghĩa là miễn là họ không chọn giá trị này là giá trị băm của các giá trị khác theo định nghĩa, thì việc chọn một giá trị có ý nghĩa như thế này sẽ không làm tăng cơ hội va chạm băm
Patrick Haugh

220

Tóm tắt: Đó không phải là sự trùng hợp ngẫu nhiên; _PyHASH_INFđược mã hóa thành 314159 trong triển khai CPython mặc định của Python và được chọn là giá trị tùy ý (rõ ràng là từ các chữ số của π) bởi Tim Peters vào năm 2000 .


Giá trị của hash(float('inf'))là một trong các tham số phụ thuộc hệ thống của hàm băm tích hợp cho các kiểu số và cũng có sẵn như sys.hash_info.inftrong 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

(Kết quả tương tự với PyPy cũng vậy.)


Về mặt mã, hashlà một hàm tích hợp. Gọi nó trên một đối tượng phao Python gọi hàm mà con trỏ được đưa ra bởi các tp_hashthuộc tính của các loại built-in float ( PyTypeObject PyFloat_Type), mà các float_hashchức năng, định nghĩa như return _Py_HashDouble(v->ob_fval), mà lần lượt

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

trong đó _PyHASH_INFđược định nghĩa là 314159:

#define _PyHASH_INF 314159

Về mặt lịch sử, lần đầu tiên đề cập đến 314159trong bối cảnh này trong mã Python (bạn có thể tìm thấy điều này bằng git bisecthoặc git log -S 314159 -p) đã được Tim Peters thêm vào tháng 8 năm 2000, trong đó hiện đang cam kết 39dce293 trong cpythonkho git.

Thông báo cam kết cho biết:

Khắc phục sự cố cho http://sourceforge.net/bugs/?func=detailorms&orms_id=111866&group_id=5470 . Đây là một lỗi gây hiểu lầm - "lỗi" thực sự là hash(x)đã trả lại lỗi khi xlà vô cực. Đã sửa lỗi đó. Đã thêm Py_IS_INFINITYmacro mới vào pyport.h. Sắp xếp lại mã để giảm sự trùng lặp ngày càng tăng trong việc băm số float và số phức, đẩy cú đâm trước đó của Trent vào kết luận hợp lý. Đã sửa lỗi cực kỳ hiếm khi băm phao có thể trả về -1 ngay cả khi không có lỗi (không lãng phí thời gian để cố gắng xây dựng trường hợp thử nghiệm, đơn giản là rõ ràng từ mã có thể xảy ra). Cải thiện hàm băm phức tạp để hash(complex(x, y))không còn hệ thống bằng nhau hash(complex(y, x))nữa.

Đặc biệt, trong cam kết này, ông tách ra mã của static long float_hash(PyFloatObject *v)trong Objects/floatobject.cvà làm cho nó chỉ return _Py_HashDouble(v->ob_fval);, và trong định nghĩa của long _Py_HashDouble(double v)trong Objects/object.canh thêm các dòng:

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

Vì vậy, như đã đề cập, đó là một sự lựa chọn tùy ý. Lưu ý rằng 271828 được hình thành từ một vài chữ số thập phân đầu tiên của e .

Cam kết sau này:


44
Sự lựa chọn -271828 cho -Inf giúp loại bỏ mọi nghi ngờ rằng hiệp hội pi là tình cờ.
Russell Borogove

24
@RussellBorogove Không nhưng nó làm giảm khả năng khoảng một triệu lần;)
đường ống

8
@cmaster: Xem phần trên, nơi nó nói tháng 5 năm 2010, cụ thể là phần tài liệu về băm các loại sốvấn đề 8188 - ý tưởng là chúng tôi muốn hash(42.0)được giống như hash(42), cũng giống như hash(Decimal(42))hash(complex(42))hash(Fraction(42, 1)). Giải pháp (của Mark Dickinson) là một IMO tao nhã: xác định hàm toán học hoạt động với bất kỳ số hữu tỷ nào và sử dụng thực tế là các số có dấu phẩy động cũng là số hữu tỷ.
ShreevatsaR

1
@ShreevatsaR Ah, cảm ơn bạn. Mặc dù tôi sẽ không quan tâm để đảm bảo các đẳng thức này, nhưng thật tốt khi biết rằng có một lời giải thích hợp lý, vững chắc và hợp lý cho mã có vẻ phức tạp :-)
cmaster - khôi phục monica

2
@cmaster Hàm băm cho số nguyên chỉ đơn giản là hash(n) = n % MM = (2 ^ 61 - 1). Điều này được khái quát hóa cho n hợp lý hash(p/q) = (p/q) mod Mvới phép chia được giải thích modulo M (nói cách khác hash(p/q) = (p * inverse(q, M)) % M:). Lý do chúng tôi muốn điều này: nếu vào một lệnh dmà chúng tôi đưa ra d[x] = foovà sau đó chúng tôi có x==y(ví dụ 42.0 == 42) nhưng d[y]không giống như d[x], thì chúng tôi sẽ gặp vấn đề. Hầu hết các mã có vẻ phức tạp đều xuất phát từ bản chất của định dạng dấu phẩy động, để phục hồi phân số một cách chính xác và cần các trường hợp đặc biệt cho các giá trị inf và NaN.
ShreevatsaR

12

Thật,

sys.hash_info.inf

trả lại 314159. Giá trị không được tạo, nó được tích hợp vào mã nguồn. Trong thực tế,

hash(float('-inf'))

trả về -271828, hoặc xấp xỉ -e, trong python 2 ( bây giờ là -314159 ).

Việc hai số vô tỷ nổi tiếng nhất mọi thời đại được sử dụng làm giá trị băm khiến nó rất khó có thể là sự trùng hợp.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.