簡體   English   中英

具有隨機數據訪問的壓縮矢量/數組類

[英]compressed vector/array class with random data access

我想制作“壓縮數組”/“壓縮矢量”類(詳見下文),它允許隨機數據訪問具有或多或少的恆定時間。

“或多或少的恆定時間”意味着雖然元素訪問時間不是恆定的,但是當我接近數組的某個點時,它不應該繼續增加。 即容器不應該做更多的計算(比如“再次解壓縮所有內容以獲取最后一個元素”,並且“幾乎不做任何事情來獲得第一個”)來獲得一個元素。 可以通過將數組拆分為壓縮數據塊來實現。 即訪問一個元素應該采用“averageTime”+ - 一些偏差。 我可以說我希望最佳訪問時間和最壞情況訪問時間相對接近平均訪問時間。

我有哪些選擇(合適的算法/已有的容器 - 如果有的話)?

貨櫃詳情:

  1. 容器充當相同元素的線性數組(例如std :: vector)
  2. 初始化容器后,數據將保持不變並且永遠不會更改。 容器需要提供只讀訪問權限。
  3. 容器應該像array / std :: vector一樣 - 即通過operator []訪問的值,有.size()等。
  4. 如果我可以將它作為模板類,那將是很好的。
  5. 對數據的訪問應該或多或少地保持恆定時間。 我不需要每個元素的相同訪問時間,但我不應該解壓縮所有內容以獲取最后一個元素。

用法示例:
二進制數據搜索。

數據詳情:
數據結構主要由浮點數和幾個整數組成。 浮點數多於整數。 沒有字符串。
2.陣列中不太可能存在許多相同的元素,因此無法簡單地索引數據。
3.一個元素的大小小於100個字節。
4.每個容器的總數據大小介於幾千字節和幾兆字節之間。
5.數據不稀疏 - 它是連續的元素塊,所有元素都被分配,沒有“空槽”。

壓縮的目的是減少與未壓縮表示形式相比較的塊所占用的RAM數量,同時保持一定程度上合理的讀取訪問性能,並允許隨機訪問元素作為數組。 即數據應該在內部以壓縮形式存儲,我應該能夠訪問它(只讀),就像它是std :: vector或類似的容器一樣。

想法/意見?

我認為你想要一個數組,其元素不是存儲,而是壓縮 ,以最小化內存使用。

關於壓縮,您對數據的結構沒有特別的了解,因此您可以使用某種標准的熵編碼 理想情況下,想要在整個陣列上運行GZIP並完成它,但這將失去O(1)訪問權限,這對您至關重要。

一種解決方案是將霍夫曼編碼索引表一起使用。

霍夫曼編碼通過將每個輸入符號(例如,ASCII字節)替換為可變比特長度的另一個符號來工作,這取決於整個流中的出現頻率。 例如,字符E經常出現,因此它得到一個短位序列,而'W'很少並且得到一個長位序列。

E -> 0b10
W -> 0b11110

現在,使用此方法壓縮整個數組。 不幸的是,由於輸出符號具有可變長度,因此您不能再像以前那樣索引數據:項目編號15不再是stream[15*sizeof(item)]

幸運的是,通過使用另外的索引表 index可以解決此問題,該index存儲每個項目在壓縮流中的開始位置。 換句話說,項目15的壓縮數據可以在stream[index[15]] ; 索引表累積變量輸出長度。

因此,要獲得第15項,您只需在stream[index[15]]處開始解壓縮字節。 這是因為霍夫曼編碼不會對輸出做任何事情,它只是連接新的代碼字,你可以開始在流內解碼,而不必解碼所有以前的項目。

當然,索引表增加了一些開銷 ; 您可能希望調整粒度,以便compressed data + index table仍然小於original data

您是在編寫嵌入式系統的編碼和/或是否有數百或數千個這樣的容器? 如果不是,雖然我認為這是一個有趣的理論問題(+1),但我懷疑減壓的結果將是非平凡的,並且使用std::vector會更好。

接下來,您確定您存儲的數據是否足夠多,以至於它的較小塊實際上是可壓縮的? 您是否嘗試過保存不同大小的塊(也許是2的冪)並嘗試通過gzip作為練習運行它們? 可能需要幫助解壓縮算法(取決於方法)所需的任何額外數據會降低執行此類壓縮容器的空間優勢。

如果您認為進行壓縮仍然是合理的,那么至少有幾種可能性,但沒有預先編寫。 您可以壓縮每個單獨的元素,存儲指向壓縮數據塊的指針。 然后索引訪問仍然是常量,只需要解壓縮實際數據。 可能使用代理對象會使實際數據解壓縮更容易和更透明(甚至可能允許您使用std::vector作為底層容器)。

或者, std::deque已經將數據存儲在塊中,因此您可以在此處使用類似的方法。 例如std::vector<compressed_data_chunk> ,其中每個塊保持說10個項目被壓縮在一起作為您的底層容器。 然后,您仍然可以直接索引所需的塊,對其進行解壓縮,然后從解壓縮的數據中返回該項。 如果你願意,你的包含對象(包含vector )甚至可以緩存最近解壓縮的一兩個塊,以便在連續訪問時增加性能(盡管這對於二進制搜索根本沒有幫助)。

我一直在考慮這個問題。 從理論的角度來看,我確定了兩種可能性:

  • Flyweight,因為這種模式可以減少重復。
  • 序列化(壓縮是某種形式的序列化)

第一個是純粹的面向對象並且非常適合我認為通常,它沒有例如弄亂指針的缺點。

第二個看起來更適合這里,雖然它確實有一點點缺點:指針失效+指針編碼/解碼問題,虛擬表等...值得注意的是,如果項目使用指針引用彼此,則它不起作用指數。

我已經看到了一些“霍夫曼編碼”解決方案,但這意味着對於每個結構,需要提供壓縮算法。 概括起來並不容易。

所以我寧願走另一條路,使用像'zlib'這樣的壓縮庫,拿起像lzo這樣的快速算法。

  • 每個節點具有大量項目的B *樹(或變體)(因為它不移動),例如1001.每個節點包含項目數組的壓縮表示。 指數未壓縮。
  • 可能: cache_view在存儲最后5個(或左右)解壓縮節點時訪問容器。 另一種變體是實現引用計數並保持數據未壓縮,只要某些人獲得了節點中某個項的句柄即可。

一些評論:

  • 如果你應該在每個節點上有大量的項目/密鑰,你有接近恆定的訪問時間,例如1001就意味着只有2個級別的間接,只要你存儲的項目不到一百萬,間接的3個級別億等......
  • 您可以構建具有這種結構的可讀/可寫容器。 我會這樣做,所以我只有在完成編寫節點后才重新壓縮。

好的,從我的理解中,你想要的是某種訪問者模板。 基本上,創建一個模板適配器,它的參數之一是它通過任何內部訪問的元素類型,指針,blob的索引等。使適配器指針類似:

const T &operator->(void) const;

因為它比參考適配器更容易創建指針適配器(但是如果你想知道如何編寫其中一個,請參見vector)。 請注意,我根據您的指南使此訪問器保持不變。 然后,在加載/壓縮blob時預先計算偏移量,並使用模板化的適配器類填充向量。 這有意義嗎? 如果您需要更多詳細信息,我將很樂意為您提供。

至於壓縮算法,我建議您只需對blob中的字節進行頻率分析,然后通過硬編碼的Huffman編碼運行未壓縮的blob(前面或多或少提過),捕獲每個元素的偏移量並存儲它在您的代理適配器中,而后者又是您的數組的元素。 實際上,您可以將其作為壓縮類的一部分,壓縮並生成可以從頭開始復制回插入到向量中的元素。 如果您需要示例代碼,請再次回復。

可以回答一下“什么是允許文件中隨機讀/寫的最佳壓縮算法?” 適應你的內存數據?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM