【问题标题】:How can I optimize Huffman Decoding?如何优化霍夫曼解码?
【发布时间】:2018-11-11 08:28:03
【问题描述】:

所以我一直在尝试使用 huffman 进行解码,我有这个工作功能,但它的时间和空间复杂性非常可怕。到目前为止,我一直在做的是读取每个字节,获取每个位并将其添加到 currentBitString。然后我反转字符串,并将其添加到一个巨大的字符串中,该字符串基本上包含文件的所有字节数据。在那之后,我会追踪这个巨大的字符串并寻找霍夫曼代码,然后如果它匹配,我会写入文件。这段代码解码一个 200kb 大约需要 60 秒,这非常糟糕,但我不确定如何改进它?我知道对于初学者来说,我可以一次向文件写入一个以上的字节,但它似乎并没有改善我尝试的时间?

         public static void decode(File f) throws Exception {

    BufferedInputStream fin = new BufferedInputStream(new FileInputStream(f));
    int i = f.getName().lastIndexOf('.');
    String extension="txt";
    String newFileName=f.getName().substring(0, i)+extension;
    File nf = new File(newFileName);
    BufferedOutputStream fw = new BufferedOutputStream(new FileOutputStream(nf));
    int c;
    byte bits;
    byte current;
    String currentBitString="";
    String bitString="";
    //read each byte from file, reverse it, add to giant bitString
    //reads ALL BYTES
    while( (c=fin.read())!=-1 ) {
        current=(byte) c;
        currentBitString="";
        bits=0;
        for(int q=0;q<8;q++) {
            bits=getBit(current,q);
            currentBitString+=bits;
        }
        StringBuilder bitStringReverse=new StringBuilder(currentBitString);
        bitString+=bitStringReverse.reverse().toString();
    }
    currentBitString="";
    boolean foundCode=false;
    for(int j=0;j<bitString.length();j++) {
        currentBitString+=bitString.charAt(j);
        for(int k=0;k<nodes.length;k++) {
            //nodes is an array of huffman nodes which contains the the byte 
            //data and the huffman codes for each byte
            if(nodes[k].code.compareTo(currentBitString.trim())==0) {
                fw.write(nodes[k].data);    
                foundCode=true;
                break;
            }
        }
        if(foundCode) {
            currentBitString="";
            foundCode=false;
        }

    }
    fw.flush();
    fw.close();
    fin.close();

}

这里是 gitBit 函数

        public static byte getBit(byte ID, int position) {
        // return cretin bit in selected byte
        return (byte) ((ID >> position) & 1);
        }

这里是 HuffmanNode 类的数据成员(节点数组是 HuffmanNodes 的数组)

       public class HuffmanNode{
       byte data;
       int repetitions;
       String code;
       HuffmanNode right;
       HuffmanNode left;
       }

【问题讨论】:

  • 也许这会有所帮助geeksforgeeks.org/…
  • 您可能有一个非常低效的编码实现,但复杂性并不能说明这一点。它仍然是 O(n) 时间,并且从快速扫描来看,也是 O(n) 空间。你问错了问题,你不能 xhsnde 它的复杂性,但你想优化代码。而且这个问题对于 SO 来说可能太宽泛了。
  • 我真的不知道你的代码在做什么。但我观察到内存分配非常繁重:对于您读取的每个字符,您分配一个 StringBuilder 实例,toString() 分配一个 String实例和+= 分配另一个9 String 实例。优化潜力巨大。
  • rules.sonarsource.com/java/RSPEC-1643。此外,当您可以使用for (int q=7;q&gt;=0;q--) 以正确的方式开始构建它时,为什么要从左到右构建位串然后反转它(从而占用更多内存并占用更多时间)。

标签: java huffman-code


【解决方案1】:

您可以将字符串连接+= 替换为StringBuilder。这会分配更少的对象并减少垃圾收集器的负载。

int c;
StringBuilder bitString = new StringBuilder();
//read each byte from file, reverse it, add to giant bitString
//reads ALL BYTES
while ((c = fin.read()) != -1) {
    byte current = (byte) c;
    StringBuilder currentBitString = new StringBuilder();
    for (int q = 0; q < 8; q++) {
        byte bits = getBit(current, q);
        currentBitString.append(bits);
    }
    bitString.append(currentBitString.reverse());
}

您应该在这里使用HashMap,而不是将代码和数据放入数组nodes。您通过遍历整个数组来比较代码,直到找到正确的匹配项。平均而言,每个项目有n/2 调用String#equals()。使用 HashMap 可以将其减少到 ~1。

用代码作为键的数据填充您的地图。

Map<String, Integer> nodes = new HashMap<>();
nodes.put(code, data);

从地图访问数据

boolean foundCode = false;
for (int j = 0; j < bitString.length(); j++) {
    currentBitString.append(bitString.charAt(j));
    Integer data = nodes.get(currentBitString.toString().trim());
    if (data != null) {
        fw.write(data);
        foundCode = true;
    }
    if (foundCode) {
        currentBitString = new StringBuilder();
        foundCode = false;
    }
}

【讨论】:

  • break语句不就是跳出整个循环,不检查是否找到代码吗?
  • 我试了一下,把break语句放到了第二个循环里,但是没用,好像某处有无限循环
  • 不再需要 break 语句,因为 HashMap 删除了第二个循环以遍历数组。更新了代码。如果代码不是 100% 正确,我认为您仍然可以理解。
  • 我明白了这个想法并实现了它,但由于某种原因时间并没有真正下降,解码一个 200kb 的文件仍然需要一分钟。
  • @TheHaruWhoCodes:在这种情况下,您应该分析您的程序,例如与视觉VM。这可以告诉您在哪种方法上花费的时间最多。
【解决方案2】:
  1. 不要将整个内容读入内存。处理遇到的代码。读取足够的位来解码下一个代码,解码它,为后续代码保留未使用的位,重复。

  2. 不要使用字符串来表示位,在这种情况下每个字符都表示一位。使用位来表示位。 shift, and, and or 运算符是您应该使用的。您将有一个整数作为位缓冲区,其中包含解码下一个代码所需的所有位。

  3. 不要对所有代码长度进行搜索,而是在其中对所有代码进行线性搜索以找到您的代码!我很难想出一个更慢的方法。您应该使用树下降或表查找进行解码。如果您首先生成canonical Huffman code,则可以实现一种简单的查找方法。有关示例,请参阅puff.c。教科书的方法(这比 puff.c 做的要慢)是在接收端构建相同的 Huffman 树,然后一点一点地沿着树向下直到你得到一个符号。发出符号并重复。

您应该能够在现代处理器的单核上在几毫秒内处理 200K 的压缩输入。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-03-18
    • 1970-01-01
    • 1970-01-01
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多