Aprimoramento da Codificação de Huffman em C# para Criptografia Simples

Esta implementação modifica a codificação de Huffman clássica em C#, substituindo a sequência binária padrão (0 e 1) por um dicionário personalizado de caracteres. Isso permite o uso da codificação para criptografia simples. Ajustes são feitos automaticamente: se o dicionário tiver mais elementos do que os caracteres únicos no texto, o excesso é removido. Valores duplicados no dicionário também são eliminados. A estrutura da árvore binária é adaptada para usar arrays em vez de filhos esquerdo/direito, o que afeta operações tradicionais em árvores.

O código a seguir demonstra a classe CifraHuffman, que encapsula a lógica. Primeiro, definimos uma classe auxiliar para os nós da árvore:

using System;
using System.Collections.Generic;
using System.Linq;

public class CifraHuffman
{
    public class No : IComparable
    {
        public No[] Filhos { get; set; }
        public int Peso { get; set; }
        public char Chave { get; set; }

        public No() { }

        public No(int peso, char chave)
        {
            this.Peso = peso;
            this.Chave = chave;
        }

        public int CompareTo(object obj)
        {
            No outro = obj as No;
            if (outro.Peso > this.Peso)
                return 1;
            else if (outro.Peso < this.Peso)
                return -1;
            return 0;
        }
    }

    private char[] dicionarioOriginal;
    private char[] dicionarioAtivo;

    public CifraHuffman(char[] dicionario)
    {
        this.dicionarioOriginal = dicionario.Distinct().ToArray();
        this.dicionarioAtivo = this.dicionarioOriginal;
    }

    public No[] CalcularFrequencias(string texto)
    {
        var lista = new List<No>();
        char[] caracteres = texto.ToCharArray();

        if (caracteres.Length < dicionarioOriginal.Length)
        {
            char[] temp = new char[caracteres.Length];
            Array.Copy(dicionarioOriginal, temp, caracteres.Length);
            dicionarioAtivo = temp;
        }

        while (caracteres.Length > 0)
        {
            char atual = caracteres[0];
            int contagem = caracteres.Count(c => c == atual);
            lista.Add(new No(contagem, atual));
            caracteres = caracteres.Where(c => c != atual).ToArray();
        }

        return lista.ToArray();
    }

    public No ConstruirArvore(No[] nos)
    {
        No raiz = null;
        var fila = nos;

        while (true)
        {
            if (fila.Length <= dicionarioAtivo.Length)
            {
                raiz = new No();
                raiz.Filhos = fila;
                break;
            }

            Array.Sort(fila);
            var novoNo = new No();
            novoNo.Filhos = new No[dicionarioAtivo.Length];

            int indice = fila.Length - 1;
            for (int i = 0; i < dicionarioAtivo.Length && indice >= 0; i++, indice--)
            {
                novoNo.Peso += fila[indice].Peso;
                novoNo.Filhos[i] = fila[indice];
            }

            var sobra = fila.Take(fila.Length - dicionarioAtivo.Length + 1).ToArray();
            sobra[sobra.Length - 1] = novoNo;
            fila = sobra;
        }

        return raiz;
    }

    public Dictionary<char, string> GerarTabelaCodigos(No raiz)
    {
        var tabela = new Dictionary<char, string>();
        PercorrerArvore(raiz, "", tabela);
        return tabela;
    }

    private void PercorrerArvore(No no, string codigoAtual, Dictionary<char, string> tabela)
    {
        if (no.Filhos == null)
        {
            tabela[no.Chave] = codigoAtual;
            return;
        }

        for (int i = 0; i < no.Filhos.Length; i++)
        {
            PercorrerArvore(no.Filhos[i], codigoAtual + dicionarioAtivo[i], tabela);
        }
    }

    public string Codificar(string textoOriginal, Dictionary<char, string> tabela)
    {
        string resultado = "";
        foreach (char c in textoOriginal)
        {
            resultado += tabela[c];
        }
        return resultado;
    }

    public string Decodificar(string codigo, Dictionary<char, string> tabela)
    {
        string resultado = "";
        int posicao = 0;

        while (posicao < codigo.Length)
        {
            foreach (var par in tabela)
            {
                if (codigo[posicao] == par.Value[0] && posicao + par.Value.Length <= codigo.Length)
                {
                    string trecho = codigo.Substring(posicao, par.Value.Length);
                    if (trecho == par.Value)
                    {
                        resultado += par.Key;
                        posicao += par.Value.Length;
                        break;
                    }
                }
            }
        }

        return resultado;
    }

    public string ProcessarTexto(string texto, out Dictionary<char, string> tabelaSaida)
    {
        var frequencias = CalcularFrequencias(texto);
        var arvore = ConstruirArvore(frequencias);
        var tabela = GerarTabelaCodigos(arvore);
        string codigo = Codificar(texto, tabela);

        tabelaSaida = tabela;
        return codigo;
    }
}

No programa principal, a classse é utilizada para codificar e decodifiacr uma string:

class Programa
{
    static void Main(string[] args)
    {
        string textoOriginal = "exemplo Huffman";
        CifraHuffman cifra = new CifraHuffman("xyz123".ToCharArray());

        Dictionary<char, string> mapeamento;
        string codigo = cifra.ProcessarTexto(textoOriginal, out mapeamento);

        Console.WriteLine("Codificação:");
        Console.WriteLine(codigo);

        string textoDecodificado = cifra.Decodificar(codigo, mapeamento);
        Console.WriteLine("Decodificação:");
        Console.WriteLine(textoDecodificado);

        Console.ReadLine();
    }
}

Tags: C# Huffman criptografia dicionário personalizado compressão

Publicado em 6-9 01:25 por Thomas