문자열에 대한 좋은 해시 함수


160

문자열에 대한 좋은 해시 함수를 생각하려고합니다. 그리고 문자열의 처음 다섯 문자에 대한 유니 코드 값을 합산하는 것이 좋습니다. 좋은 생각입니까, 아니면 나쁜 생각입니까?

Java 로이 작업을 수행하고 있지만 큰 차이가 있다고는 생각하지 않습니다.


4
좋은 해시 함수는 해시 입력 및 알고리즘 요구 사항에 크게 의존합니다. 예를 들어 모든 문자열이 같은 5 자로 시작하는 경우 이러한 해시는 그리 좋지 않습니다. 또한 정규 분포를 따르는 경향이 있습니다.
WhirlWind

1
가능한 복제 98153
Michael Mrozek

14
String스스로 사용할 수 hashCode()없습니까?
Bart Kiers

@WhirlWind, true, 아마도 영어 텍스트가 될 것 이외의 문자열이 무엇인지 확실하지 않습니다.
Leif Andersen

@Barl은 주로 교수가 우리 자신의 해시 functor를 구현하라고 말했기 때문에 ... Java를 사용하고 싶지 않은 이유는 일반적인 것이기 때문에 더 구체적인 해시 functor가 더 좋을 것입니다.
Leif Andersen

답변:


161

일반적으로 총액을하지 않을 것입니다 해시, 그렇지 않은 경우 stoppots동일한 해시를해야합니다.

그렇지 않으면 주택과 주택이 동일한 해시를 갖기 때문에 첫 n 문자로 제한하지 않습니다.

일반적으로 해시는 값을 가져와 소수를 곱하여 고유 한 해시를 생성 할 가능성이 높아 지므로 다음과 같은 작업을 수행 할 수 있습니다.

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

@jonathanasdf 항상 고유 한 해시 키를 제공한다고 어떻게 말할 수 있습니까? 수학적 증거가 있습니까? 다른 큰 소수로 해시를 수정해야한다고 생각합니다. 그렇지 않으면 오버플로 문제가 발생합니다.
devsda

17
@devsda 그는 항상 독특하다고 말하지 않았으며, 더 독특 할 것이라고 말했다. 이유에 관해서는, 구글의 빠른 검색이 글을 보여준다 : computinglife.wordpress.com/2008/11/20/... (31)은 자바 문자열 해시에 사용 된 이유를 설명. 수학적 증거는 없지만 프라임이 더 나은 이유에 대한 일반적인 개념을 설명합니다.
Pharap

2
더 나은 해싱을한다는 아이디어를 분명히 해주셔서 감사합니다. 다시 확인하기 위해-hashCode () 반환 값은 객체를 저장하기 전에 일부 테이블 인덱스에 매핑하기 위해 Java에서 사용됩니다. 따라서 hashCode ()가 m을 반환하면 크기가 k 인 테이블의 인덱스를 얻기 위해 (m mod k)와 같은 작업을 수행합니다. 맞습니까?
whitehat

1
"해시 = 해시 * 31 + charAt (i);" 스팟, 탑스, 스탑, 옵트 및 팟에 대해 동일한 해시를 생성합니다.
잭 스트라우브

1
@maq 나는 당신이 맞다고 생각합니다. 내가 무슨 생각을했는지 모르겠다.
잭 스트라우브

139

보안 인 경우 Java 암호화를 사용할 수 있습니다.

import java.security.MessageDigest;

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

93
좋은. 대규모 코퍼스에 대해 통계적 NLP를 수행하는 기계 학습 응용 프로그램이 있습니다. 텍스트의 원래 단어에 대한 형태 학적 정규화의 몇 가지 초기 단계 후에 문자열 값을 버리고 대신 해시 코드를 사용합니다. 제 말뭉치 전체에 약 600,000 개의 고유 단어가 있으며 기본 Java 해시 코드 함수를 사용하면 약 3.5 %의 충돌이 발생했습니다. 그러나 문자열 값을 SHA-256하고 다이제스트 된 문자열에서 해시 코드를 생성하면 충돌 비율은 0.0001 % 미만입니다. 감사!
benjismith

3
충돌과 단어 수에 대한 정보를 제공해 주셔서 감사합니다. 매우 도움이됩니다.
philipp

19
@benjismith 100 만분의 1이 너무 큽니다. "0.0001 % 미만"이 "정확하게 0"이라고 말하는 간접적 인 방법입니까? SHA-256 충돌이 어디서나, 어느 곳에서도 관찰되지 않았기 때문에 실제로 본 것이 의심 스럽습니다. 160 비트 SHA-1조차도 아닙니다. 동일한 SHA-256을 생성하는 두 개의 문자열이있는 경우 보안 커뮤니티는 해당 문자열을보고 싶어합니다. 당신은 매우 모호한 방식으로 세계적으로 유명 할 것입니다. 참조 SHA 함수의 비교
팀 실베스터

7
@ TimSylvester, 당신은 오해했다. SHA-256 충돌을 찾지 못했습니다. SHA-256을 계산 한 다음 32 비트 해시가 필요했기 때문에 결과 바이트 시퀀스를 일반적인 Java "hashCode"함수에 제공했습니다. 그것이 충돌을 발견 한 곳입니다. 아무것도 주목할만한 것이 없다 :)
benjismith

1
'해싱'과 '암호화'의 차이점이 없습니까? MessageDigest가 단방향 해싱 함수라는 것을 알고 있습니다. 또한 함수를 사용할 때 LibreOffice에서 파일을 열 때 해시 문자열을 많은 정크 UTF 문자로 얻었습니다. 정크 UTF 문자 대신 임의의 영숫자 문자로 해시 된 문자열을 얻을 수 있습니까?
Nav

38

아마도 String.hashCode ()를 사용해야합니다 합니다.

hashCode를 직접 구현하려면 다음을 수행하십시오.

성능 향상을 위해 해시 코드 계산에서 객체의 상당 부분을 배제하려는 유혹을받지 마십시오-Joshua Bloch, Effective Java

처음 다섯 문자 만 사용하는 것은 좋지 않습니다 . URL과 같은 계층 적 이름을 생각해보십시오. 모두 동일한 해시 코드를 갖습니다 (모두 "http : //"로 시작하기 때문에 해시 맵의 동일한 버킷 아래에 저장되어 성능이 끔찍합니다).

다음은 " Effective Java " 의 String hashCode에 대한 전쟁 이야기입니다 .

1.2 이전의 모든 릴리스에서 구현 된 문자열 해시 함수는 첫 문자부터 시작하여 문자열 전체에 고른 간격으로 최대 16자를 검사했습니다. URL과 같은 대규모 계층 이름 모음의 경우이 해시 함수는 끔찍한 동작을 나타냅니다.


1
이중 해시 모음을 사용하는 경우 첫 번째 해시를 실제로 빠르고 더럽게 만드는 것이 좋습니다. 하나가 긴 문자열을 가지고 있는데, 그중 절반이 crummy 함수에 의해 특정 값으로 매핑되고 그 중 절반이 고유 값으로 매핑되면 단일 해시 테이블의 성능은 좋지 않지만 두 배의 성능은 두 번째 해시가 전체 문자열을 검사하는 해시 테이블은 단일 해시 테이블보다 거의 두 배가 될 수 있습니다 (문자열의 절반이 완전히 해시 될 필요가 없기 때문에). 그러나 표준 Java 컬렉션은 이중 해싱을 수행하지 않습니다.
supercat

효과적인 자바 링크가 끊어졌습니다 @Frederik
KGs

17

Java로 이것을하고 있다면 왜하고 있습니까? 그냥 .hashCode()문자열을 불러


2
클래스의 일부로하고 있으며 할당의 일부는 여러 가지 해시 함수를 작성하는 것입니다. 교수는 우리에게 더 나은 사람들을 위해 외부의 도움을 청하라고 말했다.
Leif Andersen

20
JVM 버전과 구현에서 일관성을 유지해야하는 경우에 의존해서는 안됩니다 .hashCode(). 오히려 알려진 알고리즘을 사용하십시오.
Stephen Ostermiller 2014 년

7
의 알고리즘 String::hashCode은 JDK에 지정되어 있으므로 클래스의 존재만큼 이식성이 뛰어납니다 java.lang.String.
yshavit


8

Nick이 제공하는이 기능은 좋지만 new String (byte [] bytes)을 사용하여 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

FNV-1 은 문자열에 좋은 해시 함수라는 소문이 있습니다.

긴 문자열 (약 200 자 이상)의 경우 MD4 해시 함수 에서 우수한 성능을 얻을 수 있습니다 . 암호화 기능은 약 15 년 전에 고장 났지만, 비 암호화 목적을 위해 여전히 매우 훌륭하고 놀랍습니다. Java와 관련하여 16 비트 char값을 32 비트 단어로 변환해야합니다 ( 예 : 이러한 값을 쌍으로 그룹화). Java에서 MD4의 빠른 구현은 sphlib 에서 찾을 수 있습니다 . 아마도 교실 과제의 맥락에서 과잉이지만 아마도 시도해 볼만한 가치가 있습니다.


이 해시 함수는 java와 함께 제공되는 것보다 훨씬 낫습니다.
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;
}

당신이 할 수있는 또 다른 일은 "bear"(0 * b) + (1 * e) + (2 * a) + (3 * r) 단어처럼 증가함에 따라 각 문자 int 구문 분석에 색인을 곱하는 것입니다. 재생할 int 값 위의 첫 번째 해시 함수는 "here"와 "hear"에서 충돌하지만 여전히 좋은 고유 값을 제공합니다. 아래의 문자는 각 문자가 증가함에 따라 색인에 곱하기 때문에 "here"및 "hear"와 충돌하지 않습니다.

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

다음 텍스트를 입력으로 제공 : Atticus는 어느 날 Jem에게 다음과 같이 말했습니다. 당신이 그들을 칠 수 있다면 원하는 모든 파란색 제이를 쏴라. 그러나 앵무새를 죽이는 것은 죄라는 것을 기억하라.” 내가 아티 쿠스가 무언가를하는 것은 죄라는 말을들은 유일한 시간이었고, 나는 Maudie 양에게 그것에 대해 물었다. “아버지 말이 맞아요.”그녀가 말했다. “Mockingbirds는 우리가 즐길 수있는 음악을 만드는 것 외에는 아무것도하지 않습니다. 그들은 사람들의 정원을 먹지 않고 옥수수 침대에 둥지를 틀지 않고 한 가지 일을하지 않고 우리를 위해 마음을 노래합니다. 그렇기 때문에 mockingbird를 죽이는 것은 죄입니다.

이것은 출력이 될 것입니다 :

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.