[转]算术编码+统计模型=数据压缩 - 第二部分:统计模型
轉(zhuǎn)自:http://deercrane.spaces.live.com/blog/cns!8BEF692B75EB8095!189.entry
算術(shù)編碼 + 統(tǒng)計(jì)模型 = 數(shù)據(jù)壓縮 - 第二部分:統(tǒng)計(jì)模型
(撼庭秋譯自http://compression.graphicon.ru/download/articles/ppm/nelson/arithmetic2.htm)
作者:Mark Nelson
這兩部分系列文章的第一篇以一些細(xì)節(jié)準(zhǔn)確地解釋了什么是算術(shù)編碼。概括地講,算術(shù)編碼提供使用最佳位數(shù)目編碼符號(hào)的方法。由每個(gè)符號(hào)所使用的位數(shù)不一定像霍夫曼編碼(Huffman coding)的情況下一樣是一個(gè)整數(shù)。為了使用算術(shù)編碼壓縮數(shù)據(jù),需要給數(shù)據(jù)一個(gè)模型。這個(gè)模型需要能夠完成兩件事來(lái)有效地壓縮數(shù)據(jù):
·模型需要精確地預(yù)計(jì)輸入數(shù)據(jù)流中符號(hào)的頻率/概率。
·由模型生成的符號(hào)概率需要脫離均勻分布。
本文將討論有效達(dá)成這些目標(biāo),獲取高性能壓縮的一些方法。
建模
需要精確地預(yù)計(jì)輸入數(shù)據(jù)中符號(hào)的概率是算術(shù)編碼中固有的本質(zhì)。這類編碼方法的原理是隨著字符出現(xiàn)概率的增加而減少編碼這個(gè)字符所需要的位數(shù)目。因此,如果字母“e”出現(xiàn)在輸入數(shù)據(jù)的概率是25%,它將只使用2位來(lái)編碼。如果字母“Z”在輸入數(shù)據(jù)中出現(xiàn)的概率只有1%,它可能要用10位來(lái)編碼。如果模型沒(méi)有精確地生成概率,它可能用10位來(lái)表示“e”并且用2位來(lái)表示“Z”,原因是用數(shù)據(jù)展開(kāi)代替了壓縮。
第二個(gè)情況是模型需要?jiǎng)?chuàng)建脫離均勻分布的預(yù)計(jì)。體現(xiàn)在創(chuàng)建脫離均勻的預(yù)計(jì)上越好的模型,將獲得越好的壓縮率。例如,模型可能是按給所有256個(gè)可能的字符指定均勻分布的概率1/256來(lái)創(chuàng)建的。這個(gè)模型將創(chuàng)建一個(gè)與輸入文件大小嚴(yán)格一樣的輸出文件,因?yàn)槊總€(gè)符號(hào)將嚴(yán)格地用8位來(lái)編碼。只有通過(guò)正確地找到脫離均勻分布的概率才能減少位數(shù)目,而獲取壓縮率。當(dāng)然,如第一種情況所規(guī)定,增加的概率需要精確地反映事實(shí)。
一個(gè)給定符號(hào)出現(xiàn)在數(shù)據(jù)流中的概率似乎是固定的,但是不完全是這樣。取決于所使用的模型,字符的概率可以改變很多。例如,當(dāng)壓縮一個(gè)C程序時(shí),換行符在文本中的概率可能是1/40。可以通過(guò)掃描整個(gè)文本并通過(guò)字符的總數(shù)除以這個(gè)字符出現(xiàn)的次數(shù)來(lái)決定這個(gè)概率。但是如果我們使用一個(gè)看前一個(gè)單個(gè)字符的建模技術(shù),這個(gè)概率就變了。在這種情況下,如果前一個(gè)字符是“}”,換行符的概率增長(zhǎng)為1/2。這改善了建模技術(shù)并獲得了更好的壓縮,盡管這兩種模型都生成精確的概率。
有限上下文建模
我將要在本文中介紹的建模類型稱為“有限上下文”建模。這種建模類型基于一個(gè)非常簡(jiǎn)單的思想:每一個(gè)輸入符號(hào)的概率都是基于符號(hào)出現(xiàn)的上下文中計(jì)算而來(lái)。在所有我將在此說(shuō)明的例子中,上下文由除前面已經(jīng)碰到的符號(hào)外,沒(méi)有更多的東西所組成。模型的“序”稱為建立起環(huán)境的前面字符的數(shù)目。
最簡(jiǎn)單的有限上下文模型將會(huì)是一個(gè)0序模型。這意味著每一個(gè)符號(hào)的概率獨(dú)立于任何前面的符號(hào)。為了實(shí)現(xiàn)這個(gè)模型,需要的所有事情是一個(gè)包含可以在輸入流中碰到的每一個(gè)符號(hào)的頻率計(jì)數(shù)。對(duì)于1序模型,你要明了256個(gè)不同的頻率表,因?yàn)槟阈枰獮槊恳粋€(gè)可能的上下文保持獨(dú)立的一組計(jì)數(shù)。同樣,2序模型需要能夠處理65,536個(gè)不同的上下文表。
自適應(yīng)模型
隨著模型序的增長(zhǎng),壓縮率也應(yīng)該隨之改善,這似乎是合乎正常的邏輯。例如,符號(hào)“u”在本文中的出現(xiàn)概率可能只有5%,但是如果前一個(gè)上下文字符是“q”,概率增長(zhǎng)至95%。能夠預(yù)測(cè)具有高概率的字符所需要的位數(shù)目較少,以及較大的上下文應(yīng)該會(huì)讓我創(chuàng)建更好的預(yù)測(cè)。
不幸的是,隨著模型序的線性增長(zhǎng),模型所消耗的內(nèi)存呈指數(shù)級(jí)增長(zhǎng)。使用0序模型,由統(tǒng)計(jì)消耗的空間可能跟256個(gè)字節(jié)一樣小。一旦模型的序增長(zhǎng)至2或者3,甚至是很聰明地設(shè)計(jì)的模型也要消耗數(shù)百K字節(jié)。
壓縮數(shù)據(jù)的一個(gè)便利的方法就是單程掃描一遍要壓縮的符號(hào)為模型收集統(tǒng)計(jì)。然后進(jìn)行第二遍掃描真正地編碼數(shù)據(jù)。然后統(tǒng)計(jì)常常預(yù)先考慮壓縮的數(shù)據(jù),因此解碼程序?qū)⒂幸粋€(gè)統(tǒng)計(jì)的副本與其一起工作。如果模型的統(tǒng)計(jì)比要壓縮的數(shù)據(jù)消耗更多的內(nèi)存,這個(gè)方法顯然將會(huì)有嚴(yán)重的問(wèn)題。
這個(gè)問(wèn)題的解決方法是執(zhí)行“自適應(yīng)”壓縮。在自適應(yīng)數(shù)據(jù)壓縮中,壓縮程序和解壓縮程序都以相同的模型開(kāi)始。壓縮程序使用已的有模型編碼符號(hào),然后更新模型以說(shuō)明新的符號(hào)。解壓縮程序同樣使用已有的模型解碼符號(hào),然后更新模型。對(duì)壓縮程序和解壓縮程序來(lái)說(shuō),只要更新模型的操作一致,那么處理可以完美地操作而無(wú)須從壓縮程序傳送統(tǒng)計(jì)表給解壓縮程序。
自適應(yīng)數(shù)據(jù)壓縮有一個(gè)稍微不利的一點(diǎn)就是它使用不如最佳的統(tǒng)計(jì)開(kāi)始?jí)嚎s。不過(guò),通過(guò)減少隨壓縮數(shù)據(jù)傳輸?shù)拈_(kāi)銷,自適應(yīng)算法通常比固定統(tǒng)計(jì)模型執(zhí)行得更好。
自適應(yīng)壓縮真正要忍受的地方是在更新模型的開(kāi)銷中。當(dāng)使用算術(shù)編碼更新對(duì)某個(gè)特定符號(hào)的計(jì)數(shù)時(shí),更新代碼有為所有其它符號(hào)更新累積的計(jì)數(shù)的潛在開(kāi)銷,導(dǎo)致平均必須為每個(gè)單個(gè)符號(hào)的編碼和解碼執(zhí)行128次算術(shù)操作。
因?yàn)樵趦?nèi)存和CPU時(shí)間兩者中的高開(kāi)銷,較高序的自適應(yīng)模型可能在以后10年內(nèi)變得可以實(shí)用。有點(diǎn)諷刺意味的是,隨磁盤(pán)空間和內(nèi)存的降價(jià),壓縮存儲(chǔ)在其中的數(shù)據(jù)的開(kāi)銷也在降低。隨著這些開(kāi)銷持續(xù)下降,我們將能夠?qū)崿F(xiàn)比今天可以實(shí)踐的程序更加有效的程序。
一個(gè)簡(jiǎn)單的例子
0序壓縮程序的一個(gè)例子在此說(shuō)明在列表6-9中。(列表達(dá)式1-5在上個(gè)月的文章中。)COMP-1.C是壓縮程序驅(qū)動(dòng),EXPAND-1.C是展開(kāi)程序驅(qū)動(dòng)。MODEL.H和MODEL-1.C組成用于實(shí)現(xiàn)0序統(tǒng)計(jì)模型的建模單元。
當(dāng)使用一個(gè)像這個(gè)例子程序一樣的固定序的模型時(shí),壓縮程序自身相當(dāng)簡(jiǎn)單。程序只需要在循環(huán)中不停地從純文本文件中讀取字符,將字符轉(zhuǎn)換成算術(shù)編碼,編碼符號(hào),然后更新模型。
在MODEL-1.C中的建模代碼是理解這段代碼是如何工作的關(guān)鍵。在上個(gè)月的文章中,我們看到每個(gè)字符在從0.0至0.1的基礎(chǔ)上放大的范圍中是如何“擁有”概率范圍的。為了用整數(shù)實(shí)現(xiàn)算術(shù)編碼,這個(gè)所有權(quán)按照從0到最大計(jì)數(shù)重新聲明為底計(jì)數(shù)和頂計(jì)數(shù)。統(tǒng)計(jì)有多精確留在MODEL.C中。
使用MODEL-1的所有可能符號(hào)的計(jì)數(shù)存放在一個(gè)稱為totals[]數(shù)組中。對(duì)于每一個(gè)c符號(hào),底計(jì)數(shù)放在total[c]并且頂計(jì)數(shù)放在totals[c+1]。所有符號(hào)的總范圍放在totals[256]。這三個(gè)計(jì)數(shù)就是需要傳送給算術(shù)編碼程序來(lái)編碼一個(gè)給定符號(hào)的東西。
這使查找符號(hào)的計(jì)數(shù)的操作非常直接。不過(guò),在符號(hào)編碼或者解碼后更新totals[]數(shù)組是另一個(gè)麻煩事。為了更新符號(hào)c的累積計(jì)數(shù),在totals[]中從c上至256之間的每一個(gè)計(jì)數(shù)都需要增加。這意味著對(duì)編碼和解碼每一個(gè)字符平均要執(zhí)行128個(gè)增加操作。對(duì)于像使用MODEL-1.C的簡(jiǎn)單驗(yàn)證程序來(lái)說(shuō),這并不是一個(gè)主要的問(wèn)題,但是對(duì)于產(chǎn)品程序應(yīng)該修改得更有效率一些。
減少增加操作數(shù)目的一個(gè)方法就是將最頻繁訪問(wèn)符號(hào)的計(jì)數(shù)移到數(shù)組的頂部。這意味著模型必須明了每一個(gè)字符在total[]數(shù)組中的位置,但是使增加操作的數(shù)目有一個(gè)數(shù)量級(jí)的減少,會(huì)產(chǎn)生一個(gè)正面的負(fù)作用。這是對(duì)這個(gè)程序相對(duì)簡(jiǎn)單的增強(qiáng)。使用這個(gè)技術(shù)的一個(gè)非常好的程序示例是發(fā)表在1987年6月期的《Communication of the ACM》的論文《Arithmetic Coding for Data Compression》的一部分。這篇論文由 Ian H.Witten、Radford Neal和John Cleary合著的論文是關(guān)于算術(shù)編碼的出色的信息資源,并且一些簡(jiǎn)單的C源程序來(lái)說(shuō)明其內(nèi)容。
性能
COMP-1是一個(gè)只需要非常少內(nèi)存的小程序。因?yàn)樗痪S護(hù)0序統(tǒng)計(jì),當(dāng)與那些通用的壓縮程序相比,它壓縮得并不是特別好。例如,當(dāng)壓縮C代碼時(shí),COMP-1通常會(huì)將文本壓縮成每個(gè)字節(jié)大約5位。而這并不足以吸引人,但對(duì)于需要在內(nèi)存寶貴的環(huán)境中運(yùn)行的實(shí)現(xiàn)來(lái)說(shuō)會(huì)很有用。如果只是有一點(diǎn)內(nèi)存可用,維護(hù)排序的列表將增加壓縮和展開(kāi)的速度,而不影響壓縮率。
改進(jìn)
COMP-1創(chuàng)建一個(gè)足夠用的驗(yàn)證程序,但是它可能并不能讓每個(gè)人都很興奮。它壓縮得比0序霍夫曼編碼要好一點(diǎn),但同時(shí)遠(yuǎn)不及像ARC或者PKZIP這樣的程序。為了超過(guò)這些程序的壓縮率,我們需要開(kāi)始給模型代碼加入一些增強(qiáng)功能。所有增強(qiáng)的頂點(diǎn)都在用于創(chuàng)建一個(gè)壓縮程序和一個(gè)展開(kāi)程序的COMP-2.C和EXPAND-2.C以及MODEL-2.C中找到。
最高序的建模
對(duì)于用于此處的模型的每一個(gè)增強(qiáng)是增加模型的序。0序模型在計(jì)算文本文件中當(dāng)前符號(hào)的概率時(shí)并不重視前面任何的任何符號(hào)。通過(guò)考慮文本文件中前面的符號(hào),或者“上下文”,我們可以更精確地預(yù)測(cè)輸入符號(hào)。
當(dāng)我們提升到2序或者3序模型時(shí),一個(gè)問(wèn)題立刻變得很明顯。驗(yàn)證前面上下文使用的一個(gè)好例子就是在文字處理文本文件中嘗試預(yù)測(cè)“REQ”后面會(huì)出現(xiàn)哪個(gè)字符。下一個(gè)字符是“U”的概率至少應(yīng)該是90%,我們編碼這個(gè)符號(hào)少于1位。但是我們使用自適應(yīng)模型的時(shí)間可能是在我們建立起足夠大的統(tǒng)計(jì)體來(lái)開(kāi)始用高概率預(yù)測(cè)“U”之前的某個(gè)時(shí)間。
在固定序模型中的問(wèn)題是,每一個(gè)字符必須有一個(gè)有限非零概率,因此如果并且當(dāng)它出現(xiàn)時(shí)就能被編碼。因?yàn)槲覀儾](méi)有任何關(guān)于我們的輸入文本會(huì)有什么類型的統(tǒng)計(jì)方面的預(yù)備知識(shí),我們通常需要從給每一個(gè)字符指定一個(gè)相等的概率開(kāi)始。這意味著如果我們包括了EOF符號(hào),從-1至255之間的每一個(gè)字節(jié)都有1/257出現(xiàn)在“REQ”之后的機(jī)會(huì)。現(xiàn)在,盡管在這個(gè)特別的上下文之后字母“U”在一行中出現(xiàn)了10次,它的概率將只是增加至11/267。當(dāng)剩余的符號(hào)十有八九將不再可見(jiàn)時(shí),它們占用概率表中的有值空間。
解決這個(gè)問(wèn)題的方案是在給定的上下文中將所有符號(hào)的初始概率設(shè)為0,并且在前面未曾見(jiàn)過(guò)的符號(hào)出現(xiàn)時(shí)有方法回退到一個(gè)不同的上下文中。通過(guò)發(fā)出一個(gè)稱為轉(zhuǎn)義碼(Escape code)的特殊編碼來(lái)做到這一點(diǎn)。對(duì)于前面的“REQ”的上下文,我們可以將轉(zhuǎn)義碼的計(jì)數(shù)置為1,并且所有其它符號(hào)的計(jì)數(shù)置為0。在“REQ”后面字符“U”第一次出現(xiàn)時(shí),我們將必須在一個(gè)不同的上下文中“U”的編碼之后發(fā)出轉(zhuǎn)義碼。在隨后立即更新模型期間,我們把在“REQ”上下文中的“U”的計(jì)數(shù)置為 1,因此它現(xiàn)在的概率為 1/2。根據(jù)隨著字符概率的遞增它的每一次出現(xiàn)所需要的位數(shù)目是遞減的原則,下一次它出現(xiàn)時(shí),將只需用1位來(lái)編碼它。
然后明顯的問(wèn)題是:在發(fā)出轉(zhuǎn)義碼之后,我們使用稱為我們的“回退”上下文做什么呢?在MODEL-2中,如果3序上下文生成一個(gè)轉(zhuǎn)義碼,下一個(gè)要嘗試的上下文是2序上下文。這意味著上下文“REQ”第一次被使用,并且“U”需要被編碼,然后生成一個(gè)轉(zhuǎn)義碼。隨后,MODEL-2程序回退到2序模型,并且使用上下文“EQ”嘗試編碼字符“U”。這會(huì)繼續(xù)向下進(jìn)行直到0序上下文。如果在0序仍然生成轉(zhuǎn)義碼,我們回退到一個(gè)特殊的序(-1)上下文。這個(gè)-1上下文在初始化時(shí)建立,每一個(gè)可能符號(hào)的計(jì)數(shù)為1,并且從不會(huì)更新。因此它保證能夠編碼每一個(gè)符號(hào)。
使用這個(gè)轉(zhuǎn)義碼技術(shù)意味著對(duì)驅(qū)動(dòng)程序只做少許的修改。COMP-2.C程序現(xiàn)在處在一個(gè)循環(huán)中嘗試編碼它的字符:
do {
escaped = convert_int_to_symbol(c, &s);
encode_symbol(compressed_file, &s);
} while (escaped);
建模代碼負(fù)責(zé)記住當(dāng)前的序是多少,并且只要轉(zhuǎn)義碼發(fā)出時(shí)就遞減它。甚至更復(fù)雜的是明了需要哪一個(gè)上下文表用于當(dāng)前序的建模模塊的工作。
更新模型
最高序建模算法的使用要求我們需要記錄從每個(gè)序至最高序的全部上下文表,而不是只記錄用于最高序的一組上下文表。這意味著如果我們正在進(jìn)行2序建模,就有一個(gè)0序表、256個(gè)1序表以及65,535個(gè)2序表。當(dāng)新來(lái)字符已經(jīng)編碼過(guò)或者解碼過(guò)時(shí),建模編碼需要更新每一個(gè)序的相關(guān)表。在前一個(gè)例子中,“U”在“REQ”之后,建模代碼將更新3序“REQ”表中“U”的計(jì)數(shù),2序“EQ”表中的“U”的計(jì)數(shù),1序“Q”表中的“U”的計(jì)數(shù),以及0序表中“U”的計(jì)數(shù)。
更新所有這些表的代碼看起來(lái)如下:
for (order = 0; order <= max_order; order++)
update_model(order, symbol);
對(duì)這個(gè)算法進(jìn)行少許修改可以產(chǎn)生既可以更快地更新又可以獲得更好的壓縮。我們可以只更新這些實(shí)際參與編碼字符的模型,而不是為當(dāng)前上下文更新所有不同序的模型。例如,如果“U”在“REQ”之后作為轉(zhuǎn)義碼(ESCAPE)編碼,我們只遞增在“REQ”和“EQ”模型中“U”的計(jì)數(shù),因?yàn)橹皇窃凇癊Q”表中找到“U”的計(jì)數(shù)。“R”和“”兩個(gè)表都不會(huì)受影響。
這個(gè)對(duì)全部更新方法的修改稱為“排除更新”(update exclusion),因?yàn)樗懦瞬挥酶碌妮^低序模型。這在壓縮率方面通常有一個(gè)較小但是值得注意的改時(shí)。排除更新的工作基于一個(gè)事實(shí),即如果符號(hào)在較高序的模型中頻繁出現(xiàn),它們常常不會(huì)出現(xiàn)在較低序的模型中,這意味著我們不必在較低序的模型中增加它們的計(jì)數(shù)。使用了排除更新方法的更新代碼看起來(lái)如下:
for(order = encoding_order; order <= max_order; order++)
update_model(order, symbol);
轉(zhuǎn)義概率
當(dāng)我們第一次開(kāi)始編碼文本流時(shí),我們會(huì)順理成章地發(fā)出相當(dāng)多的轉(zhuǎn)義碼。從這一點(diǎn)看,用于編碼轉(zhuǎn)義碼的位數(shù)目可能會(huì)對(duì)達(dá)到的壓縮有較大的影響,特別是對(duì)小文件來(lái)說(shuō)。在MODEL-2A.C中,我們將轉(zhuǎn)義碼的計(jì)數(shù)置為1并將其保持為1,而不管剩余上下文表的狀態(tài)。這與Bell、Cleary和Witten稱為“A 方法”(Method A)相當(dāng)。B方法(Method B)將轉(zhuǎn)義字符的計(jì)數(shù)置為目前定義在上下文表中符號(hào)的數(shù)目。因此,如果目前為止已經(jīng)見(jiàn)過(guò)十一個(gè)不同的字符, 那么轉(zhuǎn)義符號(hào)的計(jì)數(shù)不管是多少將置為11。
A方法和B方法兩種方法似乎都工作得相當(dāng)好。在MODEL-2.C中的代碼可以很容易地進(jìn)行修改以支持這兩種方法之一。對(duì)于A方法和B方法最好的事情可能是它們都不需要密集的計(jì)算。當(dāng)使用A方法時(shí),可以將轉(zhuǎn)義符號(hào)加入表中,更新有這個(gè)符號(hào)的表并不會(huì)比更新沒(méi)有這個(gè)符號(hào)的表耗費(fèi)更多的工作。
在MODEL-2.C中,我已經(jīng)實(shí)現(xiàn)了一個(gè)些微復(fù)雜點(diǎn)的轉(zhuǎn)義符計(jì)數(shù)的計(jì)算算法。這個(gè)算法在計(jì)算轉(zhuǎn)義符概率時(shí)嘗試考慮三個(gè)不同的因素。首先,隨著定義在上下文表中的符號(hào)數(shù)目的增加,轉(zhuǎn)義符概率將會(huì)減少,這似乎是有意義的。當(dāng)表中的256個(gè)符號(hào)全部定義后,它達(dá)到它的最大值,使轉(zhuǎn)義符的概率成為0。
其次,這個(gè)算法嘗試考慮表中對(duì)隨機(jī)性的估量。通過(guò)將可以在表中找到的最大計(jì)數(shù)除以平均計(jì)數(shù)可以計(jì)算出這個(gè)隨機(jī)量。這個(gè)比率越高,表的隨機(jī)性就越低。在“REQ”表中有一個(gè)好例子。它可能只定義了三個(gè)符號(hào):有50個(gè)計(jì)數(shù)的“U”、10個(gè)計(jì)數(shù)的“U”和3個(gè)計(jì)數(shù)的“.”。“U”的計(jì)數(shù)50與平均計(jì)數(shù)21的比率相當(dāng)高。這意味著字符“U”可以用相當(dāng)高的精確度來(lái)預(yù)測(cè),并且轉(zhuǎn)義符的概率應(yīng)該更低。在一個(gè)高計(jì)數(shù)是10,并且平均計(jì)數(shù)是8的表中,似乎有更多一點(diǎn)隨機(jī)性,并且轉(zhuǎn)義符的概率可能更高。
最后一個(gè)因素在通過(guò)簡(jiǎn)單地計(jì)算對(duì)于這個(gè)特定的表來(lái)說(shuō)已經(jīng)見(jiàn)過(guò)多少個(gè)符號(hào)來(lái)判斷轉(zhuǎn)義符概率的時(shí)候考慮。隨著符號(hào)數(shù)目的增長(zhǎng),表的可預(yù)測(cè)性上升,使轉(zhuǎn)義符的概率下降。
我用于計(jì)算轉(zhuǎn)義符號(hào)的計(jì)數(shù)數(shù)目的公式在下面說(shuō)明:
count = (256 - number of symbols seen) * number of symbols seen
count = count / (256 * the highest symbol count)
if count is less than 1
count is 1
在這個(gè)等式中丟失的變量是未出現(xiàn)符號(hào)的計(jì)數(shù)。這是隱含在計(jì)算中,因?yàn)檗D(zhuǎn)義符概率是轉(zhuǎn)義符計(jì)數(shù)除以未出現(xiàn)符號(hào)計(jì)數(shù)。 未出現(xiàn)符號(hào)計(jì)數(shù)將自動(dòng)地將轉(zhuǎn)義符的計(jì)數(shù)縮小成概率。???
記分板
當(dāng)使用最高序建模技術(shù)時(shí),一些額外的增強(qiáng)可以用來(lái)改善壓縮效率。當(dāng)我們首先嘗試使用最高序上下文壓縮符號(hào)時(shí),我們可以生成符號(hào)的編碼,或者生成轉(zhuǎn)義碼。如果我們生成轉(zhuǎn)義碼,它意味著符號(hào)以前在這個(gè)上下文中沒(méi)有出現(xiàn)過(guò),因此我們可以將其計(jì)數(shù)為0。但是我們通過(guò)生成轉(zhuǎn)義碼獲得了一些信息。我們可以考慮轉(zhuǎn)義符的上下文并且生成與要被編碼的符號(hào)不匹配的符號(hào)列表。然后在我們?cè)谳^低序模型進(jìn)行計(jì)算時(shí),可以將這些符號(hào)臨時(shí)計(jì)數(shù)為0。在這個(gè)特定的字符的編碼工作完成后,再將這些符號(hào)的計(jì)數(shù)恢復(fù)成它們的永久值。
下面說(shuō)明一個(gè)例子。如果現(xiàn)在的上下文是“HAC”,并且下一個(gè)符號(hào)是“K”,我們將使用下面的表來(lái)編碼K。不用記分板,“HAC”上下文生成一個(gè)具有1/6概率的轉(zhuǎn)義符。“AC”上下文生成具有1/7概率的轉(zhuǎn)義符。“C”上下文生成具有1/40概率的轉(zhuǎn)義符,以及“”上下文最后生成具有1/73概率的“K”。
"" "C" "AC" "HAC"
-------------------------------------------------------------------------
ESC 1 ESC 1 ESC 1 ESC 1
'K' 1 'H' 20 'C' 5 'E' 3
'E' 40 'T' 11 'H' 2 'L' 1
'I' 22 'L' 5 'C' 1
'A' 9 'A' 3
如果我們使用記分板來(lái)排除前面見(jiàn)過(guò)的字符的計(jì)數(shù),我們可以在這些概率中做出重大的改善。第一個(gè)“HAC”的轉(zhuǎn)義符的編碼不受影響,因?yàn)橹安](méi)有看到字符。不過(guò),“AC”的轉(zhuǎn)義碼從它的計(jì)算中消除“C”符號(hào),結(jié)果得到的概率是1/3。“C”轉(zhuǎn)義碼從它的概率中排除“H”和“A”的計(jì)數(shù),概率從1/40上長(zhǎng)到1/17。并且最終,“”上下文通過(guò)排除“E”和“A”的計(jì)數(shù),將概率從1/73上升到1/24。這使編碼這個(gè)字符所要求的位數(shù)目從14.9降為12.9,可觀的節(jié)約。
保持符號(hào)記分板在壓縮中大多數(shù)情況下都會(huì)得到一些改進(jìn),并且它從不會(huì)將事情弄糟。使用記分板的主要問(wèn)題是所有較低序上下文的概率表在每次表被訪問(wèn)時(shí)都必須重新計(jì)算。這導(dǎo)致編碼文本所要求的CPU時(shí)間大大增加。記分板留在MODEL-2.C中,以便在使用它壓縮文本時(shí)驗(yàn)證可能獲得的結(jié)果。
數(shù)據(jù)結(jié)構(gòu)
對(duì)基本建模的所有改善假設(shè)較高序建模實(shí)際上可以在目標(biāo)機(jī)器上完成。隨著序的增加內(nèi)存是問(wèn)題。對(duì)于 MODEL-1中的0序模型,累積的總表占用516字節(jié)的內(nèi)存空間。如果我們對(duì)1序使用相同的數(shù)據(jù)結(jié)構(gòu),內(nèi)存的使用將暴漲到133K字節(jié),這仍然是一個(gè)可以接受的數(shù)目。但是增到達(dá)2序時(shí)統(tǒng)計(jì)單元的內(nèi)存需求將增至34M字節(jié)!因?yàn)槲覀兿矚g能夠試驗(yàn)甚至比2序更高的序,我們需要重新設(shè)計(jì)數(shù)據(jù)結(jié)構(gòu)來(lái)容納計(jì)數(shù)。
為了節(jié)省內(nèi)存空間,我們必須重新設(shè)計(jì)上下文統(tǒng)計(jì)表。在MODEL-1中的表通過(guò)將每個(gè)符號(hào)被用作直接索引到一對(duì)計(jì)數(shù),來(lái)使其自身盡可能地簡(jiǎn)單。在1序模型中,通過(guò)一次在上下表的數(shù)組中的索引來(lái)找到合適的上下文表,然后再次索引正在被討論的符號(hào),代碼看起來(lái)如下:
low_count = totals[last_char][current_char];
high_count = totals[last_char][current_char+1];
range = totals[last_char][256];
這很方便,但是有巨大的浪費(fèi)。首先,全部的上下文空間甚至沒(méi)有使用的表都被分配了。其次,在這些表中,為所有的符號(hào)都分配了空間,而不管這些符號(hào)有沒(méi)有見(jiàn)過(guò)。這兩個(gè)因素導(dǎo)致在較高序模型中有巨大數(shù)量的內(nèi)存被浪費(fèi)。
第一問(wèn)題,即為沒(méi)有使用的上下文預(yù)留了空間的解決方法是將上下文表組織成一個(gè)樹(shù)。我們可以從將0序上下文表放在一個(gè)已知位置開(kāi)始,然后用它來(lái)包含指向1序上下文表的指針。1序上下文表將包含它自身的統(tǒng)計(jì),以及指向2序上下文表的指針。這樣繼續(xù)下去,直到上下文樹(shù)的葉子包含了n序表,而沒(méi)有指向更高序的指針為止。
通過(guò)使用樹(shù)結(jié)構(gòu),我們可以將沒(méi)有使用的指針節(jié)點(diǎn)置為空指針,直到上下文出現(xiàn)。一旦上下文出現(xiàn),創(chuàng)建一個(gè)表并將其加入到樹(shù)結(jié)構(gòu)的父節(jié)點(diǎn)上。
第二個(gè)問(wèn)題是每次新表創(chuàng)建的時(shí)候也創(chuàng)建了表中的256個(gè)計(jì)數(shù)。事實(shí)是,最高序的上下文經(jīng)常只有少數(shù)幾個(gè)符號(hào),因此我們可以通過(guò)只為那些在某個(gè)上下文中可見(jiàn)的符號(hào)分配內(nèi)存來(lái)節(jié)省許多空間。
在實(shí)現(xiàn)了這些改變后,我得到了一組如下的數(shù)據(jù)結(jié)構(gòu):
typedef struct {
??? unsigned char symbol;
??? unsigned char counts;
} STATS;
typedef struct {
??? struct context *next;
} LINKS;
typedef struct context {
??? int max_index;
??? STATS *stats;
LINKS *links;
??? struct context *lesser_context;
} CONTEXT;
新的CONTEXT結(jié)構(gòu)有四個(gè)主要元素。第一個(gè)是計(jì)數(shù)器max_index。max_index告訴當(dāng)前在某個(gè)特定上下文表中定義了多少個(gè)符號(hào)。當(dāng)表第一次創(chuàng)建時(shí),它并沒(méi)有定義符號(hào),并且max_index是-1。完全定義的表的max_index是255。Max_index告訴為*stats和*links所指向的數(shù)組分配了多少個(gè)元素,每一個(gè)元素包含一個(gè)符號(hào)和對(duì)這個(gè)符號(hào)的計(jì)數(shù)。如果CONTEXT表并不是最高序的表之一,它也將有一個(gè)links數(shù)組。對(duì)于定義在stats數(shù)組中的符號(hào)來(lái)說(shuō),在links表中將會(huì)有一個(gè)指向下一個(gè)更高序CONTEXT表的指針。
CONTEXT表的樹(shù)的例子放在在圖1中。放在這里的表將在保持最大3序統(tǒng)計(jì)的時(shí)候,輸入文“ABCABDABE”后創(chuàng)建。9個(gè)輸入符號(hào)已經(jīng)生成了一個(gè)相當(dāng)復(fù)雜的數(shù)據(jù)結(jié)構(gòu),但是它在數(shù)量級(jí)上比一個(gè)數(shù)組的數(shù)組成的結(jié)構(gòu)要小。
Order 0 Order 1 Order 2 Order 3
┌─────────┬─────────┬─────────┬─────────┐
│Context: "" │Context: "A" │Context: "AB" │Context: "ABC" │
│Lesser: NULL │Lesser: "" │Lesser: "B" │Lesser: "BC" │
│Symbol Count Link │Symbol Count Link │Symbol Count Link │Symbol Count Link │
│ A 3 "A" │ B 3 "AB" │ C 1 "ABC"│ A 1 NULL │
│ B 3 "B" ├─────────┤ D 1 "ABD"├─────────┤
│ C 1 "C" │Context: "B" │ E 1 "ABE"│Context: "BCA" │
│ D 1 "D" │Lesser: "" ├─────────┤Lesser: "CA" │
│ E 1 "E" │Symbol Count Link │Context: "BC" │Symbol Count Link │
└─────────┤ C 1 "BC" │Lesser: "C" │ B 1 NULL │
│ D 1 "BD" │Symbol Count Link ├─────────┤
│ E 1 "BE" │ A 1 "BCA"│Context: "CAB" │
├─────────┼─────────┤Lesser: "AB" │
│Context: "C" │Context: "CA" │Symbol Count Link │
│Lesser: "" │Lesser: "A" │ D 1 NULL │
│Symbol Count Link │Symbol Count Link ├─────────┤
│ A 1 "CA" │ B 1 "CAB"│Context: "ABD" │
├─────────┼─────────┤Lesser: "BD" │
│Context: "D" │Context:"BD" │Symbol Count Link │
│Lesser: "" │Lesser: "D" │ A 1 NULL │
│Symbol Count Link │Symbol Count Link ├─────────┤
│ A 1 "DA" │ A 1 "BDA"│Context: "BDA" │
├─────────┼─────────┤Lesser: "DA" │
│Context: "E" │Context: "BE" │Symbol Count Link │
│Lesser: "" │Lesser: "E" │ B 1 NULL │
│Symbol Count Link │Symbol Count Link ├─────────┤
└─────────┴─────────┤Context: "DAB" │
│Lesser: "AB" │
│Symbol Count Link │
│ E 1 NULL │
├─────────┤
│Context: "ABE" │
│Lesser: "BE" │
│Symbol Count Link │
└─────────┘
"ABCABDABE"
圖1
在這個(gè)結(jié)構(gòu)中我們沒(méi)有解釋的一個(gè)元素是lesser_context指針。當(dāng)使用更高序模型時(shí),對(duì)這個(gè)指針的需要變得很明顯。如果我的建模代碼正在嘗試定位3序上下文表,它首先掃描一遍0序符號(hào)列表以查找第一個(gè)符號(hào),匹配,然后掃描1序符號(hào)列表,等等。如果在較低序模型中的符號(hào)列表相對(duì)較滿,這個(gè)掃描可能會(huì)是較長(zhǎng)的過(guò)程。更糟的情況是,每次生成轉(zhuǎn)義碼后,當(dāng)查找較低序上下文時(shí),這個(gè)過(guò)程必須重復(fù)。這些搜索會(huì)消耗過(guò)度量的CPU時(shí)間。
這個(gè)問(wèn)題的解決方法是為每個(gè)表維護(hù)一個(gè)指針,這個(gè)指針指向比其低一級(jí)序的上下文的表。例如,上下文表“ABC”將有它的指向“BC”的向后指針,“BC”有一指向“C”的向后指針,“C”也將有一個(gè)指向“”即空表的指針。然后,建模代碼需要總是要有一個(gè)指向當(dāng)前最高序上下文的指針。通過(guò)這一點(diǎn),查找order-i上下文表簡(jiǎn)單地說(shuō)實(shí)質(zhì)是進(jìn)行指針操作。
例如,在圖1中說(shuō)明的表中,假設(shè)下一個(gè)輸入文本是“X”,并且當(dāng)前的上下文是“ABE”。不需要利用lesser上下文指針的好處,我們不得不為符號(hào)“X”檢查3、2、1和0序表。這會(huì)進(jìn)行總共15次符號(hào)比較,以及3次表查找。使用反向指針消除了所有的符號(hào)比較,并且只讓我進(jìn)行3次表查找。
在維護(hù)后向指針中的工作發(fā)生在更新模型期間。當(dāng)更新圖1上下文樹(shù),以使其在“ABE”之后包含“X”,建模代碼必須為每一個(gè)序/上下文執(zhí)行一組單向查找。這個(gè)代碼放在MODEL-2.C中的add_character_to_model()程序中。每次創(chuàng)建新表時(shí),需要同時(shí)創(chuàng)建后向指針,在設(shè)計(jì)更新程序時(shí)要注意這一情況。
最后一筆:表1和2
對(duì)MODEL-2.C中的上下文樹(shù)的最后一筆是加上兩個(gè)特殊表。1序表前面已經(jīng)討論過(guò)。這是一個(gè)每個(gè)符號(hào)具有固定概率的表。如果在任何較高序模型中都符號(hào)不能找到符號(hào),我們保證它一定會(huì)出現(xiàn)在1序模型中。這個(gè)表是最后的手段。因?yàn)樗仨毐WC它總是能夠?yàn)槊恳粋€(gè)符號(hào)提供一個(gè)編碼,我們不用更新這個(gè)表,這意味著它為每一個(gè)符號(hào)使用一個(gè)固定的概率。
另外,我增加了一個(gè)用于將控制信息從編碼程序傳送到解碼程序的特殊2序表。例如,編碼程序可以傳送一個(gè)-1給解碼程序來(lái)表示一個(gè)EOF情況。因?yàn)檎5姆?hào)總是作為范圍從0到255的無(wú)符號(hào)值來(lái)定義的,建模代碼將一個(gè)負(fù)數(shù)認(rèn)作一個(gè)生成轉(zhuǎn)回2序表的所有路徑的轉(zhuǎn)義碼的特殊符號(hào)。建模代碼也可以檢測(cè)到因?yàn)樗且粋€(gè)負(fù)數(shù),當(dāng)調(diào)用update_model()代碼時(shí),符號(hào)將被忽略的情況。
模型刷新(Model Flushing)
創(chuàng)建2序模型讓我將一個(gè)第二控制碼從編碼程序傳送到展開(kāi)程序。這就是告訴解碼程序?qū)⒔y(tǒng)計(jì)刷出模型的刷新碼(Flush code)。我在壓縮程序的性能開(kāi)始減退時(shí)執(zhí)行這個(gè)操作。這個(gè)下降比率是可調(diào)的,但是我曾經(jīng)使用過(guò)我可以容忍的相當(dāng)于最壞壓縮率90%的統(tǒng)計(jì)。當(dāng)超過(guò)這個(gè)比率時(shí),我通過(guò)將所有的計(jì)數(shù)除以2來(lái)刷新模型。這會(huì)給較新的統(tǒng)計(jì)更多的權(quán)重,這樣應(yīng)該對(duì)改善模型的性能有所幫助。
實(shí)際上模型只要在輸入符號(hào)流中的字符劇烈變化時(shí)就可能會(huì)刷新。例如,如果程序正在壓縮可執(zhí)行文件,可執(zhí)行代碼壓縮期間,累積的統(tǒng)計(jì)可能在壓縮程序的數(shù)據(jù)時(shí)沒(méi)有值。不幸的是,定義檢測(cè)輸入的“自然改變”的算法并不容易。
實(shí)現(xiàn)
即使在這里使用稍微復(fù)雜些的數(shù)據(jù)結(jié)構(gòu),圍繞MODEL-2.C的壓縮程序和展開(kāi)程序的創(chuàng)建有巨大的內(nèi)存要求。當(dāng)運(yùn)行在內(nèi)存限制為640K的DOS機(jī)器上時(shí),這些程序也必須限制為1序,或者對(duì)于有較高冗余率的文本限制為2序。
為了在二進(jìn)制文件上測(cè)試較高序的壓縮率,對(duì)于這些程序來(lái)說(shuō)有三個(gè)選擇。首先,它們可以使用Zortech C并用EMS_handle指針來(lái)編譯。其次,它們可以使用DOS Extender,如Rational Systems 16/M來(lái)創(chuàng)建。第三,它們可以在支持虛擬內(nèi)存的機(jī)器上,如VMS創(chuàng)建。在這里發(fā)布的代碼是以嘗試在所有這三種選擇中可移植的方式編寫(xiě)。
我發(fā)現(xiàn)用額外1M字節(jié)的EMS,我實(shí)際上在我的PC上使用3序壓縮模型來(lái)壓縮任何ASCII文件。一些二進(jìn)制文件要求更多的內(nèi)存。我的Xenix系統(tǒng)使用3序壓縮模型沒(méi)有問(wèn)題,并且按速度來(lái)說(shuō)全面取得最佳性能。我混合使用多個(gè)DOS Extender。出于未知的原因,我使用Lattice C 286和Microsoft C + DOS 16/M的測(cè)試比Zortech的 EMS code慢很多,而邏輯將說(shuō)明與此相反的事實(shí)。它也并不是優(yōu)化的問(wèn)題,因?yàn)楫?dāng)在640K之內(nèi)時(shí),Lattice和Microsoft實(shí)現(xiàn)運(yùn)行得比Zortech快。用Lattice C/286和Zortech 2.06 code創(chuàng)建的可執(zhí)行代碼可以在DDJ的清單服務(wù)上獲得。Rational Systems 16/M授權(quán)協(xié)議要求特許使用金,因此代碼不能發(fā)布。
測(cè)試和比較:CHURN
為了測(cè)試壓縮程序,我創(chuàng)建了一個(gè)稱為CHURN的通用測(cè)試程序。CHURN通過(guò)遍歷一個(gè)目錄及其子目錄來(lái)簡(jiǎn)單地?cái)嚢?#xff0c;壓縮,然后展開(kāi)它在那里找到的所有文件。在每一次壓縮/展開(kāi)之后,比較最初的輸入和最終的輸出來(lái)確保它們是一致的。對(duì)時(shí)間和空間的壓縮統(tǒng)計(jì)也加入到累積總數(shù)中,并且程序繼續(xù)。在程序完成之后,它打印出運(yùn)行完后的累積統(tǒng)計(jì),以便可以檢測(cè)它們。(CHURN和一個(gè)它的Unix變體--CHURNX,出于空間的考慮,在此并不列出。這兩個(gè)程序都可以從DDJ清單服務(wù)上獲得。)
用一個(gè)包含要測(cè)試數(shù)據(jù)的目錄的名稱作為參數(shù)調(diào)用CHURN。我通常將CHURN的輸出重定向到一個(gè)日志文件中,以備以后分析,調(diào)用看起來(lái)如下:
churn c:\textdata > textdata.log
壓縮程序?qū)⒁恍┬畔?xiě)到stderr,因此這此信息在壓縮運(yùn)行的時(shí)候可以顯示在屏幕上。注意隨著測(cè)試不同的程序,CHURN.C按照它調(diào)用的是spawn()還是system()必須作一些修改。
在下面表1中說(shuō)明的是當(dāng)根據(jù)兩個(gè)不同的輸入體測(cè)試不同的壓縮程序時(shí)返回的結(jié)果。第一個(gè)例子是TEXTDATA,有大約1M字節(jié)的一組文本文件,由源代碼、字處理文件和文檔組成。第二個(gè)例子,BINDATA,是大約1M字節(jié)的一組隨機(jī)選擇的文件,包括可執(zhí)行文件,數(shù)據(jù)庫(kù)文件以及二進(jìn)制圖像等等。
我在這兩個(gè)數(shù)據(jù)上用七種不同的壓縮程序完成了測(cè)試。MODEL-1是在本文開(kāi)頭討論的0序示例模型。MODEL-1A實(shí)現(xiàn)1序模型而不需要轉(zhuǎn)義碼。MODEL-2A實(shí)現(xiàn)最高上下文的1序算法,它將為前面未出現(xiàn)過(guò)的符號(hào)生成轉(zhuǎn)義碼。最后,MODEL-2是一個(gè)實(shí)現(xiàn)了所有在本文中所討論的全部改善的最高上下文3序模型。
就比較而言,三個(gè)基于字典的編碼方案也可以運(yùn)行在數(shù)據(jù)集中。COMPRESS是在Unix系統(tǒng)上廣泛使用的16位 LZW實(shí)現(xiàn)。使用16位的PC實(shí)現(xiàn)占用大約500K的內(nèi)存。LZHUF是一個(gè)由Haruyasu Yoshikazi編寫(xiě)的具有自適用霍夫曼編碼階段的LZSS程序,后來(lái)由Paul Edwards修改并投稿給Fidonet。這與用在LHARC程序中的壓縮本質(zhì)上是相同的。最后,由PKWare推出的商業(yè)產(chǎn)品PKZIP 1.10,(Glendale Wisconsin),我們也在數(shù)據(jù)集上進(jìn)行了測(cè)試。PKZIP使用一個(gè)在程序的文檔中討論的私有基于字典的方案。
結(jié)果說(shuō)明了完全優(yōu)化的MODEL-2算法在此測(cè)試的數(shù)據(jù)集上提供了超強(qiáng)壓縮性能,并且在基于文本的數(shù)據(jù)上執(zhí)行得極好。二進(jìn)制數(shù)據(jù)似乎并不適合統(tǒng)計(jì)分析,基于字典的方案在性能上取得與MODEL-2接近一致的結(jié)果。
TEXTDATA - Total input bytes: 987070 Text data
----------------------------------------------------------------------------------
Model-1 Fixed context order-0. 548218 bytes 4.44 bits/byte
Model-1A Fixed context order-1. 437277 bytes 3.54 bits/byte
Model-2A Highest context, max order-1. 381703 bytes 3.09 bits/byte
COMPRESS Unix 16 bit LZW implementation. 351446 bytes 2.85 bits/byte
LZHUF Sliding window dictionary 313541 bytes 2.54 bits/byte
PKZIP Proprietary dictionary based. 292232 bytes 2.37 bits/byte
Model-2 Highest context, max order-3. 299327 bytes 1.94 bits/byte
BINDATA - Total input bytes: 989917 Binary data
----------------------------------------------------------------------------------
Model-1 Fixed context order-0. 736785 bytes 5.95 bits/byte
COMPRESS Unix 16 bit LZW implementation. 662692 bytes 5.35 bits/byte
Model-1A Fixed context order-1. 660260 bytes 5.34 bits/byte
Model-2A Highest context, max order-1. 641161 bytes 5.18 bits/byte
PKZIP Proprietary dictionary based. 503827 bytes 4.06 bits/byte
LZHUF Sliding window dictionary 503224 bytes 4.06 bits/byte
Model-2 Highest context, max order-3. 500055 bytes 4.03 bits/byte
總結(jié)
根據(jù)壓縮率,這些測(cè)試說(shuō)明統(tǒng)計(jì)建模可以至少與基于字典的方法執(zhí)行得一樣好。不過(guò),由于這些程序有較高的資源需要,它們目前稍微有點(diǎn)不實(shí)際。MODEL-2相當(dāng)慢,以范圍在每秒1K之內(nèi)的速度壓縮數(shù)據(jù),并且需要大量的內(nèi)存用于較高序建模。不過(guò),隨著內(nèi)存正在變得更便宜并且處理器變得更強(qiáng)大,在此說(shuō)明的這類方案可能變得實(shí)用。它們?cè)诮裉炜梢杂糜诖鎯?chǔ)或者傳輸開(kāi)銷極高的情形。
使用算術(shù)編碼的0序自適應(yīng)建模在今天可以有效地應(yīng)用于要求極低內(nèi)存消耗的情況。壓縮率可能不如更有老練的模型,但是內(nèi)存的消耗是最小的。
增強(qiáng)
超過(guò)在這里討論的實(shí)現(xiàn),這些算法的性能可以得到重大的改進(jìn)。第一個(gè)改進(jìn)的區(qū)域?qū)⑹莾?nèi)存管理。當(dāng)程序運(yùn)行時(shí)內(nèi)存溢出,它們立刻終止。更明智的方法將是開(kāi)始時(shí)為統(tǒng)計(jì)提供固定數(shù)量的有效內(nèi)存。當(dāng)統(tǒng)計(jì)填滿空間,然后程序?qū)⑼V垢卤?#xff0c;并且只使用它有的東西。這將意味著實(shí)現(xiàn)內(nèi)部?jī)?nèi)存管理程序而不是使用C運(yùn)行時(shí)庫(kù)例程。
另一個(gè)潛在的改時(shí)將在用于上下文表的樹(shù)數(shù)據(jù)結(jié)構(gòu)中。通過(guò)使用哈希來(lái)定位表會(huì)相當(dāng)快,并且可以要求較少的內(nèi)存。上下文表自身也可以得到改善。當(dāng)一個(gè)表到達(dá)超過(guò)為其定義的潛在符號(hào)的50%的點(diǎn)時(shí),可以使用另一個(gè)將計(jì)數(shù)存儲(chǔ)在線性數(shù)組中的數(shù)據(jù)結(jié)構(gòu)。這會(huì)允許更快的索引,并且降低了內(nèi)存需求。
最后,嘗試自適應(yīng)地修改所使用的模型的序的方法可能是有價(jià)值的。當(dāng)使用3序統(tǒng)計(jì)壓縮時(shí),輸入文本的前面部分在統(tǒng)計(jì)表填滿時(shí)生成許多轉(zhuǎn)義碼。開(kāi)始使用0序統(tǒng)計(jì)編碼而保持3序統(tǒng)計(jì)應(yīng)該是可能的。隨著表被填滿,用于編碼的序可能會(huì)增長(zhǎng)直到達(dá)到最大值。
列表 6 comp-1.c 0序固定上下文壓縮程序的驅(qū)動(dòng)程序。通過(guò)從文件中讀取符號(hào),將它們轉(zhuǎn)換成頂、底、范圍集,然后對(duì)它們進(jìn)行編碼,它遵守在BILL.C中為壓縮程序說(shuō)明的模型。 列表 7 expand-1.c 使用0序固定上下文模型的解壓縮程序的驅(qū)動(dòng)程序。 列表 8 model.h 需要與在model-1.c或model-2.c中發(fā)現(xiàn)的建模代碼接口的函數(shù)原型和外部變量聲明。 列表 9 model-1.h 用于0序固定上下文數(shù)據(jù)壓縮程序的建模模塊。 列表 10 comp-2.c 用于可變序的有限上下文壓縮程序的驅(qū)動(dòng)程序模塊。最大的序由命令行選項(xiàng)確定。這個(gè)特別的版本也監(jiān)控壓縮率,并且只要本地(256符號(hào))壓縮率達(dá)到或者高于90%時(shí),刷新模型。 列表 11 expand-2.c 這個(gè)模塊是可變有限上下文展開(kāi)程序的驅(qū)動(dòng)程序。最大的序由命令行選項(xiàng)決定。這個(gè)特別版本可以響應(yīng)由壓縮程序插入在位流中的刷新碼(FLUSH code)。 列表 12 model-2.c 這個(gè)模塊包含所有與comp-2.c和expand-2.c一起使用的建模函數(shù)。這個(gè)建模單元明了從0至max_order所有上下文,缺省是3。 列表 13 churn.c 這是用于測(cè)試壓縮/解壓縮程序精確度、速度和壓縮率的工具程序。用一個(gè)單個(gè)參數(shù)調(diào)用CHURN.EXE將引起CHURN壓縮然后解壓縮在指定目錄下的每一個(gè)文件,檢查壓縮率和操作的精確度。這是在你認(rèn)為你的新算法工作正確時(shí)可以整天運(yùn)行的一個(gè)好程序。 列表 14 churnx.c
churn程序工作在Unix系統(tǒng)上的版本。
轉(zhuǎn)載于:https://www.cnblogs.com/aquar/archive/2009/11/08/3451478.html
總結(jié)
以上是生活随笔為你收集整理的[转]算术编码+统计模型=数据压缩 - 第二部分:统计模型的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 贾跃亭又找到40亿!FF晒工厂实拍:生产
- 下一篇: 一个产品留言统计查寻的分析比较