Я беру класс алгоритмов, где мы должны реализовать сжатие LZW в Java. Я решил использовать для этого структуру данных Trie, и я уже реализовал Trie и заставил ее работать, но она очень медленная и почти не сжимается.
Мы должны использовать 8-битные символы и иметь возможность сжимать любой двоичный файл.
Учитывая файл ~ 4 МБ (bible.txt), я получаю около 549 012 элементов в моем массиве кодов. Когда я записываю эти элементы в файл (один целочисленный код в строке), я получаю сжатый файл размером 3,5 МБ, поэтому я получаю 0,5 МБ сжатия.
Как я могу сделать эту программу более эффективной? Я чувствую, что неправильно понял здесь что-то фундаментальное или упустил что-то очевидное, но у меня нет идей, почему это не сжимается.
(Я получил тестовый файл bible.txt с этого веб-сайта: https://corpus.canterbury.ac.nz/descriptions/)
Я читаю байты из двоичного файла, подобного этому (чтение как int и преобразование в char необходимо, чтобы значения выше 0x80 не были отрицательными):
public String readFile(String path) throws IOException, FileNotFoundException {
File file = new File(path);
StringBuilder string = new StringBuilder();
try (FileInputStream fileInputStream = new FileInputStream(file)) {
int singleCharInt;
char singleChar;
while((singleCharInt = fileInputStream.read()) != -1) {
singleChar = (char) singleCharInt;
string.append(singleChar);
}
}
return string.toString();
}
Мой основной метод выглядит следующим образом:
public static void main(String args[]) throws FileNotFoundException, IOException {
String bytes = new FileReader().readFile("/home/user/Code/Trie/bible.txt");
ArrayList<Integer> codes = new Compress().compress(bytes);
}
Мой класс Compress выглядит следующим образом:
public class Compress {
private int code = 0;
public ArrayList<Integer> compress(String data) {
Trie trie = new Trie();
// Initialize Trie Data Structure with alphabet (256 possible values with 8-bit
// symbols)
for (code = 0; code <= 255; code++) {
trie.insert(Character.toString((char) code), code);
}
code++;
String s = Character.toString(data.charAt(0));
ArrayList<Integer> codes = new ArrayList<Integer>();
for (int i = 1; i < data.length(); i++) {
String c = Character.toString(data.charAt(i));
if (trie.find(s + c) > 0) {
s += c;
} else {
codes.add(trie.find(s));
trie.insert(s + c, code);
code++;
s = c;
}
}
codes.add(trie.find(s));
return codes;
}
}
Мой класс Trie выглядит так:
public class Trie {
private TrieNode root;
public Trie() {
this.root = new TrieNode(false);
}
public void insert (String word, int code) {
TrieNode current = root;
for (char l: word.toCharArray()) {
current = current.getChildren().computeIfAbsent(Character.toString(l), c -> new TrieNode(false));
}
current.setCode(code);
current.setWordEnd(true);
}
public int find(String word) {
TrieNode current = root;
for (int i = 0 ; i < word.length(); i++) {
char ch = word.charAt(i);
TrieNode node = current.getChildren().get(Character.toString(ch));
if (node == null) {
return -1;
}
current = node;
}
return current.getCode();
}
}
Мой класс TrieNode выглядит так:
public class TrieNode {
private HashMap<String, TrieNode> children;
private int code;
private boolean wordEnd;
public TrieNode(boolean wordEnd) {
this.children = new HashMap<String, TrieNode>();
this.wordEnd = wordEnd;
}
public HashMap<String, TrieNode> getChildren() {
return this.children;
}
public void setWordEnd(boolean wordEnd) {
this.wordEnd = wordEnd;
}
public boolean isWordEnd() {
return this.wordEnd;
}
public int getCode() {
return this.code;
}
public void setCode(int code) {
this.code = code;
}
}
Спасибо за уделенное время!
int
,char
иbyte
, того, как они представляют данные и как Java выполняет преобразование между ними. Вы не получаете сжатия, потому что используете 4 байта (int
) для хранения каждого байта, т.е. занимая в 4 раза больше места. Кроме того, хотя я не исследовал ваш код подробно, вы также можете внести искажения при преобразовании int-to-char. Этих основ достаточно для целой главы учебника, и они слишком широки для Stack Overflow. - person Jim Garrison   schedule 31.01.2021int
, но все входные значения, с которыми вы работаете, имеют длину 1 байт (0x00-0xFF), и, поскольку вы ожидаете 250 000 кодов, вы будете использовать кодирование переменной длины. Чтобы фактически записать сжатые данные в файл, вы собираетесь преобразовать эти коды в битовые строки переменной длины, упаковать их в буфер и записать полученные байты. Здесь будут полезны буферы NIO. В вашем коде не должно бытьchar
илиString
, потому что входные данные должны обрабатываться как двоичные, без преобразования набора символов. - person Jim Garrison   schedule 31.01.2021