Unicode dizeler için verimli Trie uygulaması


12

Verimli bir String trie uygulaması arıyordum. Çoğunlukla böyle bir kod buldum:

Java'da referans uygulama (wikipedia başına)

Bu uygulamaları çoğunlukla iki nedenden dolayı beğenmiyorum:

  1. Yalnızca 256 ASCII karakteri destekler. Kiril gibi şeyleri kapsamam gerekiyor.
  2. Bunlar son derece bellek verimsizdir.

Her düğüm, Java'daki 64 bit makinede 4096 bayt olan 256 referanslık bir dizi içerir. Bu düğümlerin her biri, her biri 4096 bayt referansla 256 adede kadar alt düğüme sahip olabilir. Bu nedenle, her ASCII 2 karakter dizesi için tam bir Trie, 1MB'den biraz daha fazla gerektirir. Üç karakter dizesi? 256MB sadece düğümlerdeki diziler için. Ve bunun gibi.

Tabii ki Trie'mde 16 milyon üç karakter dizisinin hepsini istemiyorum, bu yüzden çok fazla alan boşa harcanıyor. Bu dizilerin çoğu, gerçek anahtarlar eklenen anahtar sayısını aştığından yalnızca boş referanslardır. Ve unicode eklersem, diziler daha da büyür (char, Java'da 256 yerine 64k değerlere sahiptir).

Teller için etkili bir üçlü yapma umudu var mı? Bu tür uygulamalar üzerinde birkaç iyileştirmeyi düşündüm:

  • Referanslar dizisi kullanmak yerine, boyutu gerçek düğümlerin sayısına yakın olan düğümlere bir dizi başvuruyu indeksleyen bir dizi ilkel tamsayı türü kullanabilirim.
  • Daha derin bir ağaç pahasına 16 boyutlu düğüm dizilerine izin verecek 4 bit parçalara dizeleri kırabilirim.

Yanıtlar:


2

Bu üçü ne için kullanıyorsun? Tutmayı planladığınız toplam kelime sayısı ve kurucu karakterlerinin seyrekliği nedir? Ve en önemlisi, bir trie bile uygun mu (kelimelerin listesine basit bir önek haritasıyla)?

Bir ara tablo ve işaretçileri dizinlerle değiştirme fikriniz, nispeten küçük bir kısa kelime kümeniz ve seyrek bir karakter kümeniz olması koşuluyla çalışacaktır. Aksi takdirde ara tablonuzda yer kalmaz. Ve son derece küçük bir kelime kümesine bakmazsanız, o kadar çok yer kazanmazsınız: 32 bitlik bir makinede referans olması için kısa ve 4 bayt için 2 bayt. 64 bit JVM ile çalışıyorsanız, tasarruf daha fazla olacaktır.

Karakterleri 4 bitlik parçalara bölme fikriniz muhtemelen tüm karakterleriniz son derece sınırlı bir aralıkta değilse (muhtemelen büyük US-ASCII ile sınırlı kelimeler için sorun değil, genel bir Unicode corpus ile) ).

Seyrek bir karakter kümeniz varsa HashMap<Character,Map<...>>, en iyi uygulamanız olabilir. Evet, her bir giriş çok daha büyük olacaktır, ancak çok fazla girişiniz yoksa genel bir kazanç elde edersiniz. (bir yan not olarak: Tries hakkındaki Wikipedia makalesinin - belki de hala - karma bir veri yapısına dayanan, bu seçimin alan / zaman dengesini tamamen görmezden geldiğini gösteren komik olduğunu her zaman düşündüm)

Son olarak, bir üçlüden tamamen kaçınmak isteyebilirsiniz. Eğer bir insan dilinde normal kelimelerden oluşan bir kelime topluluğuna bakıyorsanız (etkin kullanımda 10.000 kelime, 4-8 karakter uzunluğunda kelimelerle), HashMap<String,List<String>anahtarın önekin tamamı olduğu bir a ile muhtemelen daha iyi olacaksınız .


- Referanslar 32 bit üzerinde 8 bayt, 64 bit makinelerde 16 bayttır - Otomatik tamamlama işlevselliği içindir - Dizelerdeki karakterlerin çoğu ASCII aralığındadır, ancak birkaç Orta Avrupa karakteri atılmıştır. Bu yüzden daha küçük dallanma istedim. 256'dan fazla, çünkü çok sayıda karakteri kesecek. HashMap <String, List <String>> yazımı ve kullanımı gerçekten kolay olsa da, daha iyi veya daha hızlı veya daha az bellek tüketen görmüyorum. Ama HashMap <Karakter, Harita> fikrini kabul edeceğim. 128 (char benim durumumda - Çince metin için kötü olurdu) üzerinde chars için Tamam olurdu.
RokL

4

Dizeleri UTF8'e kodlarsanız, standart 256 dallanma üçgenini kullanabilir ve yine de unicode uyumlu olabilirsiniz

Ayrıca, olası 128 ascii karakterden yalnızca 70 kadar karakter (UTF8'de 1 bayta kodlayan) bulunacağını ve bunun için optimize edilemediğini (kullanılmayan kontrol karakterlerinin yerine ortak digrafları dahil etmek gibi) )


UTF8'in bu şekilde temsil edilebileceğini biliyorum. Ancak bu hala oldukça yüksek olan bellek tüketimini çözmez. Karakterleri temel 256 aralığına yerleştirmek biraz anahtar cümle gerektirir, buna değeceğinden şüpheliyim. UTF-8'e gelince ... bu aslında şu anda düşündüğüm bir konu. Java String, kolayca alabileceğim UTF-16 karakterlerini kullanır, bu bayt baytını kodlayabilirim. Veya UTF-8'e dönüştürebilir ve kullanabilirim. Bu noktada UTF-16'dan UTF-8'e dönüştürme maliyetinin engelleyici olup olmadığı açık değildir.
RokL

Bunu çoğu zaman kullanmayı düşündüğünüz dil nedir? her şey için optimize etmeye çalışmak imkansız (veya zaten yapılmış olurdu), bu yüzden ortak durum için optimize edin
cırcır ucube

1
Bu, CESU-8'in UTF-8'e tercih edileceği çok az kullanım durumundan biridir : Buradaki büyük avantaj, UTF-8 kod noktasından ilgili CESU-8 kod noktasına ulaşmak önemsizdir ( karşılık gelen UTF-8 kod noktalarına ulaşmak için 1-2 UTF-16 kod noktasını çözmek için).
Joachim Sauer

1
@ratchetfreak Java. Bence soru çoğu dile genelleştirilebilir. Sanırım C sadece byte*herhangi bir tür bitwise trie kodlamak için işaretçi döküm olabilir .
RokL

@UMad Giriş dizelerinin hangi dillerde olacağını kastediyorum (İngilizce, Fransızca, Almanca, ...)
cırcır ucube
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.