Sissejuhatus
Selles artiklis rÀÀgin ma tuntud Huffmani algoritmist ja selle rakendamisest andmete tihendamisel.
KokkuvĂ”ttes kirjutame lihtsa arhivaatri. Sellest on juba olnud , kuid praktilise rakenduseta. KĂ€esoleva postituse teoreetilised materjalid on saadud kooli informaatika tundidest ja Robert LaFore'i raamatust âData Structures and Algorithms in Javaâ. Nii et, kĂ”ik allolevat lugema!
Veidi mÔtteid
Tavalises tekstifailis kodeeritakse ĂŒks sĂŒmbol 8 bitiga (ASCII kodeering) vĂ”i 16 bitiga (Unicode kodeering). JĂ€rgmisena vaatleme ASCII kodeeringut. NĂ€iteks vĂ”tame stringi s1 = "SUSIE SAYS IT IS EASYn". Kokku on stringis 22 sĂŒmbolit, sealhulgas tĂŒhikud ja rida vahetamise sĂŒmbol â 'n'. Fail, mis sisaldab seda stringi, kaalub 22*8 = 176 bitti. Tekib kohe kĂŒsimus: kas on mĂ”istlik kasutada kĂ”iki 8 bitti ĂŒhe sĂŒmboli kodeerimiseks? Me ei kasuta ju kĂ”iki ASCII kodeeringu sĂŒmboleid. Isegi kui kasutaksime, oleks mĂ”istlikum kĂ”ige sagedasema tĂ€he â S â jaoks anda lĂŒhim vĂ”imalik kood ning kĂ”ige haruldasema tĂ€he â T (vĂ”i U, vĂ”i 'n') jaoks anda pikem kood. See ongi Huffmani algoritmi pĂ”himĂ”te: leida optimaalne kodeerimisvariant, mille korral fail on minimaalse kaaluga. On tĂ€iesti normaalne, et erinevatel sĂŒmbolitel on koodipikkused erinevad â see ongi algoritmi toime.
Kodeerimine
Miks ei vĂ”iks sĂŒmbol âSâ saada koodi, nĂ€iteks, 1 bitiga: 0 vĂ”i 1. Oletame, et see on 1. Siis anname teisele sagedamini esinevale sĂŒmbolile â â â(tĂŒhik) â 0. Kujutage ette, et hakkate dekodeerima oma sĂ”numit â kodeeritud stringi s1 â ja nĂ€ete, et kood algab 1-ga. Nii et, mida teha: kas see on sĂŒmbol S vĂ”i mĂ”ni muu sĂŒmbol, nĂ€iteks A? SeetĂ”ttu tekib oluline reegel:
Ăkski kood ei tohi olla teise eesliide
See reegel on algoritmi vĂ”tmeelement. SeetĂ”ttu alustatakse koodi loomist sagedustabelist, kus on nĂ€idatud iga sĂŒmboli sagedus (esinevuste arv):
SĂŒmbolid, millel on kĂ”ige rohkem esinemisi, peaksid olema kodeeritud vĂ”imalikult vĂ€heste bitidega. Tooksin nĂ€ite ĂŒhest vĂ”imalikust kooditabelist:
Nii et, kodeeritud sÔnum nÀeb vÀlja nii:
10 01111 10 110 1111 00 10 010 1110 10 00 110 0110 00 110 10 00 1111 010 10 1110 01110 Iga sĂŒmboli koodi eraldasin tĂŒhikuga. TĂ”eliselt tihendatud failis sellist ei ole!
TĂ”statub kĂŒsimus: kuidas see nooruk mĂ”tles vĂ€lja koodi koodide tabeli loomiseks? Sellest allpool.
Huffman'i puu loomine
Siin tulevad appi binaarsed otsingupuud. Ăra muretse, siin ei ole vajalikud otsimise, sisestamise ja kustutamise meetodid. Siin on puu struktuur Javaâs:
public class Node {
private int frequence;
private char letter;
private Node leftChild;
private Node rightChild;
...
}
class BinaryTree {
private Node root;
public BinaryTree() {
root = new Node();
}
public BinaryTree(Node root) {
this.root = root;
}
...
}
See ei ole tÀielik kood, tÀielik kood tuleb allpool.
Siin on ise algoritm puu loomise jaoks:
- Loo iga sĂŒmboli jaoks Node objekt sĂ”numist (string s1). Meie puhul tuleb 9 sĂ”lme (Node objekti). Iga sĂ”lm koosneb kahest andmevĂ€ljast: sĂŒmbol ja sagedus.
- Loo BinaryTree objekt igasuguse Node sÔlme jaoks. SÔlm muutub puu juureks.
- Sisesta need puud prioriteetjÀrjekorda. Mida madalam on sagedus, seda suurem on prioriteet. Seega valitakse alati vÀhima sagedusega puu.
SeejĂ€rel tuleb tsĂŒkliliselt teha jĂ€rgmist:
- TĂ”sta kaks puu prioriteetjĂ€rjekorrast ja tee neist uue sĂ”lme (just loodud sĂ”lm ilma sĂŒmbolita) jĂ€reltulijad. Uue sĂ”lme sagedus on kahe jĂ€reltulija puu sageduste summa.
- Selle sÔlme jaoks loo puu, mille juur on antud sÔlm. Aseta see puu tagasi prioriteetjÀrjekorda. (Kuna puul on uus sagedus, ilmselt jÀÀb see jÀrjekorras uude kohta.)
- JĂ€tka sammude 1 ja 2 tĂ€itmist, kuni jĂ€rjekorras jÀÀb ainult ĂŒks puu â Huffmani puu.
RÀÀgime sellest algoritmist stringil s1:

Siin tĂ€histab sĂŒmbol âlfâ (linefeed) uut rida, âspâ (space) on tĂŒhik.
Ja mis siis edasi?
Me saime Huffmani puu. Noh, okei. Ja mida sellega teha? Seda ei vĂ”eta isegi tasuta. SeejĂ€rel tuleb jĂ€lgida kĂ”iki vĂ”imalikke teid juurest puu lehtedeni. Kokku leppida, et serv 0 tĂ€histab vasakut jĂ€reltulijat ja 1 â paremat. Rangelt öeldes, antud tĂ€histustes on sĂŒmboli kood tee puu juurest lehteni, mis sisaldab antud sĂŒmbolit.

Nii, nĂŒĂŒd on meil tekkinud koodide tabel. Kui seda tabelit vaadata, siis vĂ”ime jĂ€reldada iga sĂŒmboli âkaaluâ â see on tema koodi pikkus. SeetĂ”ttu kaalub algfail kokku: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 bitti. Alguses oli selle kaal 176 bitti. Seega oleme seda vĂ€hendanud 176/65 = 2.7 korda! Kuid see on utoopia. Sellist koefitsenti on ebatĂ”enĂ€oline saavutada. Miks? Sellest rÀÀgime veidi hiljem.
Dekodeerimine
Noh, ilmselt on jÀÀnud kĂ”ige lihtsam â dekodeerimine. Ma arvan, et paljud teist mĂ”istsid, et lihtsalt kokku suruda fail ilma igasuguste vihjeteta selle kodeerimise kohta ei Ă”nnestu â me ei suuda seda dekodeerida! Jah, mul oli seda raske mĂ”ista, aga peame looma tekstifaili table.txt kompressioonitabeliga:
01110
00
A010
E1111
I110
S10
T0110
U01111
Y1110
Tabeli kirjutamine kujul 'sĂŒmbol'«sĂŒmboli kood». Miks on 01110 ilma sĂŒmbolita? Tegelikult on seal sĂŒmbol, lihtsalt java vahendid, mida kasutasin faili vĂ€ljundiks, konverteerivad rea vahetuse sĂŒmboli â 'n' â rea vahetuseks (kui rumal see ka ei tunduks). SeetĂ”ttu on ĂŒlal tĂŒĂŒtu tĂŒhi rida sĂŒmbol koodi 01110 jaoks. Koodi 00 sĂŒmboliks on rea alguses tĂŒhik. Ătlen kohe, et meie koefitsiendile on selle tabeli salvestamise viis ilmselt kĂ”ige ebaefektiivsem. Kuid see on arusaadav ja teostatav. Ootan hea meelega teie soovitusi kommentaarides optimeerimise kohta.
Selle tabeliga on dekodeerimine vÀga lihtne. Meenutame, millist reeglit kasutasime kodeerimise loomisel:
Ăkski kood ei tohi olla teise prefix.
Siin mĂ€ngib see hĂ”lbustavat rolli. Me loeme jĂ€rjestikku bitti ja kui saadud string d, mis koosneb loetud bittidest, vastab koodile, mis vastab sĂŒmbolile character, teame kohe, et sĂŒmbol character on kodeeritud (ja ainult tema!). JĂ€tkame character'i kirjutamist dekodeerimistringi (string, mis sisaldab dekodeeritud sĂ”numit), seadistame stringi d nulliks ja loeme edasi kodeeritud faili.
Rakendamine
On aeg alandada minu koodi kirjutada arhivator. Nimeks paneme sellele Compressor.
Alustame algusest. Esmalt kirjutame klassi Node:
public class Node {
private int frequence; // sagedus
private char letter; // tÀht
private Node leftChild; // vasak laps
private Node rightChild; // parempoolne laps
public Node(char letter, int frequence) { // konstruktor
this.letter = letter;
this.frequence = frequence;
}
public Node() {} // konstruktor ilma nimega (vt ĂŒlal ĂŒhtepuutuv osa Huffmani puu ehitamisest)
public void addChild(Node newNode) { // lisada laps
if (leftChild == null) // kui vasak on tĂŒhi => parem ka tĂŒhjaks => lisame vasakule
leftChild = newNode;
else {
if (leftChild.getFrequence() <= newNode.getFrequence()) // sisuliselt, vasak laps
rightChild = newNode; // saab see, kelle sagedus on vÀiksem
else {
rightChild = leftChild;
leftChild = newNode;
}
}
frequence += newNode.getFrequence(); // lÔpp sagedus
}
public Node getLeftChild() {
return leftChild;
}
public Node getRightChild() {
return rightChild;
}
public int getFrequence() {
return frequence;
}
public char getLetter() {
return letter;
}
public boolean isLeaf() { // lehe kontroll
return leftChild == null && rightChild == null;
}
}
NĂŒĂŒd puu:
class BinaryTree {
private Node root;
public BinaryTree() {
root = new Node();
}
public BinaryTree(Node root) {
this.root = root;
}
public int getFrequence() {
return root.getFrequence();
}
public Node getRoot() {
return root;
}
}
Prioriteetne jÀrjekord:
import java.util.ArrayList; // jah, jÀrjekord pÔhineb loendil
class PriorityQueue {
private ArrayList data; // jÀrjekorra loend
private int nElems; // elementide arv jÀrjekorras
public PriorityQueue() {
data = new ArrayList();
nElems = 0;
}
public void insert(BinaryTree newTree) { // sisestamine
if (nElems == 0)
data.add(newTree);
else {
for (int i = 0; i newTree.getFrequence()) { // kui sisestatud puu sagedus on vÀiksem
data.add(i, newTree); // kui on vĂ€iksem, nihutame kĂ”ik puud paremal positsioonil ĂŒhe vĂ”rra edasi
break; // seejÀrel asetame uue puu praeguse positsiooni kohale
}
if (i == nElems - 1)
data.add(newTree);
}
}
nElems++; // suurendame elementide arvu 1 vÔrra
}
public BinaryTree remove() { // eemaldamine jÀrjekorrast
BinaryTree tmp = data.get(0); // kopeerime eemaldatava elemendi
data.remove(0); // eemaldame
nElems--; // vÀhendame elementide arvu 1 vÔrra
return tmp; // tagastame eemaldatud elemendi (element, millel on kÔige madalam sagedus)
}
}
Klass, mis loob Huffmani puu:
public class HuffmanTree {
private final byte ENCODING_TABLE_SIZE = 127; // kooditabeli pikkus
private String myString; // sÔnum
private BinaryTree huffmanTree; // Huffmani puu
private int[] freqArray; // sagedustabel
private String[] encodingArray; // kodeerimistabel
// ----------------constructor----------------------
public HuffmanTree(String newString) {
myString = newString;
freqArray = new int[ENCODING_TABLE_SIZE];
fillFrequenceArray();
huffmanTree = getHuffmanTree();
encodingArray = new String[ENCODING_TABLE_SIZE];
fillEncodingArray(huffmanTree.getRoot(), "", "");
}
// --------------------frequence array------------------------
private void fillFrequenceArray() {
for (int i = 0; i < myString.length(); i++) {
freqArray[(int)myString.charAt(i)]++;
}
}
public int[] getFrequenceArray() {
return freqArray;
}
// ------------------------huffman tree creation------------------
private BinaryTree getHuffmanTree() {
PriorityQueue pq = new PriorityQueue();
// algoritm eespool
for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
if (freqArray[i] != 0) { // kui sĂŒmbol eksisteerib stringis
Node newNode = new Node((char) i, freqArray[i]); // luua Node
BinaryTree newTree = new BinaryTree(newNode); // ning Node'ile luua BinaryTree
pq.insert(newTree); // sisestada jÀrjekorda
}
}
while (true) {
BinaryTree tree1 = pq.remove(); // eemaldada jÀrjekorrast esimene puu.
try {
BinaryTree tree2 = pq.remove(); // eemaldada jÀrjekorrast teine puu
Node newNode = new Node(); // luua uus Node
newNode.addChild(tree1.getRoot()); // teha kahest eemaldatud puust selle jÀreltulijad
newNode.addChild(tree2.getRoot());
pq.insert(new BinaryTree(newNode));
} catch (IndexOutOfBoundsException e) { // jĂ€rjekorras jĂ€i alles ĂŒks puu
return tree1;
}
}
}
public BinaryTree getTree() {
return huffmanTree;
}
// -------------------encoding array------------------
void fillEncodingArray(Node node, String codeBefore, String direction) { // tÀita kodeerimistabel
if (node.isLeaf()) {
encodingArray[(int)node.getLetter()] = codeBefore + direction;
} else {
fillEncodingArray(node.getLeftChild(), codeBefore + direction, "0");
fillEncodingArray(node.getRightChild(), codeBefore + direction, "1");
}
}
String[] getEncodingArray() {
return encodingArray;
}
public void displayEncodingArray() { // veaks
fillEncodingArray(huffmanTree.getRoot(), "", "");
System.out.println("======================Encoding table====================");
for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
if (freqArray[i] != 0) {
System.out.print((char)i + " ");
System.out.println(encodingArray[i]);
}
}
System.out.println("========================================================");
}
// -----------------------------------------------------
String getOriginalString() {
return myString;
}
}
Klass, mis sisaldab kodeerimist/dekodeerimist:
public class HuffmanOperator {
private final byte ENCODING_TABLE_SIZE = 127; // tabeli pikkus
private HuffmanTree mainHuffmanTree; // Huffmani puu (kasutatakse ainult kokkusurumiseks)
private String myString; // algne sÔnum
private int[] freqArray; // sagedustabel
private String[] encodingArray; // kodeerimistabel
private double ratio; // kokkusurumise suhe
public HuffmanOperator(HuffmanTree MainHuffmanTree) { // kokkusurumiseks
this.mainHuffmanTree = MainHuffmanTree;
myString = mainHuffmanTree.getOriginalString();
encodingArray = mainHuffmanTree.getEncodingArray();
freqArray = mainHuffmanTree.getFrequenceArray();
}
public HuffmanOperator() {} // vÀljavÔtmiseks;
// ---------------------------------------kokkusurumine-----------------------------------------------------------
private String getCompressedString() {
String compressed = "";
String intermidiate = ""; // vahestring (ilma tÀiendavate nullideta)
// System.out.println("=============================Compression=======================");
// displayEncodingArray();
for (int i = 0; i
// tuleb lisada nullid lÔpuks (vÔib olla 1, pole vahet)
byte counter = 0; // lisatud nullide arv (baidi jaoks piisab: 0 <= counter < 8 < 127)
for (int length = intermidiate.length(), delta = 8 - length % 8;
counter < delta; counter++) { // delta - lisatud nullide arv
intermidiate += "0";
}
// liida lisatud nullide arv binaarses esituses ja vahestring
compressed = String.format("%8s", Integer.toBinaryString(counter & 0xff)).replace(" ", "0") + intermidiate;
// idealiseeritud suhe
setCompressionRatio();
// System.out.println("===============================================================");
return compressed;
}
private void setCompressionRatio() { // ideaalset suhet arvutada
double sumA = 0, sumB = 0; // A - originaalsumma
for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
if (freqArray[i] != 0) {
sumA += 8 * freqArray[i];
sumB += encodingArray[i].length() * freqArray[i];
}
}
ratio = sumA / sumB;
}
public byte[] getBytedMsg() { // lÔplik kokkusurumine
StringBuilder compressedString = new StringBuilder(getCompressedString());
byte[] compressedBytes = new byte[compressedString.length() / 8];
for (int i = 0; i < compressedBytes.length; i++) {
compressedBytes[i] = (byte) Integer.parseInt(compressedString.substring(i * 8, (i + 1) * 8), 2);
}
return compressedBytes;
}
// ---------------------------------------kokkusurumise lÔpp----------------------------------------------------------------
// ------------------------------------------------------------vÀljavÔttmine-----------------------------------------------------
public String extract(String compressed, String[] newEncodingArray) {
String decompressed = "";
String current = "";
String delta = "";
encodingArray = newEncodingArray;
// displayEncodingArray();
// saada teada lisatud nullide arv
for (int i = 0; i < 8; i++)
delta += compressed.charAt(i);
int ADDED_ZEROES = Integer.parseInt(delta, 2);
for (int i = 8, l = compressed.length() - ADDED_ZEROES; i < l; i++) {
// i = 8, kuna esimeseks bait on lisatud nullide arv
current += compressed.charAt(i);
for (int j = 0; j < ENCODING_TABLE_SIZE; j++) {
if (current.equals(encodingArray[j])) { // kui sobib
decompressed += (char)j; // siis lisame elemendi
current = ""; // ja nullime praeguse stringi
}
}
}
return decompressed;
}
public String getEncodingTable() {
String enc = "";
for (int i = 0; i < encodingArray.length; i++) {
if (freqArray[i] != 0)
enc += (char)i + encodingArray[i] + 'n';
}
return enc;
}
public double getCompressionRatio() {
return ratio;
}
public void displayEncodingArray() { // silumise jaoks
System.out.println("======================Kodeerimistabel====================");
for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
// if (freqArray[i] != 0) {
System.out.print((char)i + " ");
System.out.println(encodingArray[i]);
// }
}
System.out.println("========================================================");
}
}
Klass, mis lihtsustab faili kirjutamist:
import java.io.File;
import java.io.PrintWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.Closeable;
public class FileOutputHelper implements Closeable {
private File outputFile;
private FileOutputStream fileOutputStream;
public FileOutputHelper(File file) throws FileNotFoundException {
outputFile = file;
fileOutputStream = new FileOutputStream(outputFile);
}
public void writeByte(byte msg) throws IOException {
fileOutputStream.write(msg);
}
public void writeBytes(byte[] msg) throws IOException {
fileOutputStream.write(msg);
}
public void writeString(String msg) {
try (PrintWriter pw = new PrintWriter(outputFile)) {
pw.write(msg);
} catch (FileNotFoundException e) {
System.out.println("Vale tee, vÔi sellist faili ei eksisteeri!");
}
}
@Override
public void close() throws IOException {
fileOutputStream.close();
}
public void finalize() throws IOException {
close();
}
}
Klass, mis lihtsustab faili lugemist:
import java.io.FileInputStream;
import java.io.EOFException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
public class FileInputHelper implements Closeable {
private FileInputStream fileInputStream;
private BufferedReader fileBufferedReader;
public FileInputHelper(File file) throws IOException {
fileInputStream = new FileInputStream(file);
fileBufferedReader = new BufferedReader(new InputStreamReader(fileInputStream));
}
public byte readByte() throws IOException {
int cur = fileInputStream.read();
if (cur == -1) // kui fail on lÔppenud
throw new EOFException();
return (byte)cur;
}
public String readLine() throws IOException {
return fileBufferedReader.readLine();
}
@Override
public void close() throws IOException{
fileInputStream.close();
}
}
Ja peamine klass:
import java.io.File;
import java.nio.charset.MalformedInputException;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.List;
import java.io.EOFException;
public class Main {
private static final byte ENCODING_TABLE_SIZE = 127;
public static void main(String[] args) throws IOException {
try { // specify instruction using command line arguments
if (args[0].equals("--compress") || args[0].equals("-c"))
compress(args[1]);
else if ((args[0].equals("--extract") || args[0].equals("-x"))
&& (args[2].equals("--table") || args[2].equals("-t"))) {
extract(args[1], args[3]);
}
else
throw new IllegalArgumentException();
} catch (ArrayIndexOutOfBoundsException | IllegalArgumentException e) {
System.out.println("Vale vormingud on vale");
System.out.println("Palun vaadake Readme.txt");
e.printStackTrace();
}
}
public static void compress(String stringPath) throws IOException {
List stringList;
File inputFile = new File(stringPath);
String s = "";
File compressedFile, table;
try {
stringList = Files.readAllLines(Paths.get(inputFile.getAbsolutePath()));
} catch (NoSuchFileException e) {
System.out.println("Vale rada, vÔi sellist faili ei eksisteeri!");
return;
} catch (MalformedInputException e) {
System.out.println("Faili praegune kodeering ei ole toetatud");
return;
}
for (String item : stringList) {
s += item;
s += 'n';
}
HuffmanOperator operator = new HuffmanOperator(new HuffmanTree(s));
compressedFile = new File(inputFile.getAbsolutePath() + ".cpr");
compressedFile.createNewFile();
try (FileOutputHelper fo = new FileOutputHelper(compressedFile)) {
fo.writeBytes(operator.getBytedMsg());
}
// create file with encoding table:
table = new File(inputFile.getAbsolutePath() + ".table.txt");
table.createNewFile();
try (FileOutputHelper fo = new FileOutputHelper(table)) {
fo.writeString(operator.getEncodingTable());
}
System.out.println("Komprimitud faili rada: " + compressedFile.getAbsolutePath());
System.out.println("Kodeerimistabeli rada " + table.getAbsolutePath());
System.out.println("Ilma tabelita faili ei saa taastada!");
double idealRatio = Math.round(operator.getCompressionRatio() * 100) / (double) 100; // idealiseeritud koefitsent
double realRatio = Math.round((double) inputFile.length()
/ ((double) compressedFile.length() + (double) table.length()) * 100) / (double)100; // reaalne koefitsent
System.out.println("Ideaalne kompressioonikoefitsent on " + idealRatio);
System.out.println("Kompressioonikoefitsent arvestades kodeerimistabelit " + realRatio);
}
public static void extract(String filePath, String tablePath) throws FileNotFoundException, IOException {
HuffmanOperator operator = new HuffmanOperator();
File compressedFile = new File(filePath),
tableFile = new File(tablePath),
extractedFile = new File(filePath + ".xtr");
String compressed = "";
String[] encodingArray = new String[ENCODING_TABLE_SIZE];
// read compressed file
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!check here:
try (FileInputHelper fi = new FileInputHelper(compressedFile)) {
byte b;
while (true) {
b = fi.readByte(); // method returns EOFException
compressed += String.format("%8s", Integer.toBinaryString(b & 0xff)).replace(" ", "0");
}
} catch (EOFException e) {
}
//--------------------
// read encoding table:
try (FileInputHelper fi = new FileInputHelper(tableFile)) {
fi.readLine(); // skip first empty string
encodingArray[(byte)'n'] = fi.readLine(); // read code for 'n'
while (true) {
String s = fi.readLine();
if (s == null)
throw new EOFException();
encodingArray[(byte)s.charAt(0)] = s.substring(1, s.length());
}
} catch (EOFException ignore) {}
extractedFile.createNewFile();
// extract:
try (FileOutputHelper fo = new FileOutputHelper(extractedFile)) {
fo.writeString(operator.extract(compressed, encodingArray));
}
System.out.println("TaaskÀitatud faili rada " + extractedFile.getAbsolutePath());
}
}
Te peate ise kirjutama readme.txt faili koos juhistega đ
KokkuvÔte
TĂ”enĂ€oliselt on see kĂ”ik, mida ma öelda tahtsin. Kui teil on midagi öelda minu ebakompetentsuse kohta koodikitsendustes, algoritmis vĂ”i ĂŒldse mĂ”nes optimeerimises, kirjutage julgelt. Kui ma ei ole midagi piisavalt selgitanud, kirjutage samuti. Ootan teid kommenteerimisse!
P.S.
Jah, ma olen endiselt siin, sest ma ei unustanud koefitsienti. Stringi s1 kodeeringutabel kaalub 48 baiti â palju rohkem kui algne fail, ja me ei unustanud lisada nullide arvu (lisatud nullide arv on 7) => koefitsient on vĂ€iksem kui ĂŒks: 176/(65 + 48*8 + 7)=0.38. Kui teie ka seda mĂ€rkasite, siis olete tĂ”eliselt tubli. Jah, see rakendus on vĂ€ikeste failide jaoks ÀÀrmiselt ebaefektiivne. Aga mis juhtub suurte failidega? Failide suurus ĂŒletab oluliselt kodeeringutabeli suurust. Siin töötab algoritm tĂ”eliselt hĂ€sti! NĂ€iteks,â,âš arhivaatoreid pakub tegelikku (mitte ideaalset) koefitsienti, mis on 1.46 â peaaegu poole rohkem! Ja jah, eeldati, et fail on inglise keeles.
Allikas: habr.com
