Выполнение Trie - программирование

Выполнение Trie

Есть ли какие-либо эффективные по скорости и кэшу реализации trie в C/С++? Я знаю, что такое trie, но я не хочу изобретать колесо, реализуя его сам.

4b9b3361

Ответ 1

если вы ищете реализацию ANSI C, вы можете "украсть" ее из FreeBSD. Файл, который вы ищете, называется radix.c. Он используется для управления данными маршрутизации в ядре.

Ответ 2

Я понимаю, что речь шла о готовых реализациях, но для справки...

Прежде чем прыгать на Джуди, вы должны были прочитать " Сравнение производительности" Judy to Hash Tables". Тогда googling название, вероятно, даст вам целую жизнь обсуждения и реституции, чтобы читать.

Явное понимание кэширования, о котором я знаю, это HAT-trie.

HAT-trie, когда она реализована правильно, очень крутая. Однако для поиска префикса вам нужен шаг сортировки на хэш-ведрах, что несколько противоречит идее структуры префикса.

Несколько проще trie - burst-trie, который по существу дает вам интерполяцию между стандартным деревом какого-то типа (например, BST) и три. Мне это нравится концептуально, и это намного проще реализовать.

Ответ 3

Мне повезло с libTrie. Возможно, он не оптимизирован специально для кеша, но производительность всегда была приличной для моих приложений.

Ответ 5

Литература,

  • A Реализация двойного массива (включает реализацию C)
  • TRASH - динамическая структура данных LC-trie и хешей - (ссылка на PDF в формате PDF, описывающая динамическое использование LC-trie в ядре Linux для реализации поиска адресов в таблице IP-маршрутизации

Ответ 6

массивы Judy: очень быстрые и эффективные по памяти массивы с редкими динамическими массивами для бит, целых чисел и строк. Массивы Judy быстрее и эффективнее памяти, чем любое дерево двоичного поиска (включая avl и red-black-trees).

Ответ 7

Вы также можете попробовать TommyDS в http://tommyds.sourceforge.net/

См. страницу контрольных показателей на сайте для сравнения скорости с недрами и юбилеем.

Ответ 9

Оптимизация кэша - это то, что вам, вероятно, придется делать, потому что вам нужно будет сопоставить данные в одной кешлинке, которая обычно составляет примерно 64 байта (что, вероятно, будет работать, если вы начнете комбинировать данные, например как указатели). Но это сложно: -)

Ответ 10

"Burst Trie" , кажется, немного эффективнее пространства. Я не уверен, сколько кеш-эффективности вы можете получить из любого индекса, так как кэширование CPU настолько крошечное. Однако этот вид trie достаточно компактен, чтобы хранить большие массивы данных в ОЗУ (где обычный Trie не будет).

Я написал реализацию Scala пакета trie, который также включает в себя некоторые методы экономии пространства, которые я нашел в реализации GWT-типа.

https://github.com/nbauernfeind/scala-burst-trie

Ответ 11

У меня были очень хорошие результаты (очень хороший баланс между производительностью и размером) с marisa-trie по сравнению с несколькими реализациями TRIE, упомянутыми в моем наборе данных.

https://github.com/s-yata/marisa-trie/tree/master

Ответ 12

Совместное использование моей "быстрой" реализации для Trie, проверено только в основном сценарии:

#define ENG_LET 26

class Trie {
    class TrieNode {
    public:
        TrieNode* sons[ENG_LET];
        int strsInBranch;
        bool isEndOfStr;

        void print() {
            cout << "----------------" << endl;
            cout << "sons..";
            for(int i=0; i<ENG_LET; ++i) {
                if(sons[i] != NULL)
                    cout << " " << (char)('a'+i);
            }
            cout << endl;
            cout << "strsInBranch = " << strsInBranch << endl;
            cout << "isEndOfStr = " << isEndOfStr << endl;
            for(int i=0; i<ENG_LET; ++i) {
                if(sons[i] != NULL)
                    sons[i]->print();
            }

        }
        TrieNode(bool _isEndOfStr = false):isEndOfStr(_isEndOfStr), strsInBranch(0) {
            for(int i=0; i<ENG_LET; ++i) {
                sons[i] = NULL;
            }
        }
        ~TrieNode() {
            for(int i=0; i<ENG_LET; ++i) {
                delete sons[i];
                sons[i] = NULL;
            }
        }
    };

    TrieNode* head;
public:
    Trie() { head = new TrieNode();}
    ~Trie() { delete head; }
    void print() {
        cout << "Preorder Print : " << endl;
        head->print();
    }
    bool isExists(const string s) {
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';
            if(curr->sons[letIdx] == NULL) {
                return false;
            }
            curr = curr->sons[letIdx];
        }

        return curr->isEndOfStr;
    }
    void addString(const string& s) {
        if(isExists(s))
            return;
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';
            if(curr->sons[letIdx] == NULL) {
                curr->sons[letIdx] = new TrieNode();    
            }
            ++curr->strsInBranch;
            curr = curr->sons[letIdx];
        }
        ++curr->strsInBranch;
        curr->isEndOfStr = true;
    }
    void removeString(const string& s) {
        if(!isExists(s))
            return;
        TrieNode* curr = head;
        for(int i=0; i<s.size(); ++i) {
            int letIdx = s[i]-'a';

            if(curr->sons[letIdx] == NULL) {
                assert(false);
                return; //string not exists, will not reach here
            }
            if(curr->strsInBranch==1) {    //just 1 str that we want remove, remove the whole branch
                delete curr;
                return;
            }
            //more than 1 son
            --curr->strsInBranch;
            curr = curr->sons[letIdx];
        }
        curr->isEndOfStr = false;
    }

    void clear() {
        for(int i=0; i<ENG_LET; ++i) {
            delete head->sons[i];
            head->sons[i] = NULL;
        }
    }

};