유니 코드 문자열을위한 효율적인 Trie 구현


12

효율적인 String trie 구현을 찾고 있습니다. 주로 다음과 같은 코드를 찾았습니다.

Java에서 참조 구현 (wikipedia 당)

나는 주로 두 가지 이유로 이러한 구현을 싫어합니다.

  1. 256 개의 ASCII 문자 만 지원합니다. 키릴 같은 것들을 다룰 필요가 있습니다.
  2. 그들은 매우 메모리 비효율적입니다.

각 노드에는 Java의 64 비트 시스템에서 4096 바이트 인 256 개의 참조 배열이 있습니다. 이러한 각 노드는 각각 4096 바이트의 참조를 가진 최대 256 개의 하위 노드를 가질 수 있습니다. 따라서 모든 ASCII 2 문자열에 대한 전체 Trie에는 1MB 이상의 비트가 필요합니다. 세 개의 문자열? 노드의 배열에만 256MB. 등등.

물론 Trie에 1,600 만 개의 3 개 문자열을 모두 가지고 있지는 않기 때문에 많은 공간이 낭비됩니다. 이러한 배열의 대부분은 실제 삽입 된 키 수를 훨씬 능가하므로 널 (null) 참조입니다. 그리고 유니 코드를 추가하면 배열이 더 커집니다 (char는 Java에서 256 대신 64k 값을 가짐).

효율적인 현악기를 만들 수있는 희망이 있습니까? 이러한 유형의 구현에 대해 몇 가지 개선 사항을 고려했습니다.

  • 참조 배열을 사용하는 대신 크기가 실제 노드 수에 가까운 노드에 대한 참조 배열로 색인하는 기본 정수 유형의 배열을 사용할 수 있습니다.
  • 문자열을 4 비트 부분으로 나눌 수있어 더 깊은 트리를 희생하면서 16 크기의 노드 배열을 허용합니다.

답변:


2

이 트라이를 위해 무엇을 사용하고 있습니까? 보유하고자하는 단어의 총 수는 얼마이며 그 구성 문자의 희소성은 무엇입니까? 그리고 가장 중요한 것은 trie조차도 적절합니까 (단어 목록에 대한 접두사의 간단한 맵과 비교)?

상대적으로 작은 짧은 단어 집합과 희소 문자 집합이 있다면 중간 테이블에 대한 아이디어와 포인터를 인덱스로 바꾸는 것이 효과적입니다. 그렇지 않으면 중간 테이블의 공간이 부족해질 위험이 있습니다. 그리고 아주 작은 단어 집합을보고 있지 않으면 32 비트 시스템에서 짧은 2 바이트와 4 바이트의 참조를위한 공간을 절약 할 수 있습니다. 64 비트 JVM에서 실행 중이면 더 많은 비용을 절약 할 수 있습니다.

예상되는 모든 문자가 극도로 제한된 범위에 있지 않는 한 문자를 4 비트 청크로 나누는 것에 대한 아이디어는 크게 절약하지 못할 것입니다 (일반적인 유니 코드 모음이 아닌 대문자 US-ASCII로 제한된 단어의 경우 괜찮을 수도 있습니다) ).

희소 문자 세트 HashMap<Character,Map<...>>가 있으면 가장 적합한 구현이 될 수 있습니다. 예, 각 출품작은 훨씬 커지지 만 출품작이 많지 않으면 전반적으로 승리합니다. (측면 참고 사항 : Tries에 대한 Wikipedia 기사는 해시 된 데이터 구조를 기반으로 한 예제를 보여 주었을 때 항상 재미 있다고 생각했습니다. 해당 선택의 공간 / 시간 균형을 완전히 무시했습니다.)

마지막으로, 당신은 모두 trie를 피하고 싶을 수도 있습니다. 인간 언어로 된 일반적인 단어 모음을보고 있다면 (4,0-8 자 길이로 10,000 개 단어 사용), HashMap<String,List<String>키가 전체 접두사 인을 사용하면 훨씬 나을 것입니다 .


-참조는 32 비트에서 8 바이트, 64 비트 시스템에서 16 바이트입니다.-자동 완성 기능을위한 것입니다.-문자열에서 대부분의 문자는 ASCII 범위에 있지만 몇 개의 중앙 유럽 문자가 발생합니다. 이것이 더 작은 분기를 원했던 이유입니다. 많은 문자를 잘라 내기 때문에 256보다 큽니다. 실제로 작성하고 사용하기는 쉽지만 HashMap <String, List <String >>이 메모리를 더 좋거나 더 빠르거나 덜 소비하는 것을 보지 못했습니다. 그러나 HashMap <Character, Map> 아이디어를 받아들입니다. 128 자 이상이면 괜찮을 것입니다 (제 경우에는 희귀합니다-중국어 텍스트에는 좋지 않습니다).
RokL

4

문자열을 UTF8로 인코딩하면 표준 256 분기 트리를 사용할 수 있으며 여전히 유니 코드 호환 가능

또한 가능한 128 ASCII 문자 중 70 개 정도의 문자 (모든 UTF8에서 1 바이트로 인코딩 됨)를 가장 많이 찾을 수 있습니다 (사용하지 않는 제어 문자 대신 일반적인 digraph 포함) )


UTF8이 그렇게 표현 될 수 있다는 것을 알고 있습니다. 그러나 여전히 여전히 높은 메모리 소비를 해결하지 못합니다. 문자를 기본 256 범위로 바꾸려면 약간의 스위치 문장이 필요합니다. 그만한 가치가 있다고 생각합니다. UTF-8이 진행되는 한 ... 이것은 실제로 내가 지금 고민하고있는 문제입니다. Java String은 UTF-16 문자를 사용하므로 쉽게 얻을 수 있으며 바이트 단위로 인코딩 할 수 있습니다. 또는 UTF-8로 변환하여 사용할 수 있습니다. 이 시점에서 UTF-16에서 UTF-8로 변환하는 비용이 엄청 나지 않은지 확실하지 않습니다.
RokL

대부분의 경우이 언어를 사용하는 언어는 무엇입니까? 모든 것에 대한 최적화를 시도하는 것은 불가능합니다 (또는 이미 완료되었을 것입니다). 일반적인 경우에 맞게 최적화하십시오
ratchet freak

1
이것은 CESU-8 이 UTF-8보다 선호 되는 매우 적은 유스 케이스 중 하나입니다. 여기서 UTF-8 코드 포인트에서 해당 CESU-8 코드 포인트로 얻는 것이 사소한 것입니다 (필요한 경우). 1-2 UTF-16 코드 포인트를 디코딩하여 해당 UTF-8 코드 포인트에 도달).
Joachim Sauer

1
@ratchetfreak 자바. 질문은 대부분의 언어로 일반화 될 수 있다고 생각합니다. C byte*에서는 비트 타입으로 모든 유형을 인코딩 하기 위해 포인터를 캐스팅 할 수 있다고 생각 합니다.
RokL

@ UMad 나는 입력 문자열이 어떤 언어로 사용되는지를 의미했습니다 (영어, 프랑스어, 독일어, ...)
ratchet freak
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.