วิธีใช้การแฮชแบบลอยด้วยความเท่าเทียมกันโดยประมาณ


15

สมมติว่าเรามีคลาส Python ดังต่อไปนี้ (ปัญหามีอยู่ใน Java เช่นเดียวกับequalsและhashCode)

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

ที่degreesเป็นอุณหภูมิเคลวินเป็นลอยที่ ตอนนี้ฉันต้องการที่จะใช้การทดสอบความเท่าเทียมกันและ hashing Temperatureในทางที่

  • เปรียบเทียบลอยขึ้นกับความแตกต่าง epsilon แทนการทดสอบความเท่าเทียมกันโดยตรง
  • และได้รับเกียรตินิยมสัญญาว่าหมายถึงa == bhash(a) == hash(b)
def __eq__(self, other):
    return abs(self.degrees - other.degrees) < EPSILON

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

เอกสาร Python พูดถึงตัวเลขการแฮชเล็กน้อยเพื่อให้แน่ใจว่าhash(2) == hash(2.0)นี่ไม่ใช่ปัญหาเดียวกัน

ฉันยังอยู่ในเส้นทางที่ถูกต้องหรือไม่? และถ้าเป็นเช่นนั้นวิธีมาตรฐานในการใช้การแฮชในสถานการณ์นี้คืออะไร?

ปรับปรุง : ตอนนี้ผมเข้าใจว่าชนิดของการทดสอบความเสมอภาคลอยนี้จะช่วยลดกริยาของและ== equalsแต่วิธีการที่จะไปพร้อมกับ "ความรู้ทั่วไป" ที่ลอยไม่ควรเปรียบเทียบโดยตรง? หากคุณใช้ตัวดำเนินการความเท่าเทียมกันโดยการเปรียบเทียบลอยเครื่องมือวิเคราะห์แบบคงที่จะบ่น พวกเขาถูกต้องหรือไม่


9
ทำไมคำถามมีแท็กของจาวา
Laiv

8
เกี่ยวกับการอัปเดตของคุณ: ฉันจะบอกว่าโดยปกติแล้วการแฮ็ชแบบลอยนั้นเป็นสิ่งที่น่าสงสัย พยายามหลีกเลี่ยงการใช้ลอยเป็นกุญแจหรือเป็นองค์ประกอบที่ตั้งไว้
J. Fabian Meier

6
@ Neil: ในเวลาเดียวกันไม่ปัดเศษเสียงเหมือนจำนวนเต็ม? โดยที่ฉันหมายถึง: หากคุณสามารถปัดเศษไปพูดพันองศาแล้วคุณก็สามารถใช้การแทนจุดคงที่ - จำนวนเต็มแสดงอุณหภูมิในพันองศา เพื่อความสะดวกในการใช้งานคุณสามารถมี getter / setter แปลงจาก / เป็น float อย่างโปร่งใสหากคุณต้องการ ...
Matthieu M.

4
เคลวินไม่มีองศาอีกต่อไป องศาก็ยังคลุมเครือ ทำไมไม่เรียกมันว่าkelvinอะไร?
โซโลมอน Ucko

คำตอบ:


41

ใช้การทดสอบความเสมอภาคและการ hashing สำหรับอุณหภูมิในแบบที่เปรียบเทียบกับความแตกต่างของเอปไซลอนลอยแทนการทดสอบความเท่าเทียมกันโดยตรง

ความเท่าเทียมกันเลือนฝ่าฝืนข้อกำหนดที่ Java วางบนequalsวิธีคือกริยาคือว่าถ้าx == yและแล้วy == z x == zแต่ถ้าคุณทำความเท่าเทียมกันแบบฟัซซี่ด้วยเช่น epsilon ของ 0.1 แล้ว0.1 == 0.2และ0.2 == 0.3แต่0.1 == 0.3ไม่ถือ

ในขณะที่ Python ไม่ได้จัดทำเอกสารข้อกำหนดดังกล่าว แต่ความหมายของการมีความเท่าเทียมกันที่ไม่เกี่ยวกับสกรรมกริยาทำให้มันเป็นความคิดที่แย่มาก การให้เหตุผลเกี่ยวกับประเภทดังกล่าวทำให้ปวดศีรษะ

ดังนั้นฉันขอแนะนำว่าอย่าทำอย่างนั้น

จัดเตรียมความเสมอภาคที่แน่นอนและตั้งค่าแฮชของคุณตามวิธีที่เห็นได้ชัดและจัดเตรียมวิธีแยกต่างหากเพื่อทำการจับคู่แบบคลุมเครือหรือไปกับวิธีการเรียนเทียบเท่าที่ Kain แนะนำ แม้ว่าในกรณีหลังนี้ฉันขอแนะนำให้คุณกำหนดค่าของคุณให้กับสมาชิกตัวแทนของคลาสความเท่าเทียมกันในตัวสร้างและจากนั้นไปด้วยความเท่าเทียมกันที่เรียบง่ายและถูกต้องสำหรับส่วนที่เหลือ มันง่ายกว่าที่จะให้เหตุผลเกี่ยวกับประเภทของวิธีนี้

(แต่ถ้าคุณทำเช่นนั้นคุณอาจใช้การแทนจุดคงที่แทนจุดลอยตัวเช่นคุณใช้จำนวนเต็มนับพันองศาหรือความแม่นยำที่คุณต้องการ)


2
ความคิดที่น่าสนใจ ดังนั้นด้วยการสะสมเอปไซลอนหลายล้านตัวและด้วยความสลับซับซ้อนคุณสามารถสรุปได้ว่าสิ่งใดมีค่าเท่ากับอะไร :-) แต่ข้อ จำกัด ทางคณิตศาสตร์นี้ยอมรับรากฐานที่ไม่ต่อเนื่องของคะแนนลอยตัวซึ่งในหลายกรณีเป็นการประมาณจำนวนที่พวกมันต้องการแสดง
Christophe

@Christophe คำถามที่น่าสนใจ หากคุณคิดเกี่ยวกับมันคุณจะเห็นว่าวิธีการนี้จะทำให้คลาสสมมูลขนาดใหญ่หนึ่งชั้นออกมาจากโฟลตซึ่งความละเอียดสูงกว่า epsilon (แน่นอนว่าอยู่ที่ 0 และอยู่กึ่งกลางของคลาส) และปล่อยให้โฟลตอื่น ๆ แต่นั่นไม่ใช่ประเด็นปัญหาที่แท้จริงคือว่าไม่ว่าจะสรุปว่าตัวเลข 2 หมายเลขนั้นเท่ากันหรือไม่นั้นขึ้นอยู่กับว่ามีหนึ่งในสามที่เปรียบเทียบและลำดับที่ทำ
Ordous

การแก้ไขที่อยู่ของ @ OP ฉันจะเพิ่มความไม่ถูกต้องของ floating-point ==ควร "ติด" ==ประเภทที่มีพวกมัน Temperatureนั่นคือถ้าพวกเขาปฏิบัติตามคำแนะนำของคุณในการให้ความเสมอภาคที่แน่นอนแล้วเครื่องมือในการวิเคราะห์ของพวกเขาคงควรเพิ่มเติมกำหนดค่าให้เตือนเมื่อความเท่าเทียมกันถูกนำมาใช้ในการ มันเป็นสิ่งเดียวที่คุณสามารถทำได้จริง ๆ
HTNW

@HTNW: นั่นจะง่ายเกินไป ชั้นอัตราส่วนอาจมีข้อมูลที่ไม่ได้มีส่วนร่วมในfloat approximation ==นอกจากนี้เครื่องมือการวิเคราะห์แบบสแตติกจะให้คำเตือนแล้วในการ==ใช้งานของชั้นเรียนเมื่อหนึ่งในสมาชิกที่ถูกเปรียบเทียบเป็นfloatประเภท
MSalters

@MSalters? เครื่องมือการวิเคราะห์สแตติกที่กำหนดค่าได้อย่างเพียงพอน่าจะสามารถทำสิ่งที่ฉันแนะนำได้ดี หากคลาสมีfloatฟิลด์ที่ไม่ได้เข้าร่วม==อย่ากำหนดค่าเครื่องมือของคุณเพื่อเตือนใน==คลาสนั้น หากคลาสทำเช่นนั้นการทำเครื่องหมายคลาส==ว่า "แน่นอนเกินไป" จะทำให้เครื่องมือไม่สนใจข้อผิดพลาดประเภทนั้นในการนำไปใช้ เช่นใน Java ถ้า@Deprecated void foo()แล้วvoid bar() { foo(); }เป็นคำเตือน แต่@Deprecated void bar() { foo(); }ไม่ได้เป็น บางทีเครื่องมือมากมายอาจไม่รองรับสิ่งนี้ แต่อาจมีบางอย่าง
HTNW

16

โชคดี

คุณจะไม่สามารถบรรลุเป้าหมายนั้นได้โดยไม่ต้องงี่เง่ากับแฮ็กหรือเสียสละเอปไซลอน

ตัวอย่าง:

สมมติว่าแต่ละจุดแฮชกับค่าแฮชที่เป็นเอกลักษณ์ของตนเอง

เนื่องจากตัวเลขจุดลอยตัวต่อเนื่องกันจะมีตัวเลขมากถึง k ก่อนหน้าค่าจุดลอยตัวที่กำหนดและสูงสุดถึง k ตัวเลขหลังจากค่าจุดลอยตัวที่กำหนดซึ่งอยู่ภายใน epsilon ของจุดที่กำหนด

  1. สำหรับแต่ละจุดภายใน epsilon ของกันและกันที่ไม่ได้มีค่าแฮชเดียวกัน

    • ปรับรูปแบบการแฮชเพื่อให้แฮชสองจุดนี้เป็นค่าเดียวกัน
  2. การเหนี่ยวนำสำหรับคู่ดังกล่าวทั้งหมดลำดับของจำนวนจุดลอยตัวจะยุบลงไปในค่าเดียว

มีบางกรณีที่สิ่งนี้จะไม่ถือเป็นจริง:

  • บวก / ลบไม่มีที่สิ้นสุด
  • น่าน
  • ช่วง De-Normalized บางช่วงที่อาจไม่สามารถเชื่อมโยงไปยังช่วงหลักสำหรับ epsilon ที่กำหนดได้
  • บางทีอินสแตนซ์ที่เฉพาะเจาะจงของรูปแบบอื่น ๆ

อย่างไรก็ตาม> = 99% ของช่วงจำนวนจุดลอยตัวจะแฮชเป็นค่าเดียวสำหรับค่าใด ๆ ของ epsilon ที่มีค่าทศนิยมอย่างน้อยหนึ่งค่าที่สูงกว่าหรือต่ำกว่าค่าบางจุดที่ได้รับ

ผล

ทั้ง> = 99% ช่วงจุดลอยตัวทั้งหมดแฮชเป็นค่าเดียวอย่างจริงจังซึ่งประกอบด้วยเจตนาของค่าแฮช (และอุปกรณ์ / คอนเทนเนอร์ใด ๆ อาศัยแฮชการชนกันของการกระจายที่ค่อนข้างต่ำ)

หรือเอปไซลอนนั้นอนุญาตให้ใช้ได้เฉพาะการแข่งขันที่แน่นอนเท่านั้น

เป็นเม็ด

แน่นอนคุณสามารถไปหาวิธีที่ละเอียดแทน

ภายใต้วิธีนี้คุณจะกำหนดที่แน่นอนลงไปที่ความละเอียดเฉพาะ เช่น:

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

ที่เก็บข้อมูลแต่ละอันจะมีแฮชที่ไม่ซ้ำกันและจุดลอยตัวใด ๆ ในที่เก็บข้อมูลนั้นเปรียบได้กับการลอยตัวอื่น ๆ ในที่เก็บเดียวกัน

น่าเสียดายที่มันยังมีความเป็นไปได้ที่ทุ่นลอยสองอันจะห่างจากเอปไซลอนและมีแฮชสองอันแยกกัน


2
ฉันยอมรับว่าแนวทางแบบละเอียดที่นี่น่าจะดีที่สุดหากเหมาะสมกับความต้องการของ OP แม้ว่าฉันเกรงว่า OP จะมีข้อกำหนดชนิด +/- 0.1% แต่ก็ไม่สามารถเป็นเม็ดละเอียดได้
Neil

4
@DocBrown ส่วน "ไม่สามารถทำได้" ถูกต้อง หาก epsilon ที่ใช้ความเท่าเทียมกันควรบ่งบอกว่ารหัสแฮชเท่ากันคุณก็จะมีรหัสแฮชทั้งหมดโดยอัตโนมัติดังนั้นฟังก์ชันแฮชจะไม่เป็นประโยชน์อีกต่อไป วิธีการฝากข้อมูลอาจมีผล แต่คุณจะมีหมายเลขที่มีรหัสแฮชที่แตกต่างกันซึ่งอยู่ใกล้กันโดยพลการ
J. Fabian Meier

2
วิธีการฝากข้อมูลสามารถแก้ไขได้โดยการตรวจสอบไม่เพียง แต่ที่ฝากข้อมูลด้วยคีย์แฮชที่แน่นอน แต่ยังมีที่เก็บข้อมูลที่อยู่ใกล้เคียงสองอัน (หรืออย่างน้อยหนึ่งแห่ง) สำหรับเนื้อหาของพวกเขาเช่นกัน ซึ่งช่วยลดปัญหาของเคสขอบเหล่านั้นสำหรับค่าใช้จ่ายในการเพิ่มเวลาทำงานโดยปัจจัยอย่างมากที่สุดสองตัว (เมื่อใช้อย่างถูกต้อง) อย่างไรก็ตามจะไม่เปลี่ยนลำดับเวลาการทำงานทั่วไป
Doc Brown

ในขณะที่คุณอยู่ในจิตวิญญาณทุกอย่างจะไม่ยุบ ด้วย epsilon ขนาดเล็กที่ตายตัวตัวเลขส่วนใหญ่จะเท่ากันเท่านั้น แน่นอนสำหรับเอปไซลอนเหล่านั้นจะไร้ประโยชน์ดังนั้นในจิตวิญญาณของคุณว่าถูกต้อง
Carsten S

1
@ CarstenS ใช่ข้อความของฉันที่ 99% ของช่วงแฮชไปที่แฮชเดียวไม่ได้ครอบคลุมช่วงโฟลตทั้งหมด มีค่าช่วงสูงจำนวนมากที่แยกจากกันโดยมากกว่า epsilon ที่จะแฮ็คกับถังที่ไม่ซ้ำกันของตัวเอง
Kain0_0

7

คุณสามารถสร้างแบบจำลองอุณหภูมิของคุณเป็นจำนวนเต็มภายใต้ประทุน อุณหภูมิมีขอบเขตล่างตามธรรมชาติ (-273.15 องศาเซลเซียส) ดังนั้น double (-273.15 เท่ากับ 0 สำหรับจำนวนเต็มพื้นฐานของคุณ) องค์ประกอบที่สองที่คุณต้องการคือความละเอียดในการจับคู่ของคุณ คุณกำลังใช้ความละเอียดระดับนี้โดยปริยายอยู่แล้ว มันคือ EPSILON ของคุณ

เพียงแค่แบ่งอุณหภูมิของคุณด้วย EPSILON แล้วนำส่วนแบ่งของมันตอนนี้แฮชของคุณและค่าที่เท่ากันของคุณจะทำงานในการซิงค์ ใน Python 3 จำนวนเต็มไม่ จำกัด EPSILON อาจมีขนาดเล็กลงหากคุณต้องการ

ระวัง ถ้าคุณเปลี่ยนค่าของ EPSILON และคุณได้ต่อเนื่องวัตถุพวกเขาจะเข้ากันไม่ได้!

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

1

การใช้ตารางแฮชแบบ floating-point ที่สามารถค้นหาสิ่งที่ "โดยประมาณเท่ากับ" กับคีย์ที่กำหนดจะต้องใช้วิธีการสองสามอย่างหรือการรวมกันของมัน:

  1. ปัดเศษแต่ละค่าเป็นส่วนเพิ่มซึ่งค่อนข้างใหญ่กว่าช่วง "fuzzy" ก่อนเก็บไว้ในตารางแฮชและเมื่อพยายามค้นหาค่าให้ตรวจสอบตารางแฮชสำหรับค่าที่ถูกปัดเศษด้านบนและด้านล่างของค่าที่ต้องการ

  2. เก็บแต่ละรายการไว้ในตารางแฮชโดยใช้ปุ่มที่อยู่ด้านบนและด้านล่างของค่าที่ต้องการ

โปรดทราบว่าการใช้วิธีใดวิธีหนึ่งอาจต้องการรายการตารางแฮชที่ไม่ได้ระบุรายการ แต่เป็นรายการเนื่องจากจะมีหลายรายการที่เชื่อมโยงกับแต่ละคีย์ วิธีแรกข้างต้นจะลดขนาดตารางแฮชที่ต้องการให้น้อยที่สุด แต่การค้นหารายการที่ไม่อยู่ในตารางแต่ละรายการจะต้องใช้การค้นหาตารางแฮชสองรายการ วิธีที่สองจะสามารถระบุได้อย่างรวดเร็วว่ารายการไม่ได้อยู่ในตาราง แต่โดยทั่วไปแล้วจะต้องมีตารางที่จะถือรายการประมาณสองเท่าเท่าที่จำเป็น หากมีใครพยายามหาวัตถุในพื้นที่ 2 มิติมันอาจจะมีประโยชน์ในการใช้วิธีการหนึ่งอย่างสำหรับทิศทาง X และอีกทิศทางหนึ่งสำหรับทิศทาง Y ดังนั้นแทนที่จะเก็บแต่ละรายการไว้เพียงครั้งเดียว แต่ต้องการการดำเนินการค้นหาสี่ครั้งสำหรับแต่ละการค้นหา สามารถใช้การค้นหาเดียวเพื่อค้นหารายการ แต่ต้องเก็บแต่ละรายการสี่ครั้ง


0

แน่นอนว่าคุณสามารถนิยาม“ เกือบเท่ากับ” ได้โดยการลบคำพูดของ mantissa แปดบิตสุดท้ายแล้วเปรียบเทียบหรือแปลงข้อมูล ปัญหาคือตัวเลขที่อยู่ใกล้กันอาจแตกต่างกัน

มีความสับสนอยู่ที่นี่: ถ้าตัวเลขทศนิยมสองตัวเปรียบเทียบกันพวกเขาเท่ากัน เพื่อตรวจสอบว่าพวกเขาเท่ากันคุณใช้“ ==“ บางครั้งคุณไม่ต้องการตรวจสอบความเท่าเทียมกัน แต่เมื่อคุณทำ“ ==“ เป็นวิธีที่จะไป


0

นี่ไม่ใช่คำตอบ แต่ความคิดเห็นเพิ่มเติมที่อาจมีประโยชน์

ฉันทำงานเกี่ยวกับปัญหาที่คล้ายกันขณะใช้MPFR (อิง GNU MP) วิธีการ "ฝากข้อมูล" ตามที่ระบุโดย @ Kain0_0 ดูเหมือนจะให้ผลลัพธ์ที่ยอมรับได้ แต่ระวังข้อ จำกัด ที่เน้นไว้ในคำตอบนั้น

ฉันต้องการเพิ่ม - ขึ้นอยู่กับสิ่งที่คุณพยายามทำ - ใช้ระบบพีชคณิตคอมพิวเตอร์"แน่นอน" ( caveat emptor ) เช่น Mathematica อาจช่วยเสริมหรือตรวจสอบโปรแกรมตัวเลขที่ไม่แม่นยำ สิ่งนี้จะช่วยให้คุณสามารถคำนวณผลลัพธ์ได้โดยไม่ต้องกังวลกับการปัดเศษตัวอย่างเช่น7*√2 - 5*√2จะให้ผลลัพธ์2แทน2.00000001หรือคล้ายกัน ของหลักสูตรนี้จะแนะนำภาวะแทรกซ้อนเพิ่มเติมที่อาจหรืออาจไม่คุ้มค่า

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.