Dùmhlachadh dàta leis an algairim Huffman

Clàrachadh

San artaigil seo, bruidhnidh mi mun algairim Huffman ainmeil, a bharrachd air a chleachdadh ann an teannachadh dàta.

Mar thoradh air an sin, sgrìobhaidh sinn tasglann sìmplidh. Tha seo air a bhith mar-thà artaigil air Habré, ach às aonais buileachadh practaigeach. Tha stuth teòiridheach na dreuchd làithreach air a thoirt bho leasanan saidheans coimpiutair na sgoile agus leabhar Robert Laforet "Data Structures and Algorithms in Java". Mar sin, tha a h-uile dad fon ghearradh!

Beagan meòrachadh

Ann am faidhle teacsa àbhaisteach, tha aon charactar air a chòdachadh le 8 pìosan (còdachadh ASCII) no 16 (còdachadh Unicode). An ath rud, beachdaichidh sinn air còdachadh ASCII. Mar eisimpleir, gabh an sreang s1 = "Tha SUSIE ag ràdh gur e EASYn a th’ ann". Gu h-iomlan, tha 22 caractaran san loidhne, gu dearbh, a’ toirt a-steach beàrnan agus an caractar loidhne ùr - ‘n’. Bidh cuideam 22 * ​​8 = 176 bit anns an fhaidhle anns a bheil an loidhne seo. Tha a’ cheist ag èirigh sa bhad: a bheil e reusanta na 8 pìosan gu lèir a chleachdadh gus 1 caractar a chòdachadh? Cha bhith sinn a’ cleachdadh a h-uile caractar ASCII. Fiù 's nan robh, bhiodh e na b' reusanta an litir as trice a thoirt seachad - S - an còd as giorra a ghabhas a dhèanamh, agus airson na litreach as teirce - T (no U, no 'n') - thoir an còd nas dearbhte. Is e seo an algairim Huffman: feumaidh tu an roghainn còdaidh as fheàrr a lorg, anns am bi am faidhle aig a’ chuideam as ìsle. Tha e gu math àbhaisteach gum bi faid còd eadar-dhealaichte aig caractaran eadar-dhealaichte - is e seo bunait an algairim.

Còdadh

Carson nach toir thu còd don charactar 'S', mar eisimpleir, 1 pìos a dh'fhaid: 0 no 1. Leig leis gur e 1 a th' ann. dì-chòdaich do theachdaireachd - sreang air a chòdachadh s0 - agus chì thu gu bheil an còd a' tòiseachadh le 1. Mar sin, dè nì thu: an e an caractar S a th' ann, neo an e caractar eile a th' ann, leithid A? Mar sin, tha riaghailt chudromach ag èirigh:

Chan fhaod còd sam bith a bhith na ro-leasachan air fear eile

Is e an riaghailt seo an iuchair don algairim. Mar sin, tha cruthachadh a 'chòd a' tòiseachadh le clàr tricead, a tha a 'sealltainn tricead (àireamh thachartasan) gach samhla:

Dùmhlachadh dàta leis an algairim Huffman Bu chòir na caractaran leis a’ mhòr-chuid de thachartasan a chòdachadh leis an fheadhainn as lugha comasach an àireamh de bhuillean. Bheir mi eisimpleir de aon de na clàran còd a dh’ fhaodadh a bhith ann:

Dùmhlachadh dàta leis an algairim Huffman Mar sin seallaidh an teachdaireachd còdaichte mar seo:

10 01111 10 110 1111 00 10 010 1110 10 00 110 0110 00 110 10 00 1111 010 10 1110 01110

Dhealaich mi còd gach caractar le àite. Cha tachair seo dha-rìribh ann am faidhle teannachaidh!
Tha a 'cheist ag èirigh: ciamar a thàinig an rookie seo suas le còd mar a chruthaicheas tu clàr còd? Thèid seo a dheasbad gu h-ìosal.

A 'togail craobh Huffman

Seo far am bi craobhan sgrùdaidh binary a’ tighinn gu teasairginn. Na gabh dragh, cha bhith feum agad air na dòighean sgrùdaidh, cuir a-steach agus cuir às an seo. Seo structar na craoibhe ann an java:

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;
    }
    ...
}

Chan e seo an còd iomlan, bidh an còd slàn gu h-ìosal.

Seo an algairim airson craobh a thogail:

  1. Cruthaich nì Node airson gach caractar bhon teachdaireachd (loidhne s1). Anns a 'chùis againn, bidh 9 nodan (Node Rudan). Tha dà raon dàta anns gach nód: samhla agus tricead
  2. Cruthaich rud craobh (BinaryTree) airson gach aon de na nodan Node. Bidh an nód gu bhith na bhunait don chraoibh.
  3. Cuir na craobhan sin a-steach don ciudha prìomhachais. Mar as ìsle an tricead, is ann as àirde am prìomhachas. Mar sin, nuair a thèid an toirt a-mach, bidh an craobh leis an tricead as ìsle an-còmhnaidh air a thaghadh.

An ath rud, feumaidh tu na leanas a dhèanamh gu cearcallach:

  1. Faigh dà chraobh air ais bhon ciudha prìomhachais agus dèan clann dhaibh de nód ùr (nòd ùr gun litir). Tha tricead an nód ùr co-ionann ri suim tricead an dà chraobh sliochd.
  2. Airson an nód seo, cruthaich craobh le freumhachadh aig an nód seo. Cuir a’ chraobh seo air ais dhan ciudha prìomhachais. (Leis gu bheil tricead ùr aig a’ chraoibh, is coltaiche gun tèid i a-steach gu àite ùr sa chiudha)
  3. Lean air adhart ceumannan 1 agus 2 gus am bi aon chraobh air fhàgail sa chiudha - craobh Huffman

Beachdaich air an algairim seo air loidhne s1:

Dùmhlachadh dàta leis an algairim Huffman

An seo tha an samhla "lf" (linefeed) a 'comharrachadh loidhne ùr, tha "sp" (space) na àite.

Agus dè a tha dol?

Fhuair sinn craobh Huffman. ceart gu leòr. Agus dè a dhèanamh leis? Cha toir iad an-asgaidh e, agus an uairsin, feumaidh tu a h-uile slighe a lorg bhon fhreumh gu duilleagan na craoibhe. Tha sinn ag aontachadh oir 0 a chomharrachadh ma tha e a’ leantainn chun leanabh clì agus 1 ma tha e a’ leantainn chun fhear cheart. Gu cruaidh, anns na comharran seo, is e an còd caractar an t-slighe bho fhreumh na craoibhe chun na duilleige anns a bheil an aon charactar seo.

Dùmhlachadh dàta leis an algairim Huffman

Mar sin, thionndaidh an clàr còdan a-mach. Thoir an aire ma tha sinn a 'beachdachadh air a' chlàr seo, faodaidh sinn co-dhùnadh mu "cuideam" gach caractar - is e seo fad a chòd. An uairsin, ann an cruth teann, bidh cuideam am faidhle tùsail: 2 * 3 + 2 * 4 + 3 * 3 + 6 * 2 + 1 * 4 + 1 * 5 + 2 * 4 + 4 * 2 + 1 * 5 = 65 pìosan . An toiseach bha cuideam 176 pìosan ann. Mar sin, lughdaich sinn e cho mòr ri 176/65 = 2.7 uair! Ach is e utopia a tha seo. Chan eil e coltach gum faighear a leithid de cho-mheas. Carson? Thèid seo a dheasbad beagan nas fhaide air adhart.

Dì-chòdachadh

Uill, is dòcha gur e dì-chòdachadh an rud as sìmplidh a tha air fhàgail. Tha mi a’ smaoineachadh gu bheil mòran agaibh air tomhas gu bheil e do-dhèanta dìreach faidhle teann a chruthachadh gun sanasan sam bith air mar a chaidh a chòdachadh - cha bhith e comasach dhuinn a chòdachadh! Bha, bha, bha e duilich dhomh seo a thoirt gu buil, ach feumaidh mi clàr faidhle teacsa.txt a chruthachadh le clàr teannachaidh:

01110
 00
A010
E1111
I110
S10
T0110
U01111
Y1110

Inntrigeadh clàr san fhoirm 'caractar'"còd caractar". Carson a tha 01110 gun samhla? Gu dearbh, is ann le samhla a tha e, dìreach na h-innealan java a bhios mi a 'cleachdadh nuair a bhios mi a' cur a-mach gu faidhle, tha an caractar loidhne ùr - 'n' - air a thionndadh gu loidhne ùr (ge bith dè cho gòrach 'sa tha e). Mar sin, is e an loidhne falamh gu h-àrd an caractar airson còd 01110. Airson còd 00, tha an caractar na àite aig toiseach na loidhne. Feumaidh mi a ràdh anns a 'bhad gum faod an dòigh seo air a' bhòrd a stòradh a bhith ag ràdh gur e seo an dòigh as neo-reusanta airson ar co-èifeachd khan. Ach tha e furasta a thuigsinn agus a chur an gnìomh. Bidh mi toilichte na molaidhean agad a chluinntinn anns na beachdan mu optimization.

Leis a 'chlàr seo, tha e gu math furasta a dhì-chòdachadh. Cuimhnichidh sinn dè an riaghailt a bha sinn air ar stiùireadh nuair a bha sinn a’ cruthachadh a’ chòdachaidh:

Chan fhaod còd sam bith a bhith na ro-leasachan air fear eile

Seo far a bheil e a 'cluich pàirt cuideachaidh. Bidh sinn a’ leughadh mean air mhean ann an sreath, agus cho luath ‘s a bhios an t-sreang d a thig às, anns a bheil na pìosan leughaidh, a’ maidseadh a’ chòdachadh a tha co-chosmhail ri caractar a’ charactar, tha fios againn sa bhad gun deach an caractar caractar (agus dìreach e!) a chòdachadh. An uairsin, bidh sinn a’ sgrìobhadh caractar chun t-sreang dì-chòdachadh (an t-sreang anns a bheil an teachdaireachd dì-chòdaichte), ath-shuidhich an sreang d, agus leugh am faidhle còdaichte tuilleadh.

Реализация

Tha an t-àm ann mo chòd a mhaslachadh le bhith a’ sgrìobhadh tasglann. Canaidh sinn Compressor ris.

Tòisich thairis. An toiseach, bidh sinn a’ sgrìobhadh an clas Node:

public class Node {
    private int frequence;//частота
    private char letter;//буква
    private Node leftChild;//левый потомок
    private Node rightChild;//правый потомок

   

    public Node(char letter, int frequence) { //собственно, конструктор
        this.letter = letter;
        this.frequence = frequence;
    }

    public Node() {}//перегрузка конструтора для безымянных узлов(см. выше в разделе о построении дерева Хаффмана)
    public void addChild(Node newNode) {//добавить потомка
        if (leftChild == null)//если левый пустой=> правый тоже=> добавляем в левый
            leftChild = newNode;
        else {
            if (leftChild.getFrequence() <= newNode.getFrequence()) //в общем, левым потомком
                rightChild = newNode;//станет тот, у кого меньше частота
            else {
                rightChild = leftChild;
                leftChild = newNode;
            }
        }

        frequence += newNode.getFrequence();//итоговая частота
    }

    public Node getLeftChild() {
        return leftChild;
    }

    public Node getRightChild() {
        return rightChild;
    }

    public int getFrequence() {
        return frequence;
    }

    public char getLetter() {
        return letter;
    }

    public boolean isLeaf() {//проверка на лист
        return leftChild == null && rightChild == null;
    }
}

A-nis an craobh:

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;
    }
}

Ciudha prìomhachais:

import java.util.ArrayList;//да-да, очередь будет на базе списка

class PriorityQueue {
    private ArrayList<BinaryTree> data;//список очереди
    private int nElems;//кол-во элементов в очереди

    public PriorityQueue() {
        data = new ArrayList<BinaryTree>();
        nElems = 0;
    }

    public void insert(BinaryTree newTree) {//вставка
        if (nElems == 0)
            data.add(newTree);
        else {
            for (int i = 0; i < nElems; i++) {
                if (data.get(i).getFrequence() > newTree.getFrequence()) {//если частота вставляемого дерева меньше 
                    data.add(i, newTree);//чем част. текущего, то cдвигаем все деревья на позициях справа на 1 ячейку                   
                    break;//затем ставим новое дерево на позицию текущего
                }
                if (i == nElems - 1) 
                    data.add(newTree);
            }
        }
        nElems++;//увеличиваем кол-во элементов на 1
    }

    public BinaryTree remove() {//удаление из очереди
        BinaryTree tmp = data.get(0);//копируем удаляемый элемент
        data.remove(0);//собственно, удаляем
        nElems--;//уменьшаем кол-во элементов на 1
        return tmp;//возвращаем удаленный элемент(элемент с наименьшей частотой)
    }
}

An clas a chruthaicheas craobh Huffman:

public class HuffmanTree {
    private final byte ENCODING_TABLE_SIZE = 127;//длина кодировочной таблицы
    private String myString;//сообщение
    private BinaryTree huffmanTree;//дерево Хаффмана
    private int[] freqArray;//частотная таблица
    private String[] encodingArray;//кодировочная таблица


    //----------------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();
        //алгоритм описан выше
        for (int i = 0; i < ENCODING_TABLE_SIZE; i++) {
            if (freqArray[i] != 0) {//если символ существует в строке
                Node newNode = new Node((char) i, freqArray[i]);//то создать для него Node
                BinaryTree newTree = new BinaryTree(newNode);//а для Node создать BinaryTree
                pq.insert(newTree);//вставить в очередь
            }
        }

        while (true) {
            BinaryTree tree1 = pq.remove();//извлечь из очереди первое дерево.

            try {
                BinaryTree tree2 = pq.remove();//извлечь из очереди второе дерево

                Node newNode = new Node();//создать новый Node
                newNode.addChild(tree1.getRoot());//сделать его потомками два извлеченных дерева
                newNode.addChild(tree2.getRoot());

                pq.insert(new BinaryTree(newNode);
            } catch (IndexOutOfBoundsException e) {//осталось одно дерево в очереди
                return tree1;
            }
        }
    }

    public BinaryTree getTree() {
        return huffmanTree;
    }

    //-------------------encoding array------------------
    void fillEncodingArray(Node node, String codeBefore, String direction) {//заполнить кодировочную таблицу
        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() {//для отладки
        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;
    }
}

An clas anns a bheil còdachadh/còdachadh:

public class HuffmanOperator {
    private final byte ENCODING_TABLE_SIZE = 127;//длина таблицы
    private HuffmanTree mainHuffmanTree;//дерево Хаффмана (используется только для сжатия)
    private String myString;//исходное сообщение
    private int[] freqArray;//частотаная таблица
    private String[] encodingArray;//кодировочная таблица
    private double ratio;//коэффициент сжатия 


    public HuffmanOperator(HuffmanTree MainHuffmanTree) {//for compress
        this.mainHuffmanTree = MainHuffmanTree;

        myString = mainHuffmanTree.getOriginalString();

        encodingArray = mainHuffmanTree.getEncodingArray();

        freqArray = mainHuffmanTree.getFrequenceArray();
    }

    public HuffmanOperator() {}//for extract;

    //---------------------------------------compression-----------------------------------------------------------
    private String getCompressedString() {
        String compressed = "";
        String intermidiate = "";//промежуточная строка(без добавочных нулей)
        //System.out.println("=============================Compression=======================");
        //displayEncodingArray();
        for (int i = 0; i < myString.length(); i++) {
            intermidiate += encodingArray[myString.charAt(i)];
        }
        //Мы не можем писать бит в файл. Поэтому нужно сделать длину сообщения кратной 8=>
        //нужно добавить нули в конец(можно 1, нет разницы)
        byte counter = 0;//количество добавленных в конец нулей (байта в полне хватит: 0<=counter<8<127)
        for (int length = intermidiate.length(), delta = 8 - length % 8; 
        		counter < delta ; counter++) {//delta - количество добавленных нулей
            intermidiate += "0";
        }
        
        //склеить кол-во добавочных нулей в бинарном предаствлении и промежуточную строку 
        compressed = String.format("%8s", Integer.toBinaryString(counter & 0xff)).replace(" ", "0") + intermidiate;
        		
        //идеализированный коэффициент
        setCompressionRatio();
        //System.out.println("===============================================================");
        return compressed;
    }
    
    private void setCompressionRatio() {//посчитать идеализированный коэффициент 
        double sumA = 0, sumB = 0;//A-the original sum
        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() {//final compression
        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;
    }
    //---------------------------------------end of compression----------------------------------------------------------------
    //------------------------------------------------------------extract-----------------------------------------------------
    public String extract(String compressed, String[] newEncodingArray) {
        String decompressed = "";
        String current = "";
        String delta = "";
        encodingArray = newEncodingArray;
        
        //displayEncodingArray();
        //получить кол-во вставленных нулей
        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, т.к. первым байтом у нас идет кол-во вставленных нулей
            current += compressed.charAt(i);
            for (int j = 0; j < ENCODING_TABLE_SIZE; j++) {
                if (current.equals(encodingArray[j])) {//если совпало
                    decompressed += (char)j;//то добавляем элемент
                    current = "";//и обнуляем текущую строку
                }
            }
        }

        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() {//для отладки
        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("========================================================");
    }
    }

Clas a chuidicheas sgrìobhadh gu faidhle:

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("Неверный путь, или такого файла не существует!");
    	}
    }

    @Override
    public void close() throws IOException {
        fileOutputStream.close();
    }

    public void finalize() throws IOException {
        close();
    }
}

Clas a chuidicheas leughadh bho fhaidhle:

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)//если закончился файл
    		throw new EOFException();
    	return (byte)cur;
    }
    
    public String readLine() throws IOException {
    	return fileBufferedReader.readLine();
    }
    
    @Override
    public void close() throws IOException{
    	fileInputStream.close();
    }
}

Uill, agus am prìomh chlas:

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 {//указываем инструкцию с помощью аргументов командной строки
            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("Неверный формат ввода аргументов ");
            System.out.println("Читайте Readme.txt");
            e.printStackTrace();
        }
    }

	public static void compress(String stringPath) throws IOException {
        List<String> 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("Неверный путь, или такого файла не существует!");
            return;
        } catch (MalformedInputException e) {
        	System.out.println("Текущая кодировка файла не поддерживается");
        	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("Путь к сжатому файлу: " + compressedFile.getAbsolutePath());
        System.out.println("Путь к кодировочной таблице " + table.getAbsolutePath());
        System.out.println("Без таблицы файл будет невозможно извлечь!");
        
        double idealRatio = Math.round(operator.getCompressionRatio() * 100) / (double) 100;//идеализированный коэффициент
        double realRatio = Math.round((double) inputFile.length() 
        		/ ((double) compressedFile.length() + (double) table.length()) * 100) / (double)100;//настоящий коэффициент
        
        System.out.println("Идеализированный коэффициент сжатия равен " + idealRatio);
        System.out.println("Коэффициент сжатия с учетом кодировочной таблицы " + 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("Путь к распакованному файлу " + extractedFile.getAbsolutePath());
    }
}

Feumaidh tu am faidhle a sgrìobhadh le stiùireadh readme.txt thu fhèin 🙂

co-dhùnadh

Tha mi creidsinn gur e sin a bha mi airson a ràdh. Ma tha rudeigin ri ràdh mu mo neo-chomasachd de leasachaidhean ann an còd, algairim, san fharsaingeachd, optimization sam bith, an uair sin a 'faireachdainn an-asgaidh sgrìobhadh. Mura h-eil mi air rudeigin a mhìneachadh, sgrìobh cuideachd. Bu mhath leam cluinntinn bhuat anns na beachdan!

PS

Tha, tha, tha mi fhathast an seo, oir cha do dhìochuimhnich mi mun cho-èifeachd. Airson an t-sreang s1, tha cuideam 48 bytes air a’ chlàr còdaidh - tòrr a bharrachd na am faidhle tùsail, agus cha do dhìochuimhnich iad mu na neoni a bharrachd (is e an àireamh de neamhan a bharrachd 7) => bidh an co-mheas teannachaidh nas lugha na aon: 176 /(65 + 48*8 + 7) = 0.38. Ma mhothaich thu seo cuideachd, an uairsin chan ann dìreach nad aghaidh a tha thu deiseil. Bidh, bidh am buileachadh seo gu math neo-èifeachdach airson faidhlichean beaga. Ach dè thachras do fhaidhlichean mòra? Tha meud nam faidhlichean tòrr nas motha na meud a’ chlàir còdaidh. Seo far a bheil an algairim ag obair mar a bu chòir! Mar eisimpleir, airson Monologue aig Faust tha an tasglann a’ toirt co-èifeachd fìor (chan eil air leth freagarrach) co-ionann ri 1.46 - cha mhòr uair gu leth! Agus bha, bha còir gum biodh am faidhle sa Bheurla.

Source: www.habr.com

Cuir beachd ann