Cách thực hiện băm phao với đẳng thức gần đúng


15

Giả sử chúng ta có lớp Python sau (vấn đề tồn tại trong Java giống với equalshashCode)

class Temperature:
    def __init__(self, degrees):
        self.degrees = degrees

nơi degreeslà nhiệt độ trong Kelvin như một phao. Bây giờ, tôi muốn thực hiện kiểm tra và băm bình đẳng Temperaturetheo cách mà

  • so sánh nổi lên đến chênh lệch epsilon thay vì kiểm tra đẳng thức trực tiếp,
  • và tôn vinh hợp đồng a == bngụ ý hash(a) == hash(b).
def __eq__(self, other):
    return abs(self.degrees - other.degrees) < EPSILON

def __hash__(self):
    return # What goes here?

Tài liệu Python nói một chút về các số băm để đảm bảo điều đó hash(2) == hash(2.0)nhưng đây không hoàn toàn là vấn đề tương tự.

Tôi thậm chí đang đi đúng hướng? Và nếu vậy, cách tiêu chuẩn để thực hiện băm trong tình huống này là gì?

Cập nhật : Bây giờ tôi hiểu rằng loại kiểm tra đẳng thức cho phao này giúp loại bỏ tính siêu việt của ==equals. Nhưng làm thế nào mà đi cùng với "kiến thức chung" mà phao không nên được so sánh trực tiếp? Nếu bạn triển khai một toán tử đẳng thức bằng cách so sánh các float, các công cụ phân tích tĩnh sẽ khiếu nại. Họ có đúng không?


9
Tại sao câu hỏi có thẻ Java?
Laiv

8
Về cập nhật của bạn: Tôi sẽ nói rằng băm nổi thường là một điều đáng nghi ngờ. Cố gắng tránh sử dụng float làm khóa hoặc làm thành phần.
J. Fabian Meier

6
@Neil: Đồng thời, không làm tròn âm thanh như số nguyên? Điều đó có nghĩa là: nếu bạn có thể làm tròn đến, ví dụ, một phần nghìn độ, thì bạn có thể chỉ cần sử dụng biểu diễn điểm cố định - một số nguyên biểu thị nhiệt độ tính bằng phần nghìn độ. Để dễ sử dụng, bạn có thể có một getter / setter chuyển đổi trong suốt từ / sang float nếu bạn muốn ...
Matthieu M.

4
Kelvins không còn độ. Bằng cấp cũng mơ hồ. Tại sao không chỉ gọi nó kelvin?
Solomon Ucko

5
Python có hỗ trợ điểm cố định ít nhiều xuất sắc , có thể đó là thứ dành cho bạn.
Jonas Schäfer

Câu trả lời:


41

thực hiện kiểm tra đẳng thức và băm cho Nhiệt độ theo cách so sánh nổi lên với chênh lệch epsilon thay vì kiểm tra đẳng thức trực tiếp,

Bình đẳng mờ vi phạm các yêu cầu mà Java đặt ra cho equalsphương thức, cụ thể là tính siêu việt , tức là nếu x == yy == z, sau đó x == z. Nhưng nếu bạn làm một bình đẳng mờ với, ví dụ, một epsilon 0,1, sau đó 0.1 == 0.20.2 == 0.3, nhưng 0.1 == 0.3không giữ.

Mặc dù Python không ghi nhận một yêu cầu như vậy, nhưng những hệ lụy của việc có một đẳng thức không mang tính bắc cầu làm cho nó trở thành một ý tưởng rất tồi; lý do về các loại như vậy là gây đau đầu.

Vì vậy, tôi thực sự khuyên bạn không nên làm điều đó.

Hoặc cung cấp sự bình đẳng chính xác và dựa vào hàm băm của bạn theo cách rõ ràng và cung cấp một phương thức riêng để thực hiện so khớp mờ hoặc đi theo cách tiếp cận lớp tương đương do Kain đề xuất. Mặc dù trong trường hợp sau, tôi khuyên bạn nên sửa giá trị của mình cho một thành viên đại diện của lớp tương đương trong hàm tạo, và sau đó đi với sự bình đẳng chính xác đơn giản và băm cho phần còn lại; lý do này dễ dàng hơn nhiều về các loại theo cách này.

(Nhưng nếu bạn làm điều đó, bạn cũng có thể sử dụng biểu diễn điểm cố định thay vì dấu phẩy động, tức là bạn sử dụng một số nguyên để đếm một phần nghìn độ, hoặc bất kỳ độ chính xác nào bạn yêu cầu.)


2
những suy nghĩ thú vị. Vì vậy, bằng cách tích lũy hàng triệu epsilon và với tính siêu việt, bạn có thể kết luận rằng mọi thứ đều bằng bất cứ thứ gì khác :-) Nhưng ràng buộc toán học này có thừa nhận nền tảng rời rạc của các điểm nổi, trong nhiều trường hợp là xấp xỉ số mà chúng dự định đại diện không?
Christophe

@Christophe Câu hỏi thú vị. Nếu bạn nghĩ về nó, bạn sẽ thấy rằng cách tiếp cận này sẽ tạo ra một lớp tương đương lớn duy nhất ngoài phao có độ phân giải lớn hơn epsilon (dĩ nhiên là tập trung vào 0) và để các phao khác trong lớp riêng của chúng. Nhưng đó không phải là vấn đề, vấn đề thực sự là liệu nó có kết luận rằng 2 số có bằng nhau hay không phụ thuộc vào việc có một số thứ ba được so sánh và thứ tự được thực hiện hay không.
Ordous

Giải quyết vấn đề chỉnh sửa của @ OP, tôi sẽ nói thêm rằng tính không chính xác của dấu phẩy động ==sẽ "lây nhiễm" các ==loại có chứa chúng. Đó là, nếu họ làm theo lời khuyên của bạn về việc cung cấp một đẳng thức chính xác, thì công cụ phân tích tĩnh của họ sẽ được cấu hình thêm để cảnh báo khi sử dụng đẳng thức Temperature. Đó là điều duy nhất bạn có thể làm, thực sự.
HTNW

@HTNW: Điều đó quá đơn giản. Một lớp tỷ lệ có thể có một float approximationlĩnh vực không tham gia ==. Ngoài ra, công cụ phân tích tĩnh sẽ đưa ra cảnh báo bên trong việc ==triển khai các lớp khi một trong các thành viên được so sánh là một floatloại.
MSalters

@MSalters? Có lẽ, các công cụ phân tích tĩnh đủ cấu hình có thể làm những gì tôi đề xuất là tốt. Nếu một lớp có một floattrường không tham gia ==, thì đừng cấu hình công cụ của bạn để cảnh báo về ==lớp đó. Nếu lớp đó xảy ra, thì có lẽ việc đánh dấu lớp ==là "quá chính xác" sẽ khiến công cụ bỏ qua loại lỗi đó trong quá trình thực hiện. Ví dụ, trong Java, nếu @Deprecated void foo(), thì void bar() { foo(); }là một cảnh báo, nhưng @Deprecated void bar() { foo(); }không phải. Có thể nhiều công cụ không hỗ trợ điều này, nhưng một số có thể.
HTNW

16

Chúc may mắn

Bạn sẽ không thể đạt được điều đó, mà không ngu ngốc với băm, hoặc hy sinh epsilon.

Thí dụ:

Giả sử rằng mỗi điểm băm đến giá trị băm duy nhất của riêng nó.

Vì các số dấu phẩy động là tuần tự, sẽ có tối đa k số trước một giá trị dấu phẩy động cho trước và tối đa k số sau một giá trị dấu phẩy động đã cho nằm trong một số epsilon của điểm đã cho.

  1. Đối với mỗi hai điểm trong epsilon của nhau không chia sẻ cùng một giá trị băm.

    • Điều chỉnh sơ đồ băm sao cho hai điểm này băm đến cùng một giá trị.
  2. Việc quy nạp cho tất cả các cặp như vậy toàn bộ chuỗi số dấu phẩy động sẽ thu gọn về phía một giá trị duy nhất.

Có một vài trường hợp điều này sẽ không đúng:

  • Vô cực tích cực / tiêu cực
  • NaN
  • Một vài phạm vi Không chuẩn hóa có thể không liên kết được với phạm vi chính cho một epsilon nhất định.
  • có lẽ một vài trường hợp cụ thể định dạng khác

Tuy nhiên> = 99% phạm vi điểm nổi sẽ băm thành một giá trị duy nhất cho bất kỳ giá trị nào của epsilon bao gồm ít nhất một giá trị dấu phẩy động trên hoặc dưới một số giá trị dấu phẩy động đã cho.

Kết quả

Hoặc> = 99% toàn bộ phạm vi dấu phẩy động băm đến một giá trị duy nhất nghiêm trọng hóa ý định của giá trị băm (và bất kỳ thiết bị / vùng chứa nào dựa vào hàm băm va chạm thấp phân tán).

Hoặc epsilon là như vậy chỉ cho phép khớp chính xác.

Dạng hạt

Bạn tất nhiên có thể đi cho một cách tiếp cận chi tiết thay thế.

Theo cách tiếp cận này, bạn xác định các nhóm chính xác xuống một độ phân giải cụ thể. I E:

[0.001, 0.002)
[0.002, 0.003)
[0.003, 0.004)
...
[122.999, 123.000)
...

Mỗi nhóm có một hàm băm duy nhất và bất kỳ điểm nổi nào trong nhóm so sánh với bất kỳ số float nào khác trong cùng một nhóm.

Thật không may, vẫn có thể có hai phao cách xa epsilon và có hai băm riêng biệt.


2
Tôi đồng ý rằng phương pháp chi tiết ở đây có lẽ sẽ tốt nhất, nếu phù hợp với yêu cầu của OP. Mặc dù tôi sợ OP có các yêu cầu loại +/- 0,1%, có nghĩa là nó không thể ở dạng chi tiết.
Neil

4
@DocBrown Phần "không thể" là chính xác. Nếu đẳng thức dựa trên epsilon có nghĩa là các mã băm bằng nhau, thì bạn tự động có tất cả các mã băm bằng nhau, do đó hàm băm không còn hữu ích nữa. Cách tiếp cận xô có thể có kết quả, nhưng bạn sẽ có các số với các mã băm khác nhau gần nhau tùy ý.
J. Fabian Meier

2
Cách tiếp cận xô có thể được sửa đổi bằng cách kiểm tra không chỉ xô với khóa băm chính xác, mà cả hai nhóm hàng xóm (hoặc ít nhất một trong số chúng) cho nội dung của chúng. Điều đó giúp loại bỏ vấn đề của các trường hợp cạnh đó đối với chi phí tăng thời gian chạy theo hệ số nhiều nhất là hai (khi được thực hiện đúng). Tuy nhiên, nó không thay đổi thứ tự thời gian chạy chung.
Doc Brown

Trong khi bạn đúng về tinh thần, không phải mọi thứ sẽ sụp đổ. Với một epsilon nhỏ cố định, hầu hết các số sẽ chỉ bằng nhau. Tất nhiên, đối với những người mà epsilon sẽ là vô dụng, vì vậy một lần nữa, về tinh thần bạn là chính xác.
Carsten S

1
@CarstenS Có, tuyên bố của tôi rằng 99% phạm vi băm cho một hàm băm duy nhất không thực sự bao gồm toàn bộ phạm vi nổi. Có nhiều giá trị phạm vi cao được phân tách bằng nhiều hơn epsilon sẽ băm vào các nhóm độc đáo của riêng chúng.
Kain0_0

7

Bạn có thể mô hình nhiệt độ của bạn dưới dạng một số nguyên dưới mui xe. Nhiệt độ có giới hạn dưới tự nhiên (-273,15 độ C). Vì vậy, nhân đôi (-273,15 bằng 0 cho số nguyên cơ bản của bạn). Yếu tố thứ hai mà bạn cần là độ chi tiết của ánh xạ. Bạn đang sử dụng mức độ chi tiết này một cách ngầm định; đó là EPSILON của bạn.

Chỉ cần chia nhiệt độ của bạn cho EPSILON và lấy sàn của nó, bây giờ hàm băm và số bằng của bạn sẽ hoạt động đồng bộ. Trong Python 3, số nguyên không bị ràng buộc, EPSILON có thể nhỏ hơn nếu bạn muốn.

THƯỞNG Nếu bạn thay đổi giá trị của EPSILON và bạn đã tuần tự hóa đối tượng thì chúng sẽ không tương thích!

#Pseudo code
class Temperature:
    def __init__(self, degrees):
        #CHECK INVALID VALUES HERE
        #TRANSFORM TO KELVIN HERE
        self.degrees = Math.floor(kelvin/EPSILON)

1

Việc triển khai bảng băm dấu phẩy động có thể tìm thấy những thứ "gần bằng" với một khóa đã cho sẽ yêu cầu sử dụng một vài cách tiếp cận hoặc kết hợp chúng:

  1. Làm tròn mỗi giá trị thành một số gia lớn hơn một chút so với phạm vi "mờ" trước khi lưu nó vào bảng băm và khi cố gắng tìm giá trị, hãy kiểm tra bảng băm để biết các giá trị được làm tròn ở trên và dưới giá trị cần tìm.

  2. Lưu trữ từng mục trong bảng băm bằng các khóa ở trên và dưới giá trị được tìm kiếm.

Lưu ý rằng việc sử dụng một trong hai cách tiếp cận có thể sẽ yêu cầu các mục trong bảng băm không xác định các mục, mà là danh sách, vì có thể sẽ có nhiều mục được liên kết với mỗi khóa. Cách tiếp cận đầu tiên ở trên sẽ giảm thiểu kích thước bảng băm cần thiết, nhưng mỗi tìm kiếm cho một mục không có trong bảng sẽ yêu cầu hai lần tra cứu bảng băm. Cách tiếp cận thứ hai sẽ nhanh chóng có thể xác định rằng các mặt hàng không có trong bảng, nhưng nhìn chung sẽ yêu cầu bảng giữ khoảng gấp đôi số lượng mục nếu không được yêu cầu. Nếu một người đang cố gắng tìm các đối tượng trong không gian 2D, có thể hữu ích khi sử dụng một cách tiếp cận cho hướng X và một cho hướng Y, để thay vì mỗi mục được lưu trữ một lần nhưng yêu cầu bốn thao tác truy vấn cho mỗi lần tra cứu hoặc là có thể sử dụng một tra cứu để tìm một mục nhưng phải lưu trữ mỗi mục bốn lần,


0

Tất nhiên, bạn có thể định nghĩa gần như bằng nhau bằng cách xóa tám bit cuối cùng của lớp phủ và sau đó so sánh hoặc băm. Vấn đề là những con số rất gần nhau thể khác nhau.

Có một số nhầm lẫn ở đây: nếu hai số dấu phẩy động so sánh bằng nhau, chúng bằng nhau. Để kiểm tra xem chúng có bằng nhau không, bạn sử dụng dịch vụ ==. Đôi khi bạn không muốn kiểm tra sự bình đẳng, nhưng khi bạn làm thế, thì == là cách để đi.


0

Đây không phải là một câu trả lời, nhưng một nhận xét mở rộng có thể hữu ích.

Tôi đã làm việc với một vấn đề tương tự, trong khi sử dụng MPFR (dựa trên GNU MP). Cách tiếp cận "xô" như được phác thảo bởi @ Kain0_0 dường như cho kết quả chấp nhận được, nhưng hãy lưu ý đến những hạn chế được nêu trong câu trả lời đó.

Tôi muốn thêm rằng - tùy thuộc vào những gì bạn đang cố gắng thực hiện - sử dụng hệ thống đại số máy tính "chính xác" ( caveat emptor ) như Mathicala có thể giúp bổ sung hoặc xác minh chương trình số không chính xác. Điều này sẽ cho phép bạn tính toán kết quả mà không phải lo lắng về làm tròn, ví dụ, 7*√2 - 5*√2sẽ mang lại 2thay vì 2.00000001hoặc tương tự. Tất nhiên, điều này sẽ giới thiệu các biến chứng bổ sung có thể có hoặc không có giá trị.

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.