ฟังก์ชั่นแฮชที่ดีสำหรับเงื่อนไข


160

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

ฉันกำลังทำสิ่งนี้ใน Java แต่ฉันไม่คิดว่าจะสร้างความแตกต่างได้มากนัก


4
ฟังก์ชันแฮชที่ดีนั้นขึ้นอยู่กับอินพุตเข้าสู่แฮชและข้อกำหนดของอัลกอริทึม แฮชดังกล่าวจะไม่ดีมากหากสตริงทั้งหมดของคุณเริ่มต้นด้วยอักขระห้าตัวที่เหมือนกันตัวอย่างเช่น นอกจากนี้ยังมีแนวโน้มที่จะส่งผลให้การกระจายปกติ
WhirlWind

1
สำเนาซ้ำที่เป็นไปได้ของ98153
Michael Mrozek

14
ทำไมคุณไม่สามารถใช้Stringของตัวเองได้hashCode()?
Bart Kiers

@WirlWind จริงฉันไม่แน่ใจว่าสตริงจะมีอะไรนอกจากนั้นอาจเป็นข้อความภาษาอังกฤษ
Leif Andersen

@Barl ส่วนใหญ่เป็นเพราะอาจารย์ของฉันบอกให้เราใช้ฟังก์ชั่นแฮชของเราเอง ... และเหตุผลที่ฉันไม่ต้องการใช้ Java ก็เพราะมันเป็นเรื่องธรรมดา
Leif Andersen

คำตอบ:


161

โดยปกติ hashes จะไม่ทำเงินก้อนมิฉะนั้นstopและpotsจะมีกัญชาเดียวกัน

และคุณจะไม่ จำกัด เฉพาะอักขระ n ตัวแรกเพราะมิฉะนั้นบ้านและบ้านจะมีแฮชเดียวกัน

โดยทั่วไปแฮชจะใช้ค่าและคูณด้วยจำนวนเฉพาะ (ทำให้มีโอกาสมากที่จะสร้างแฮชที่ไม่ซ้ำกัน) ดังนั้นคุณสามารถทำสิ่งต่อไปนี้:

int hash = 7;
for (int i = 0; i < strlen; i++) {
    hash = hash*31 + charAt(i);
}

@jonathanasdf คุณจะบอกได้อย่างไรว่ามันจะให้รหัสแฮชที่ไม่ซ้ำใครอยู่เสมอ มีข้อพิสูจน์ทางคณิตศาสตร์หรือไม่? ฉันคิดว่าเราต้องใช้ mod ของแฮชกับหมายเลขเฉพาะที่ใหญ่กว่ามิฉะนั้นปัญหาล้นเกิดขึ้น
devsda

17
@devsda เขาไม่ได้พูดว่าเป็นเอกลักษณ์เสมอไปเขาบอกว่ามีแนวโน้มที่จะไม่เหมือนใคร สำหรับเหตุผลการค้นหาอย่างรวดเร็วใน google เปิดเผยบทความนี้: computinglife.wordpress.com/2008/11/20/…อธิบายว่าทำไม 31 ถูกใช้สำหรับการแฮชสตริงของ Java ไม่มีการพิสูจน์ทางคณิตศาสตร์ แต่อธิบายแนวคิดทั่วไปว่าทำไมช่วงเวลาทำงานจึงดีขึ้น
Pharap

2
ขอบคุณมากที่อธิบายความคิดในการทำ hashing ให้ดีขึ้น Just to double check - Java hashCode () ส่งคืนค่าจะใช้แผนที่เพื่อดัชนีตารางบางอย่างก่อนที่จะเก็บวัตถุ ดังนั้นถ้า hashCode () ส่งคืน m มันจะทำสิ่งที่ต้องการ (m mod k) เพื่อรับดัชนีของตารางขนาด k นั่นถูกต้องใช่ไหม?
whitehat

1
"hash = hash * 31 + charAt (i);" สร้างแฮชเดียวกันสำหรับสปอตท็อปหยุด opts และพ็อต
Jack Straub

1
@ maq ฉันเชื่อว่าคุณถูกต้อง ไม่รู้ว่าฉันคิดอะไรอยู่
Jack Straub

139

หากเป็นเรื่องความปลอดภัยคุณสามารถใช้ Java crypto:

import java.security.MessageDigest;

MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
messageDigest.update(stringToEncrypt.getBytes());
String encryptedString = new String(messageDigest.digest());

93
ดี ฉันมีแอปพลิเคชันการเรียนรู้ด้วยเครื่องจักรโดยใช้ NLP เชิงสถิติเพื่อรวบรวมคลังข้อมูลขนาดใหญ่ หลังจากผ่านไปสองสามครั้งแรกของการปรับสภาพสัณฐานวิทยาของคำต้นฉบับในข้อความฉันทิ้งค่าสตริงและใช้รหัสแฮชแทน ตลอดทั้งคลังข้อมูลของฉันมีคำที่ไม่ซ้ำกันประมาณ 600,000 คำและด้วยการใช้ฟังก์ชันจาวาโค้ดแฮชโค้ดเริ่มต้นฉันได้รับการชนประมาณ 3.5% แต่ถ้าฉัน SHA-256 ค่าสตริงแล้วสร้าง hashcode จากสตริงที่ผ่านการย่อยอัตราส่วนการชนจะน้อยกว่า 0.0001% ขอบคุณ!
benjismith

3
ขอบคุณที่ให้ข้อมูลเกี่ยวกับการชนและจำนวนคำ มีประโยชน์มาก
philipp

19
@benjismith หนึ่งในล้านมีขนาดใหญ่เกินไป ... คือ "น้อยกว่า 0.0001%" วิธีการพูดว่า "0" ใช่ไหม ฉันสงสัยจริงๆว่าคุณเห็นการชนของ SHA-256 เพราะไม่เคยมีใครสังเกตเห็น ไม่แม้แต่กับ SHA-1 160 บิต หากคุณมีสองสตริงที่สร้าง SHA-256 เดียวกันชุมชนความปลอดภัยก็ชอบที่จะเห็นมัน คุณจะมีชื่อเสียงระดับโลก ... ในทางที่คลุมเครือ ดูการเปรียบเทียบฟังก์ชั่น SHA
Tim Sylvester

7
@TimSylvester คุณเข้าใจผิด ฉันไม่พบการชนของ SHA-256 ฉันคำนวณ SHA-256 แล้วป้อนลำดับไบต์ผลลัพธ์ลงในฟังก์ชัน "hashCode" Java แบบทั่วไปเพราะฉันต้องการแฮชแบบ 32 บิต นั่นคือที่ฉันพบการชน ไม่มีอะไรโดดเด่น :)
benjismith

1
ไม่มี 'ความแตกต่างระหว่าง' การแฮช 'และ' การเข้ารหัส 'หรือไม่ ฉันเข้าใจ MessageDigest เป็นฟังก์ชั่นการแฮชทางเดียวใช่มั้ย นอกจากนี้เมื่อฉันใช้ฟังก์ชั่นฉันได้สตริงแฮชเป็นอักขระ UTF ขยะจำนวนมากเมื่อฉันเปิดไฟล์ใน LibreOffice เป็นไปได้หรือไม่ที่จะได้รับสตริงที่แฮชเป็นกลุ่มตัวอักษรและตัวเลขแบบสุ่มแทนที่จะเป็นอักขระ UTF ขยะ
Nav

38

คุณอาจจะใช้String.hashCode ()

หากคุณต้องการติดตั้ง hashCode ด้วยตนเอง:

อย่าถูกล่อลวงให้แยกส่วนสำคัญของวัตถุออกจากการคำนวณรหัสแฮชเพื่อปรับปรุงประสิทธิภาพ - Joshua Bloch, Java ที่มีประสิทธิภาพ

โดยใช้เพียงห้าตัวอักษรแรกเป็นความคิดที่ดี คิดเกี่ยวกับชื่อลำดับชั้นเช่น URL: พวกเขาทั้งหมดจะมีรหัสแฮชเดียวกัน (เพราะพวกเขาทั้งหมดเริ่มต้นด้วย "http: //" ซึ่งหมายความว่าพวกเขาจะถูกเก็บไว้ภายใต้ถังเดียวกันในแผนที่กัญชาแสดงประสิทธิภาพที่น่ากลัว

นี่คือเรื่องราวสงครามที่ถูกถอดความจาก String hashCode จาก " Effective Java ":

ฟังก์ชันแฮชสตริงถูกนำมาใช้ในการเผยแพร่ทั้งหมดก่อนที่จะตรวจสอบ 1.2 ตัวอักษรที่มีความยาวมากที่สุดสิบหกตัวโดยเว้นระยะเท่ากันทั่วทั้งสตริงเริ่มต้นด้วยอักขระตัวแรก สำหรับคอลเลกชันขนาดใหญ่ของชื่อลำดับขั้นเช่น URL ฟังก์ชันแฮชนี้แสดงพฤติกรรมที่แย่มาก


1
หากมีใครใช้คอลเลกชันแบบแฮชสองครั้งมันอาจคุ้มค่าที่จะให้แฮชแรกนั้นรวดเร็วและสกปรกจริงๆ หากมีหนึ่งพันสายยาวครึ่งหนึ่งของที่มีการแมปโดยฟังก์ชั่น crummy กับค่าหนึ่งโดยเฉพาะและครึ่งหนึ่งของที่ถูกแมปไปยังค่าที่แตกต่างกันประสิทธิภาพในตารางเดียว hashed จะไม่ดี แต่ประสิทธิภาพในคู่ ตารางแฮชที่แฮชที่สองตรวจสอบสายอักขระทั้งหมดอาจเป็นเกือบสองเท่าของตารางแฮชเดี่ยว (เนื่องจากครึ่งสตริงไม่ต้องถูกแฮชเต็ม) ไม่มีคอลเลกชัน Java มาตรฐานใด ๆ ที่ทำ hashing สองครั้ง
supercat

การเชื่อมโยงที่มีประสิทธิภาพ Java เสีย @Frederik
กิโลกรัม

17

หากคุณกำลังทำสิ่งนี้ใน Java ทำไมคุณถึงทำมัน เพียงแค่โทรหา.hashCode()สาย


2
ฉันทำมันเป็นส่วนหนึ่งของชั้นเรียนและส่วนหนึ่งของงานที่มอบหมายคือการเขียนฟังก์ชันแฮชที่แตกต่างกันหลายอย่าง อาจารย์บอกให้เรารับความช่วยเหลือจากภายนอกเพื่อสิ่งที่ดีกว่า
Leif Andersen

20
หากคุณจำเป็นต้องมีความสอดคล้องกันในรุ่น JVM .hashCode()และการใช้งานของคุณมีคุณไม่ควรพึ่งพา ค่อนข้างใช้อัลกอริทึมที่รู้จักกันบ้าง
Stephen Ostermiller

7
อัลกอริทึมสำหรับการString::hashCodeระบุไว้ใน JDK java.lang.Stringดังนั้นมันจึงเป็นแบบพกพาเป็นอยู่มากในชั้นเรียน
yshavit


8

ฟังก์ชั่นนี้จัดทำโดย Nick นั้นดี แต่ถ้าคุณใช้ String ใหม่ (ไบต์ [] ไบต์) เพื่อทำการแปลงให้เป็น String มันจะล้มเหลว คุณสามารถใช้ฟังก์ชั่นนี้เพื่อทำสิ่งนั้น

private static final char[] hex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

public static String byteArray2Hex(byte[] bytes) {
    StringBuffer sb = new StringBuffer(bytes.length * 2);
    for(final byte b : bytes) {
        sb.append(hex[(b & 0xF0) >> 4]);
        sb.append(hex[b & 0x0F]);
    }
    return sb.toString();
}

public static String getStringFromSHA256(String stringToEncrypt) throws NoSuchAlgorithmException {
    MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
    messageDigest.update(stringToEncrypt.getBytes());
    return byteArray2Hex(messageDigest.digest());
}

อาจเป็นเช่นนี้สามารถช่วยใครบางคน


คุณสามารถส่งอาร์เรย์ไบต์ไปที่ messageDigest.update ()
szgal

byteArray2Hex () - นั่นคือสิ่งที่ฉันต้องการอย่างสมบูรณ์แบบ! ขอบคุณมาก :)
Krzysiek

5
// djb2 hash function
unsigned long hash(unsigned char *str)
{
    unsigned long hash = 5381;
    int c;

    while (c = *str++)
        hash = ((hash << 5) + hash) + c; /* hash * 33 + c */

    return hash;
}

ลอจิกต้นทางที่อยู่เบื้องหลังฟังก์ชั่นแฮช djb2 - SO


1
ฉันคิดว่ามันเป็นเพียงจำนวนเฉพาะที่จะเริ่มต้นเพื่อให้เรามีการชนกันน้อยลง
CornSmith

5

FNV-1มีข่าวลือว่าเป็นฟังก์ชันแฮชที่ดีสำหรับสตริง

สำหรับสตริงที่ยาว (ยาวกว่าพูดประมาณ 200 ตัวอักษร) คุณจะได้รับประสิทธิภาพที่ดีจากฟังก์ชันแฮชMD4 ในฐานะที่เป็นฟังก์ชั่นการเข้ารหัสมันถูกทำลายเมื่อประมาณ 15 ปีที่แล้ว แต่สำหรับวัตถุประสงค์ที่ไม่ใช่การเข้ารหัสมันยังดีมากและรวดเร็วอย่างน่าประหลาดใจ ในบริบทของ Java คุณจะต้องแปลงค่า 16 บิตcharเป็นคำ 32 บิตเช่นโดยการจัดกลุ่มค่าดังกล่าวเป็นคู่ การดำเนินอย่างรวดเร็วของ MD4 ในชวาสามารถพบได้ในsphlib อาจ overkill ในบริบทของการกำหนดห้องเรียน แต่อย่างอื่นน่าลอง


ฟังก์ชั่นแฮชนี้ดีขึ้นมากแล้วฟังก์ชันที่มาพร้อมกับจาวา
clankill3r

3

หากคุณต้องการที่จะเห็นการใช้งานมาตรฐานอุตสาหกรรมผมมองไปที่java.security.MessageDigest

"การย่อยข้อความมีความปลอดภัยฟังก์ชันแฮชทางเดียวที่ใช้ข้อมูลขนาดโดยพลการและส่งออกค่าแฮชที่มีความยาวคงที่"


1

นี่คือลิงค์ที่อธิบายฟังก์ชั่นแฮชต่างๆมากมายสำหรับตอนนี้ฉันชอบฟังก์ชั่นแฮชของ ELF สำหรับปัญหาเฉพาะของคุณ มันจะใช้เป็นอินพุตสตริงของความยาวโดยพลการ


1

sdbm: อัลกอริทึมนี้ถูกสร้างขึ้นสำหรับไลบรารีฐานข้อมูล sdbm (การปรับใช้โดเมนสาธารณะซ้ำของ ndbm)

static unsigned long sdbm(unsigned char *str)
{   
    unsigned long hash = 0;
    int c;
    while (c = *str++)
            hash = c + (hash << 6) + (hash << 16) - hash;

    return hash;
}

0
         public String hashString(String s) throws NoSuchAlgorithmException {
    byte[] hash = null;
    try {
        MessageDigest md = MessageDigest.getInstance("SHA-256");
        hash = md.digest(s.getBytes());

    } catch (NoSuchAlgorithmException e) { e.printStackTrace(); }
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < hash.length; ++i) {
        String hex = Integer.toHexString(hash[i]);
        if (hex.length() == 1) {
            sb.append(0);
            sb.append(hex.charAt(hex.length() - 1));
        } else {
            sb.append(hex.substring(hex.length() - 2));
        }
    }
    return sb.toString();
}

-1

มันเป็นความคิดที่ดีที่จะทำงานกับจำนวนคี่เมื่อพยายามพัฒนาฟังก์ชั่น hast ที่ดีสำหรับสตริง ฟังก์ชั่นนี้ใช้สตริงและส่งกลับค่าดัชนีจนถึงตอนนี้มันใช้งานได้ค่อนข้างดี และมีการชนกันน้อย ดัชนีอยู่ในช่วงตั้งแต่ 0 - 300 อาจจะมากกว่านั้น แต่ฉันก็ยังไม่ได้อะไรที่สูงกว่านี้แม้แต่คำพูดยาว ๆ อย่าง "วิศวกรรมเครื่องกลไฟฟ้า"

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += 7*n%31;
    }
    return u%139;
}

อีกสิ่งที่คุณสามารถทำได้คือการคูณตัวละครแต่ละตัวแยกวิเคราะห์ดัชนีโดยเพิ่มขึ้นเช่นคำว่า "หมี" (0 * b) + (1 * e) + (2 * a) + (3 * r) ซึ่งจะทำให้คุณ ค่า int ที่จะเล่นกับ ฟังก์ชันแฮชแรกเหนือชนกันที่ "ที่นี่" และ "ได้ยิน" แต่ก็ยังดีที่ให้ค่าที่ไม่ซ้ำกันบางอย่าง ด้านล่างไม่ได้ขัดแย้งกับ "ที่นี่" และ "ได้ยิน" เพราะฉันทวีคูณตัวละครแต่ละตัวด้วยดัชนีเมื่อมันเพิ่มขึ้น

int keyHash(string key)
{
    unsigned int k = (int)key.length();
    unsigned int u = 0,n = 0;

    for (Uint i=0; i<k; i++)
    {
        n = (int)key[i];
        u += i*n%31;
    }
    return u%139;
}

-1

นี่คือฟังก์ชั่นแฮชธรรมดาที่ฉันใช้สำหรับตารางแฮชที่ฉันสร้าง มันเป็นพื้นสำหรับการใช้ไฟล์ข้อความและเก็บทุกคำในดัชนีซึ่งแสดงถึงลำดับตัวอักษร

int generatehashkey(const char *name)
{
        int x = tolower(name[0])- 97;
        if (x < 0 || x > 25)
           x = 26;
        return x;
}

สิ่งนี้โดยทั่วไปคือคำถูกแฮชตามตัวอักษรตัวแรกของพวกเขา ดังนั้นคำที่ขึ้นต้นด้วย 'a' จะได้รับรหัสแฮชของ 0, 'b' จะได้ 1 และอื่น ๆ และ 'z' จะเป็น 25 ตัวเลขและสัญลักษณ์จะมีคีย์แฮชที่ 26 ข้อได้เปรียบนี้มีให้ ; คุณสามารถคำนวณได้อย่างง่ายดายและรวดเร็วโดยที่คำที่กำหนดจะถูกจัดทำดัชนีในตารางแฮชตั้งแต่คำทั้งหมดตามลำดับตัวอักษรสิ่งนี้: รหัสสามารถพบได้ที่นี่: https://github.com/abhijitcpatil/general

ให้ข้อความต่อไปนี้เป็นข้อความป้อนข้อมูล:แอทติคัสพูดกับเจมวันหนึ่ง“ ฉันอยากให้คุณถ่ายที่กระป๋องในสวนหลังบ้าน แต่ฉันรู้ว่าคุณจะไล่ตามนก ยิงนกสีน้ำเงินที่คุณต้องการถ้าคุณสามารถยิงมันได้ แต่จำไว้ว่ามันเป็นบาปที่จะฆ่านกกระเต็น " นั่นเป็นเพียงครั้งเดียวที่ฉันเคยได้ยินแอตติคัสบอกว่ามันเป็นบาปที่จะทำอะไรบางอย่างและฉันถามคุณ Maudie เกี่ยวกับเรื่องนี้ “ สิทธิของพ่อคุณ” เธอพูด “ กระเต็นไม่ทำอะไรเลยยกเว้นทำเพลงให้เราสนุก พวกเขาไม่กินสวนของผู้คนไม่ทำรังในเปลข้าวโพดพวกเขาไม่ทำอะไรเลยนอกจากร้องเพลงให้พวกเรา นั่นเป็นเหตุผลว่าทำไมมันถึงเป็นบาปที่จะฆ่ากระเต็น

นี่จะเป็นผลลัพธ์:

0 --> a a about asked and a Atticus a a all after at Atticus
1 --> but but blue birds. but backyard
2 --> cribs corn can cans
3 --> do dont dont dont do dont do day
4 --> eat enjoy. except ever
5 --> for for fathers
6 --> gardens go
7 --> hearts heard hit
8 --> its in it. I it I its if I in
9 --> jays Jem
10 --> kill kill know
11 --> 
12 --> mockingbird. music make Maudie Miss mockingbird.”
13 --> nest
14 --> out one one only one
15 --> peoples
16 --> 17 --> right remember rather
18 --> sin sing said. she something sin say sin Shoot shot said
19 --> to Thats their thing they They to thing to time the That to the the tin to
20 --> us. up us
21 --> 
22 --> why was was want
23 --> 
24 --> you you youll you
25 --> 
26 --> Mockingbirds  Your em Id

2
ฟังก์ชั่นแฮชที่ดีจะกระจายค่าต่าง ๆ อย่างเท่าเทียมกันในที่เก็บข้อมูล
Jonathan Peterson

-1

สิ่งนี้จะหลีกเลี่ยงการชนกันและจะรวดเร็วจนกว่าเราจะใช้การเลื่อนในการคำนวณ

 int k = key.length();
    int sum = 0;
    for(int i = 0 ; i < k-1 ; i++){
        sum += key.charAt(i)<<(5*i);
    }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.