2009-07-02 15 views
14

मेरे पास एक ऐसा एप्लिकेशन है जहां मैं डेटा के छोटे ब्लॉक (कुछ सौ बाइट) लाखों बार पढ़ रहा हूं और लिख रहा हूं। मैं एक उदाहरण डेटा फ़ाइल के आधार पर एक संपीड़न शब्दकोश उत्पन्न करना चाहता हूं और उस शब्दकोष का हमेशा उपयोग करता हूं जैसा कि मैंने छोटे ब्लॉक को पढ़ और लिखते हैं। मैं एलजेडब्लूडब्ल्यू संपीड़न एल्गोरिदम की ओर झुका रहा हूँ। विकिपीडिया पेज (http://en.wikipedia.org/wiki/Lempel-Ziv-Welch) संपीड़न और डिकंप्रेशन के लिए छद्म कोड सूचीबद्ध करता है। यह इसे संशोधित करने के लिए काफी सरल दिखता है जैसे कि शब्दकोश निर्माण कोड का एक अलग ब्लॉक है। तो मेरे पास दो प्रश्न हैं:प्रीकंप्यूटेड शब्दकोश के साथ छोटे ब्लॉक में लापरवाही संपीड़न

  1. क्या मैं सही रास्ते पर हूं या क्या कोई बेहतर तरीका है?
  2. LZW एल्गोरिदम डिकंप्रेशन चरण के दौरान शब्दकोश में क्यों जोड़ता है? क्या मैं इसे छोड़ सकता हूं, या क्या मैं अपने शब्दकोश में दक्षता खो दूंगा?

धन्यवाद।

अद्यतन: अब मैं सोच रहा हूं कि आदर्श केस एक लाइब्रेरी ढूंढना है जो मुझे संकुचित डेटा से अलग शब्दकोश को स्टोर करने देता है। क्या ऐसा कुछ भी मौजूद है?

अद्यतन: मैंने http://www.enusbaum.com/blog/2009/05/22/example-huffman-compression-routine-in-c पर कोड लेना और इसे अनुकूलित करना समाप्त कर दिया। मैं उस पृष्ठ पर टिप्पणियों में क्रिस हूं। मैंने अपने ब्लॉग को उस ब्लॉग लेखक को वापस ईमेल किया, लेकिन मैंने अभी तक नहीं सुना है। उस कोड के साथ मैं जो संपीड़न दर देख रहा हूं वह बिल्कुल प्रभावशाली नहीं है। शायद यह 8-बिट पेड़ के आकार के कारण है।

अद्यतन: मैंने इसे 16 बिट्स में परिवर्तित कर दिया और संपीड़न बेहतर है। यह मूल कोड से भी तेज़ है।

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 

namespace Book.Core 
{ 
    public class Huffman16 
    { 
    private readonly double log2 = Math.Log(2); 

    private List<Node> HuffmanTree = new List<Node>(); 

    internal class Node 
    { 
     public long Frequency { get; set; } 
     public byte Uncoded0 { get; set; } 
     public byte Uncoded1 { get; set; } 
     public uint Coded { get; set; } 
     public int CodeLength { get; set; } 
     public Node Left { get; set; } 
     public Node Right { get; set; } 

     public bool IsLeaf 
     { 
     get { return Left == null; } 
     } 

     public override string ToString() 
     { 
     var coded = "00000000" + Convert.ToString(Coded, 2); 
     return string.Format("Uncoded={0}, Coded={1}, Frequency={2}", (Uncoded1 << 8) | Uncoded0, coded.Substring(coded.Length - CodeLength), Frequency); 
     } 
    } 

    public Huffman16(long[] frequencies) 
    { 
     if (frequencies.Length != ushort.MaxValue + 1) 
     { 
     throw new ArgumentException("frequencies.Length must equal " + ushort.MaxValue + 1); 
     } 
     BuildTree(frequencies); 
     EncodeTree(HuffmanTree[HuffmanTree.Count - 1], 0, 0); 
    } 

    public static long[] GetFrequencies(byte[] sampleData, bool safe) 
    { 
     if (sampleData.Length % 2 != 0) 
     { 
     throw new ArgumentException("sampleData.Length must be a multiple of 2."); 
     } 
     var histogram = new long[ushort.MaxValue + 1]; 
     if (safe) 
     { 
     for (int i = 0; i <= ushort.MaxValue; i++) 
     { 
      histogram[i] = 1; 
     } 
     } 
     for (int i = 0; i < sampleData.Length; i += 2) 
     { 
     histogram[(sampleData[i] << 8) | sampleData[i + 1]] += 1000; 
     } 
     return histogram; 
    } 

    public byte[] Encode(byte[] plainData) 
    { 
     if (plainData.Length % 2 != 0) 
     { 
     throw new ArgumentException("plainData.Length must be a multiple of 2."); 
     } 

     Int64 iBuffer = 0; 
     int iBufferCount = 0; 

     using (MemoryStream msEncodedOutput = new MemoryStream()) 
     { 
     //Write Final Output Size 1st 
     msEncodedOutput.Write(BitConverter.GetBytes(plainData.Length), 0, 4); 

     //Begin Writing Encoded Data Stream 
     iBuffer = 0; 
     iBufferCount = 0; 
     for (int i = 0; i < plainData.Length; i += 2) 
     { 
      Node FoundLeaf = HuffmanTree[(plainData[i] << 8) | plainData[i + 1]]; 

      //How many bits are we adding? 
      iBufferCount += FoundLeaf.CodeLength; 

      //Shift the buffer 
      iBuffer = (iBuffer << FoundLeaf.CodeLength) | FoundLeaf.Coded; 

      //Are there at least 8 bits in the buffer? 
      while (iBufferCount > 7) 
      { 
      //Write to output 
      int iBufferOutput = (int)(iBuffer >> (iBufferCount - 8)); 
      msEncodedOutput.WriteByte((byte)iBufferOutput); 
      iBufferCount = iBufferCount - 8; 
      iBufferOutput <<= iBufferCount; 
      iBuffer ^= iBufferOutput; 
      } 
     } 

     //Write remaining bits in buffer 
     if (iBufferCount > 0) 
     { 
      iBuffer = iBuffer << (8 - iBufferCount); 
      msEncodedOutput.WriteByte((byte)iBuffer); 
     } 
     return msEncodedOutput.ToArray(); 
     } 
    } 

    public byte[] Decode(byte[] bInput) 
    { 
     long iInputBuffer = 0; 
     int iBytesWritten = 0; 

     //Establish Output Buffer to write unencoded data to 
     byte[] bDecodedOutput = new byte[BitConverter.ToInt32(bInput, 0)]; 

     var current = HuffmanTree[HuffmanTree.Count - 1]; 

     //Begin Looping through Input and Decoding 
     iInputBuffer = 0; 
     for (int i = 4; i < bInput.Length; i++) 
     { 
     iInputBuffer = bInput[i]; 

     for (int bit = 0; bit < 8; bit++) 
     { 
      if ((iInputBuffer & 128) == 0) 
      { 
      current = current.Left; 
      } 
      else 
      { 
      current = current.Right; 
      } 
      if (current.IsLeaf) 
      { 
      bDecodedOutput[iBytesWritten++] = current.Uncoded1; 
      bDecodedOutput[iBytesWritten++] = current.Uncoded0; 
      if (iBytesWritten == bDecodedOutput.Length) 
      { 
       return bDecodedOutput; 
      } 
      current = HuffmanTree[HuffmanTree.Count - 1]; 
      } 
      iInputBuffer <<= 1; 
     } 
     } 
     throw new Exception(); 
    } 

    private static void EncodeTree(Node node, int depth, uint value) 
    { 
     if (node != null) 
     { 
     if (node.IsLeaf) 
     { 
      node.CodeLength = depth; 
      node.Coded = value; 
     } 
     else 
     { 
      depth++; 
      value <<= 1; 
      EncodeTree(node.Left, depth, value); 
      EncodeTree(node.Right, depth, value | 1); 
     } 
     } 
    } 

    private void BuildTree(long[] frequencies) 
    { 
     var tiny = 0.1/ushort.MaxValue; 
     var fraction = 0.0; 

     SortedDictionary<double, Node> trees = new SortedDictionary<double, Node>(); 
     for (int i = 0; i <= ushort.MaxValue; i++) 
     { 
     var leaf = new Node() 
     { 
      Uncoded1 = (byte)(i >> 8), 
      Uncoded0 = (byte)(i & 255), 
      Frequency = frequencies[i] 
     }; 
     HuffmanTree.Add(leaf); 
     if (leaf.Frequency > 0) 
     { 
      trees.Add(leaf.Frequency + (fraction += tiny), leaf); 
     } 
     } 

     while (trees.Count > 1) 
     { 
     var e = trees.GetEnumerator(); 
     e.MoveNext(); 
     var first = e.Current; 
     e.MoveNext(); 
     var second = e.Current; 

     //Join smallest two nodes 
     var NewParent = new Node(); 
     NewParent.Frequency = first.Value.Frequency + second.Value.Frequency; 
     NewParent.Left = first.Value; 
     NewParent.Right = second.Value; 

     HuffmanTree.Add(NewParent); 

     //Remove the two that just got joined into one 
     trees.Remove(first.Key); 
     trees.Remove(second.Key); 

     trees.Add(NewParent.Frequency + (fraction += tiny), NewParent); 
     } 
    } 

    } 

} 

उपयोग के उदाहरण:

var freqs = Huffman16.GetFrequencies(File.ReadAllBytes(@"D:\nodes"), true); 

किसी दिए गए शब्दकोश के साथ एक एनकोडर को प्रारंभ करने के लिए::

var huff = new Huffman16(freqs); 

और ऐसा करने के लिए

नमूना डेटा से शब्दकोश बनाने के लिए कुछ संपीड़न:

var encoded = huff.Encode(raw); 

और विसंपीड़न:

var raw = huff.Decode(encoded); 
+0

क्या आपके ब्लॉक लगातार आकार में हैं? – user57368

+0

इसके अलावा, मेरे पास एक नमूना डेटा फ़ाइल आरएआर-एड है और पुष्टि की है कि डेटा अत्यधिक संकुचित (~ 9 x संपीड़न अनुपात) है। – Fantius

+0

नहीं, वे लगातार आकार में नहीं हैं। – Fantius

उत्तर

3

मेरे दिमाग में कठिन हिस्सा यह है कि आप अपना स्थिर शब्दकोश कैसे बनाते हैं। आप अपने नमूना डेटा से बनाए गए LZW शब्दकोश का उपयोग नहीं करना चाहते हैं। एलजेडब्लूडब्ल्यू समय सीखने का एक गुच्छा बर्बाद कर देता है क्योंकि यह डिकंप्रेसर कर से तेज़ी से शब्दकोश का निर्माण नहीं कर सकता है (एक टोकन केवल कंप्रेसर द्वारा देखी जाने वाली दूसरी बार इस्तेमाल किया जाएगा, इसलिए डिकंप्रेसर इसे पहली बार अपने शब्दकोश में जोड़ सकता है) । इसका फ्लिप पक्ष यह है कि यह उस शब्दकोश में चीजें जोड़ रहा है जो कभी भी उपयोग नहीं किया जा सकता है, बस स्ट्रिंग फिर से दिखाई देने पर। (उदाहरण के लिए, 'स्टैक ओवरफ्लो' के लिए टोकन रखने के लिए आपके पास 'एसी', 'को', 'वी', 'आरएफ' आदि के लिए प्रविष्टियां भी होंगी ...)

हालांकि, कच्चे टोकन स्ट्रीम को देखते हुए एक एलजेड 77 एल्गोरिदम से अच्छी तरह से काम कर सकता है। आप केवल कम से कम दो बार देखे तारों के लिए टोकन देखेंगे। फिर आप अपने शब्दकोश में शामिल करने के लिए सबसे आम टोकन/तारों की एक सूची बना सकते हैं।

एक बार जब आप एक स्थिर शब्दकोश है, LZW बिना का उपयोग कर शब्दकोश अद्यतन लेकिन (सबसे अच्छा संपीड़न मैं पारंपरिक 12 बिट तय आकार टोकन के बजाय एक स्थिर Huffman मेज पर विचार कर रहे प्राप्त करने के लिए के रूप में जॉर्ज फिलिप्स सुझाव दिया एक आसान कार्यान्वयन की तरह लगता है)। एक एलजेडब्लूडब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्लू डब्ल्यू स्टैको इत्यादि)।

इस बिंदु पर यह वास्तव में एलजेडब्लूडब्लू नहीं है - एलजेडब्लूडब्ल्यू चालाक बनाता है कि डिकंप्रेसर एक ही शब्दकोश को कंप्रेसर कर सकता है जो केवल संपीड़ित डेटा स्ट्रीम को देखता है। कुछ आप उपयोग नहीं करेंगे। लेकिन सभी एलजेडब्लूडब्ल्यू कार्यान्वयन में एक ऐसा राज्य है जहां शब्दकोश भरा हुआ है और अब अपडेट नहीं किया गया है, इस प्रकार आप इसे अपने स्थिर शब्दकोश के साथ उपयोग करेंगे।

0
  1. सही रास्ते एक पुस्तकालय का उपयोग करना है - लगभग हर आधुनिक भाषा एक संकुचन लाइब्रेरी है। सी #, पायथन, पर्ल, जावा, वीबीनेट, जो भी आप उपयोग करते हैं।

  2. एलजेडब्लूडब्ल्यू पिछले इनपुट पर शब्दकोश के आधार पर कुछ जगह बचाता है। इसमें प्रारंभिक शब्दकोश है, और जब आप कुछ डिकंप्रेस करते हैं, तो आप उन्हें शब्दकोश में जोड़ते हैं - इसलिए शब्दकोश बढ़ रहा है। (मैं कुछ विवरण यहाँ छोड़ते हुए हूँ, लेकिन इस सामान्य विचार है)

आप प्रारंभिक एक के रूप में पूरे (पूर्ण) शब्दकोश आपूर्ति के द्वारा इस कदम को छोड़ सकते हैं। लेकिन इसके लिए कुछ जगह खर्च होगी।

+4

मैं एक संपीड़न पुस्तकालय का उपयोग करने के साथ ठीक हूँ। मुझे नहीं लगता था कि मुझे वह मिल जाएगा जो मुझे शब्दकोश के लिए निम्न स्तर की पहुंच प्रदान करेगा। आपके पास कोई उदाहरण है? – Fantius

3

LZW डिक्रप्रेशन के दौरान शब्दकोश में जोड़ता है ताकि यह सुनिश्चित किया जा सके कि यह कंप्रेसर के समान शब्दकोष है। अन्यथा डीकोडिंग ठीक से काम नहीं करेगा।

हालांकि, यदि आप ऐसे राज्य में थे जहां शब्दकोश तय किया गया था, तो हाँ, आपको नए कोड जोड़ने की आवश्यकता नहीं होगी।

आपका दृष्टिकोण उचित रूप से अच्छी तरह से काम करेगा और मौजूदा उपकरणों का उपयोग प्रोटोटाइप और परिणामों को मापने के लिए करना आसान है। यही है, उदाहरण फ़ाइल को संपीड़ित करें और फिर उदाहरण और परीक्षण डेटा एक साथ। बाद वाले के आकार का आकार ब्लॉक के अपेक्षित संपीड़ित आकार होगा।

LZW फ्लाई पर एक शब्दकोश बनाने के लिए एक चालाक तरीका है और सभ्य परिणाम देता है। लेकिन आपके विशिष्ट डेटा ब्लॉक का एक अधिक गहन विश्लेषण एक अधिक कुशल शब्दकोश उत्पन्न करने की संभावना है।

एलजेडब्लूडब्ल्यू संपीड़ित डेटा का प्रतिनिधित्व करने में सुधार के लिए भी जगह है। उदाहरण के लिए, प्रत्येक शब्द संदर्भ हफमैन अपने उपयोग की अपेक्षित आवृत्ति के आधार पर इष्टतम लंबाई के करीब एन्कोड किया जा सकता है। वास्तव में इष्टतम होने के लिए कोड अंकगणित एन्कोडेड होना चाहिए।

+0

अहह, # 2 का आपका उत्तर सही अर्थ बनाता है और हिंडसाइट में स्पष्ट प्रतीत होता है। धन्यवाद। – Fantius

1

मैं यह देखने के लिए आपके डेटा को देखता हूं कि कोई स्पष्ट कारण है कि यह संपीड़ित करना इतना आसान है या नहीं। आप LZ78 से कुछ अधिक सरल करने में सक्षम हो सकते हैं। मैंने एलजेड 77 (लुकबैक) और एलजेड 78 (शब्दकोश) दोनों किया है।

अपने डेटा पर LZ77 चलाने का प्रयास करें। LZ77 के साथ कोई शब्दकोश नहीं है, इसलिए आप बिना किसी बदलाव के लाइब्रेरी का उपयोग कर सकते हैं। Deflate एलजेड 77 का कार्यान्वयन है।

एक सामान्य शब्दकोश का उपयोग करने का आपका विचार एक अच्छा है, लेकिन यह जानना मुश्किल है कि फाइलें एक-दूसरे के समान हैं या कुछ परीक्षण किए बिना स्वयं ही समान हैं।

+0

मैंने डिफ्लेट करने का प्रयास किया है। डेटा के साथ केवल कुछ 100 बाइट्स के रूप में, यह इतना अच्छा नहीं है। – Fantius

+0

विकिपीडिया के अनुसार, "LZ78 डिकंप्रेशन इनपुट तक यादृच्छिक पहुंच की अनुमति देता है जब तक कि संपूर्ण शब्दकोश उपलब्ध न हो।" यह मेरे आवेदन के लिए बिल्कुल सही है, लेकिन मुझे ऐसे पुस्तकालय के बारे में पता नहीं है जो इस तरह के संचालन की अनुमति देता है। – Fantius

+0

हाँ। आपको एक शब्दकोश जमा करना होगा। एलजेड 77 में करने के लिए समकक्ष चीज नकली डेटा को देखने की इजाजत देकर वास्तविक डेटा से पहले नकली डेटा लेना होगा। नकली डेटा सिर्फ कुछ प्रतिनिधि फाइलें हो सकती है। – Nosredna

0

मुझे बार-बार लॉग प्रविष्टियों और कुछ का उपयोग करके अन्वेषण करना बहुत पसंद है।

क्या आप अपने उपयोग के मामले के लिए इस दृष्टिकोण का उपयोग करने के लिए संपीड़न आंकड़े साझा कर सकते हैं ताकि मैं इसे अन्य विकल्पों के साथ तुलना कर सकूं?

क्या आपने माना है कि समय के साथ आम शब्दकोश बढ़ रहा है या यह वैध विकल्प नहीं है?

+0

क्षमा करें, मेरे पास कोई आंकड़ा नहीं है। मेरे मामले में, मुझे बढ़ने के लिए शब्दकोश की आवश्यकता नहीं थी। – Fantius

संबंधित मुद्दे