hell0w0rd
@hell0w0rd
Просто разработчик

Что не так с реализацией алгоритма Хаффмана?

вот код

Все работает, за исключением преобразования последнего символа. Вообще ни в какую не преобразовывается, и я не понимаю почему, хотя подозреваю что не совсем правильно работаю с файлами.

PS Также вопрос было бы полезно для новичков в виде туториала? Описать реализацию этого алгоритма? Видел несколько статей, но ни в одной не описывается обратный процесс преобразования.
  • Вопрос задан
  • 5300 просмотров
Решения вопроса 1
@mayorovp
Так, а какие файлы называются «большими»?
Навскидку я вижу две проблемы — файлы более 2Гб (переполнение int) и нулевые символы (которым ошибочно не назначается код).

Первая проблема лечится использованием большего типа данных, а вторая таким вот патчем:
void Compressor::buildCodeTable(Node *root)
{
    if (root->hasChild())
    {
        code.push_back(0);
        buildCodeTable(root->getChild(true));
        code.pop_back();

        code.push_back(1);
        buildCodeTable(root->getChild(false));
        code.pop_back();
    } else {
        codeTable[letter] = root->getLetter();
    }
}
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 5
barmaley_exe
@barmaley_exe
Для последнего байта нужно знать количество значащих бит. Ведь разные символы, сжатые по Хаффману, имеют различную битовую длину, а писать можно только байты.
Может быть, этот случай Вами уже учтён, но при поверхностном взгляде я этого не увидел.
Ответ написан
Комментировать
@mayorovp
Вот этот кусок надо добавить в функцию compress перед закрытием потока:
if (count > 0)
    out << buf;

Это связано с тем, что после цикла в buf могли оказаться значимые биты, не образующие полного байта и потому еще не записанные в поток.
Ответ написан
Комментировать
@mayorovp
Да, еще замечание по структуре: мне не кажется хорошей идеей объединение методов compress и decompress в одном классе, хранящем входной файл в виде поля. Как мне кажется, еще на этапе создания экземпляра пользователь библиотеки будет знать, что ему с этим файлом делать — упаковывать или распаковывать.

ИМХО лучше выделить поля charMap, charTree и inputFile в базовый класс, а остальное разбросать по классам Compressor и Decompressor. Еще лучше ограничиться двумя глобальными функциями.

Еще один вариант — убрать сущность «входной файл» из полей класса. Это позволит собирать статистику по одному потоку, а сжимать — другой, что может быть полезным для сжатия «на лету». Или можно хранить статистику отдельно от сжатых данных, чему тоже можно найти применение.
Ответ написан
@mayorovp
Еще две ошибки — вы не удаляете созданные сущности Node, что вызывает утечку памяти. Кроме того, выполнение tree.sort в цикле — ужасная идея. Правильная реализация алгоритма использует тот факт, что сущности Node в алгоритме создаются в порядке возрастания частот, благодаря чему можно обойтись одной сортировкой исходных нод в начале.
Ответ написан
@mayorovp
Да, только что заметил: метод decompress теперь может распаковать несколько мусорных символов в конце файла (попробуйте упаковать файл, содержащий всего один символ). Надо бы записывать перед сжатым потоком длину исходного, или хотя бы ее последние три бита.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы