การบีบอัดข้อมูลไบนารีอย่างง่าย


27

ฉันมีไฟล์ที่มีเลขฐานสองเรียงจากถึง2 n - 1 :02n1

0000000000
0000000001
0000000010
0000000011
0000000100
...
1111111111

7zไม่บีบอัดไฟล์นี้อย่างมีประสิทธิภาพมาก (สำหรับ n = 20, 22 MB ถูกบีบอัดเป็น 300 kB)

มีอัลกอริทึมที่สามารถรับรู้โครงสร้างข้อมูลที่ง่ายมากและบีบอัดไฟล์เป็นหลายไบต์หรือไม่? นอกจากนี้ฉันต้องการทราบว่าสาขาใดของ CS หรือทฤษฎีสารสนเทศศึกษาอัลกอริทึมอัจฉริยะเช่นนั้น "AI" กว้างเกินไปโปรดแนะนำคำหลักที่เป็นรูปธรรมมากขึ้น
แนวคิดเรื่องความสมมาตรควรมีบทบาทพื้นฐานในการบีบอัดข้อมูล แต่เคียวรีค้นหา "สมมาตรในการบีบอัดข้อมูล" และ "ทฤษฎีกลุ่มในการบีบอัดข้อมูล" กลับมาอย่างน่าประหลาดใจแทบไม่มีอะไรเกี่ยวข้องเลย


11
ตรวจสอบความซับซ้อนของ Kolmogorov ซึ่งในบางแง่การบีบอัดที่ดีที่สุด (ขึ้นอยู่กับค่าคงที่สารเติมแต่ง) น่าเสียดายที่ความซับซ้อนของ Kolmogorov นั้นไม่สามารถคำนวณได้ ...
Yuval Filmus

12
ทำไมคุณต้องบีบอัดข้อมูลนั้น คุณไม่สามารถสร้างมันใหม่เมื่อใดก็ได้ที่คุณต้องการ (ซึ่งมีความสัมพันธ์อย่างใกล้ชิดกับวิธีการที่ซับซ้อนของ Kolmogorov ที่กล่าวถึงโดย @YuvalFilmus: ความซับซ้อนของ Kolmogorov นั้นเป็นความยาวของโปรแกรมที่สั้นที่สุดที่สร้างผลลัพธ์)
David Richerby

7
ฉันเขียนอัลกอริธึมเช่นนี้ในโรงเรียนมัธยมเมื่อ 20 ปีก่อน เมื่อกำหนดอินพุตของคุณมันจะถูกบีบอัดเป็น "ไม่กี่ไบต์" (ประมาณ 3,500,000: 1 ในสถานการณ์ที่เหมาะสม) อย่างไรก็ตามข้อมูลไม่ค่อยมีลักษณะเช่นนี้ดังนั้นจึงไม่มีประโยชน์ที่จะมีอัลกอริทึมเช่นนี้ อัลกอริทึมการบีบอัดทั่วไปต้องจัดการกับรูปแบบที่ซับซ้อนไม่ใช่เรื่องง่าย ทุกคนสามารถเขียนอัลกอริทึมเพื่อจัดเก็บข้อมูลเชิงเส้น แต่การจัดเก็บข้อมูลที่น่าสนใจคือความท้าทาย
phyrfox

3
n = 20 ให้ 22 เมกะไบต์อย่างไร ฉันได้รับ 4.2 MB หากใช้จำนวนเต็ม 4 ไบต์
njzk2

3
@ JiK โอ้ตกลง นั่นจะเป็นแนวคิดแรกของการบีบอัดไม่ใช่การใช้ 8 บิตเพื่อแทนบิตเดียว
njzk2

คำตอบ:


27

นี้น่าจะเป็นกรณีที่ใช้ที่ชัดเจนสำหรับการบีบอัดเดลต้า ถ้าเป็นที่รู้จักมาก่อนสิ่งนี้สำคัญ: เก็บคำต่อคำหมายเลขแรกและสำหรับแต่ละหมายเลขถัดไปจะเก็บเฉพาะความแตกต่างจากก่อนหน้า ในกรณีของคุณสิ่งนี้จะให้n

0
1
1
1
1
...

ซึ่งสามารถทำได้ด้วยการเข้ารหัสความยาวการรันอย่างง่ายจะถูกเก็บไว้ในพื้นที่เนื่องจากมีเพียงกลุ่มO ( 1 ) (คือสอง) ของเดลตาที่ต่างกันO(n)O(1)

หากไม่มีใครรู้ว่าสิ่งที่ง่ายที่สุดคือการค้นหาแบบขนาดเดรัจฉานเพื่อหาคำขนาดที่การแสดงความยาวเดลต้า / รันเดอร์นี้สั้นที่สุด บางทีอาจจะเป็นเพียง แต่การค้นหานี้เพื่อสุ่มเลือกnก้อนขนาด Nเพื่อตัดทอนค่าใช้จ่ายในการค้นหาnในขณะที่รักษาความน่าเชื่อถือที่ดีNn

ซึ่งแตกต่างจากข้อเสนอ“ ทั้งหมดหรือไม่มีเลย” ของ DW การบีบอัดเดลต้าพร้อมการเข้ารหัสแบบรันไทม์สามารถให้อัตราส่วนการบีบอัดที่สมเหตุสมผลสำหรับเนื้อหาบางประเภทในโลกแห่งความเป็นจริงเช่นเสียงความละเอียดต่ำ (ดังนั้นจึงเหมาะสำหรับการบีบอัดสัญญาณเสียงที่มีคุณภาพต่ำความหน่วงต่ำและต่ำมาก)


44

แน่นอนว่ามีอัลกอริธึม นี่คืออัลกอริทึมของฉัน:

  1. ขั้นแรกให้ตรวจสอบว่าไฟล์มีคำสั่งให้เลขฐานสองจากที่จะ2 n - 1สำหรับบางn ถ้าเป็นเช่นนั้นเขียน 0 บิตตามด้วยnหนึ่งบิตตามด้วย 0 บิต02n1nn

  2. ถ้าไม่เขียนออกมา 1 บิตจากนั้นเขียนการบีบอัด 7z ของไฟล์

สิ่งนี้มีประสิทธิภาพอย่างมากสำหรับไฟล์ของโครงสร้างเฉพาะนั้น

ประเด็นคือ: ไม่มีอาหารกลางวันฟรีในการบีบอัดข้อมูล คุณอาจสามารถสร้างอัลกอริทึมการบีบอัดที่บีบอัดไฟล์ประเภทหนึ่งได้เป็นอย่างดี แต่ถ้าคุณรู้เบื้องต้นเกี่ยวกับลักษณะของไฟล์ที่คุณจะบีบอัดคุณสามารถเพิ่มประสิทธิภาพอัลกอริทึมของคุณสำหรับไฟล์ประเภทนั้น ๆ

พื้นที่คือ "การบีบอัดข้อมูล" ดูแท็กการของเราและอ่านตำราเกี่ยวกับการบีบอัดข้อมูล


5
หน้าที่ของคอมเพรสเซอร์คือการจดจำรูปแบบทั่วไปและใช้ประโยชน์จากมัน มันไม่เหมือนรูปแบบนี้เป็นเรื่องแปลกหรือคลุมเครือ ดังนั้นจึงเป็นคำถามธรรมดาที่จะถามว่าทำไมจึงไม่มีการใช้ประโยชน์ บอกให้เขารู้ว่ามีการแลกเปลี่ยนหรือให้อัลกอริธึมที่ล้มเหลวกับเขาทุกอย่างยกเว้นรูปแบบนั้นคือการรวมทั้งหมด
Mehrdad

17
แน่นอนว่าดูแปลกสำหรับฉัน สิ่งนี้จะเกิดขึ้นในข้อมูลในชีวิตจริงน้อยมากเมื่อเทียบกับรูปแบบชนิดของคอมเพรสเซอร์ที่ดีที่มองหา
amalloy

17
@Mehrdad มันไม่ได้เป็นต่อว่าต่อขานตำรวจออก: มันเป็นจุดทั้งหมด สำหรับรูปแบบ X ใด ๆ ที่สร้างขึ้นและตรวจสอบเพียงอย่างเดียวมีอัลกอริทึมการบีบอัดที่ค้นหารูปแบบนั้น ดังนั้นนี่คือคำตอบสำหรับคำถามใด ๆ ตามแนวของ "มีอัลกอริธึมการบีบอัดที่เกี่ยวข้องกับ X หรือไม่" แน่นอนว่ามีอัลกอริทึมที่มองหาลวดลายที่ซับซ้อนกว่าเล็กน้อย และมีหนึ่งที่มีลักษณะรูปแบบที่ซับซ้อนมากขึ้นกว่าหนึ่งเช่นกันว่าไม่มีที่สิ้นสุด คำคัดค้านของคุณไม่ได้รับการพิสูจน์
David Richerby

ความคิดเห็นไม่ได้มีไว้สำหรับการอภิปรายเพิ่มเติม การสนทนานี้ได้รับการย้ายไปแชท
Gilles 'SO- หยุดความชั่วร้าย'

แอปพลิเคชั่นสุดขีดของหลักการนี้คือการเชื่อมโยงแม่เหล็ก bittorrent ที่ไฟล์หรือคอลเลกชันของไฟล์ที่มีขนาดใดจะแสดงเพียง (บีบอัด) เพื่อ 160 บิตของข้อมูลคงที่ แน่นอนว่ามีความเสี่ยงที่อาจเกิดการชนกันของแฮชได้
slebetman

17

สิ่งใดก็ตามที่ใช้ BWT (Burrows – Wheeler transform) ควรจะสามารถบีบอัดได้ดีพอสมควร

การทดสอบ Python อย่างรวดเร็วของฉัน:

>>> import gzip
>>> import lzma
>>> import zlib
>>> import bz2
>>> import time
>>> dLen = 16
>>> inputData = '\n'.join('{:0{}b}'.format(x, dLen) for x in range(2**dLen))
>>> inputData[:100]
'0000000000000000\n0000000000000001\n0000000000000010\n0000000000000011\n0000000000000100\n000000000000010'
>>> inputData[-100:]
'111111111111010\n1111111111111011\n1111111111111100\n1111111111111101\n1111111111111110\n1111111111111111'
>>> def bwt(text):
    N = len(text)
    text2 = text * 2
    class K:
        def __init__(self, i):
            self.i = i
        def __lt__(a, b):
            i, j = a.i, b.i
            for k in range(N): # use `range()` in Python 3
                if text2[i+k] < text2[j+k]:
                    return True
                elif text2[i+k] > text2[j+k]:
                    return False
            return False # they're equal

    inorder = sorted(range(N), key=K)
    return "".join(text2[i+N-1] for i in inorder)

>>> class nothing:
    def compress(x):
        return x

>>> class bwt_c:
    def compress(x):
        return bwt(x.decode('latin_1')).encode('latin_1')

>>> for first in ('bwt_c', 'nothing', 'lzma', 'zlib', 'gzip', 'bz2'):
    fTime = -time.process_time()
    fOut = eval(first).compress(inputData)
    fTime += time.process_time()
    print(first, fTime)
    for second in ('nothing', 'lzma', 'zlib', 'gzip', 'bz2'):
        print(first, second, end=' ')
        sTime = -time.process_time()
        sOut = eval(second).compress(fOut)
        sTime += time.process_time()
        print(fTime + sTime, len(sOut))

bwt_c 53.76768319200005
bwt_c nothing 53.767727423000224 1114111
bwt_c lzma 53.83853460699993 2344
bwt_c zlib 53.7767307470001 5930
bwt_c gzip 53.782549449000044 4024
bwt_c bz2 54.15730512699997 237
nothing 6.357100005516259e-05
nothing nothing 0.0001084830000763759 1114111
nothing lzma 0.6671195740000258 27264
nothing zlib 0.05987233699988792 118206
nothing gzip 2.307255977000068 147743
nothing bz2 0.07741139000017938 187906
lzma 0.6767229399999906
lzma nothing 0.6767684639999061 27264
lzma lzma 0.6843232409999018 27324
lzma zlib 0.6774435929999072 27280
lzma gzip 0.6774431810001715 27292
lzma bz2 0.6821310499999527 27741
zlib 0.05984937799985346
zlib nothing 0.05989508399989063 118206
zlib lzma 0.09543156799986718 22800
zlib zlib 0.06264000899977873 24854
zlib gzip 0.0639041649999399 24611
zlib bz2 0.07275534999985211 21283
gzip 2.303239570000187
gzip nothing 2.303286251000145 147743
gzip lzma 2.309592880000082 1312
gzip zlib 2.3042639900002087 2088
gzip gzip 2.304663197000309 1996
gzip bz2 2.344431411000187 1670
bz2 0.07537686600016968
bz2 nothing 0.07542737000017041 187906
bz2 lzma 0.11371452700018381 22940
bz2 zlib 0.0785322910001014 24719
bz2 gzip 0.07945505000020603 24605
bz2 bz2 0.09332576600013454 27138

(ตัวเลขที่นี่คือ 'first_compressor second_compressor time_taken bytes_out')

(BWT นำมาจากที่นี่ )

นี่คือ 'ไม่กี่ไบต์เท่านั้น' แต่ยังดีกว่าเพียงแค่ gzip เพียงอย่างเดียว BWT + bz2 ลดลงเป็น 237 ไบต์จาก 1114111 สำหรับอินพุต 16 บิตตัวอย่างเช่น

อนิจจา BWT นั้นช้าเกินไปและใช้หน่วยความจำมากเกินไปสำหรับแอปพลิเคชั่นหลายตัว โดยเฉพาะอย่างยิ่งเมื่อได้รับสิ่งนี้คือการใช้งานแบบไร้เดียงสาใน Python บนเครื่องของฉันฉัน RAM หมดก่อน 2 ** 20

ด้วย Pypy ฉันสามารถรันอินพุต 2 ** 20 เต็มและบีบอัดมันเป็น 2611 ไบต์ด้วย BWT ตามด้วย bz2 แต่ใช้เวลามากกว่า 3 นาทีและจุดสูงสุดที่ 4GB มากกว่าใช้ ...

ยังน่าเสียดายที่วิธีการนี้ยังคงเป็นพื้นที่เอาต์พุต O (2 ^ n) ซึ่งจะปรากฏขึ้น - อย่างน้อยจากการปรับเส้นโค้ง 1..20


คุณสามารถกำจัดevalโดยการทำ: และfor first in (bwt_c, nothing, lzma, zlib, gzip, bz2): fOut = first.compress(inputData)
kasperd

@kasperd - ฉันจะพิมพ์ชื่ออย่างไรในกรณีนั้น โดยส่วนตัวแล้วมันง่ายกว่า (และมีข้อผิดพลาดน้อยกว่า) ในการทำ eval มากกว่าที่จะพยายามซิงค์ชื่อ + ข้อมูลอ้างอิง
TLW

5
bwt แรกจากนั้น bz2 บีบอัดได้ดีมาก นี่เป็นพฤติกรรมที่แปลกประหลาดอย่างมากและอาจเป็นเพราะรูปแบบที่แน่นอนนี้ โปรดทราบว่าคุณกำลังทำ bwt สองครั้ง (bz2 อิงจาก BWT) ซึ่งส่งผลให้การบีบอัดแย่ลง นอกจากนี้โปรดทราบว่าวันนี้ bwt ทำงานแบบปกติที่4 times block sizeหน่วยความจำ (เช่น ~ 4MB สำหรับสิ่งนี้) และที่ความเร็ว>10 MB/s(ฉันเป็นผู้เขียน bwt ไลบรารี / อัลกอริทึมการบีบอัดดังกล่าว) ซึ่งค่อนข้างใช้งานได้สำหรับแอปพลิเคชันจำนวนมาก โปรดทราบว่าแม้ gzip จะให้ผลลัพธ์ที่สามารถบีบอัดได้ดีมาก ขอบคุณสำหรับการแบ่งปันฉันไม่ได้ตระหนักถึงการวิจัยเกี่ยวกับการใช้ bwt สองครั้ง
Christoph

3
@ คริสโตฟ - ฉันรู้ว่า bz2 มาจาก BWT ... ฉันเริ่มเขียนคำตอบสำหรับผลของ 'แค่ใช้ bz2' แต่พบว่ามันไม่ได้บีบอัดได้ดีอย่างที่ฉันคาดไว้ 'และตัดสินใจดูว่า BWT ของฉันจะทำได้ดีกว่านี้หรือไม่ มีเพียงฉันเท่านั้นที่ต้องการคอมเพรสเซอร์สำหรับเอาต์พุตและไป 'อาจลองใช้คอมเพรสเซอร์อื่นเพื่อดูว่าเกิดอะไรขึ้น'
TLW

1
@Christoph - ฉันดูอีกครั้ง 2 bwts ของข้อมูลนี้สร้างสิ่งที่เป็นอย่างมากที่คล้อยตามการเข้ารหัส RLE ถ้าคุณนับจำนวนคู่ RLE ที่จำเป็นสำหรับ 0, 1, 2, ... ที่ซ้อนกัน BWT บนอินพุต 16 บิตคุณจะได้รับ 622591 1081343 83 ...
TLW

10

การเข้ารหัส PNG ทำสิ่งที่คุณต้องการอย่างแน่นอน มันใช้งานได้กับข้อมูลในชีวิตจริงด้วยไม่ใช่เพียงแค่ข้อมูลที่มีการจัดระเบียบอย่างมาก

ใน PNG แต่ละแถวจะถูกเข้ารหัสด้วยตัวกรองซึ่งจะระบุ 4 แถว หนึ่งในนั้นคือ "เข้ารหัสพิกเซลนี้เป็นความแตกต่างระหว่างค่าของมันและมูลค่าของพิกเซลที่อยู่เหนือมัน" หลังจากการกรองข้อมูลจะถูกบีบอัดโดยใช้ DEFLATE

การกรองนี้เป็นตัวอย่างเฉพาะของการเข้ารหัส Delta ที่กล่าวถึงโดย leftaroundabout ในคำตอบของเขายกเว้นแทนที่จะติดตามด้วยการเข้ารหัสความยาว Run คุณจะติดตามมันด้วยอัลกอริทึม DEFLATE ที่มีประสิทธิภาพยิ่งขึ้น มันบรรลุเป้าหมายเดียวกันเท่านั้น DEFLATE เท่านั้นที่จะจัดการอินพุตที่หลากหลายมากขึ้นในขณะที่ยังคงอัตราส่วนการบีบอัดที่ต้องการ

เครื่องมืออีกอย่างหนึ่งที่มักใช้ในข้อมูลทางวิทยาศาสตร์ที่ตัวกรองแบบง่าย + DEFLATE ไม่ค่อยมีประสิทธิภาพเท่าการเข้ารหัสแบบ RICE ใน RICE คุณจะต้องใช้บล็อกของค่าและส่งออกบิตที่สำคัญที่สุดทั้งหมดก่อนจากนั้นบิตที่สองที่มีความสำคัญมากที่สุดจนถึงบิตที่มีนัยสำคัญน้อยที่สุด จากนั้นคุณบีบอัดผลลัพธ์ สำหรับข้อมูลของคุณที่ไม่ค่อยมีประสิทธิภาพเท่าการกรองสไตล์ PNG (เนื่องจากข้อมูลของคุณสมบูรณ์แบบสำหรับการกรอง PNG) แต่สำหรับข้อมูลทางวิทยาศาสตร์จำนวนมากมันมีแนวโน้มที่จะนำไปสู่ผลลัพธ์ที่ดี ในข้อมูลทางวิทยาศาสตร์จำนวนมากเราเห็นบิตที่สำคัญที่สุดมีแนวโน้มที่จะเปลี่ยนแปลงอย่างช้าๆในขณะที่ค่าที่สำคัญน้อยที่สุดคือเกือบจะสุ่ม สิ่งนี้จะแยกข้อมูลที่คาดเดาได้สูงออกจากข้อมูลที่มีความน่าเชื่อถือสูง

0000000000       00000  most significant bits
0000000001       00000
0000000010  =>   00000
0000000011       00000
0000000100       00000
                 00000
                 00000
                 00001
                 00110
                 01010 least significant bits

5

อัลกอริธึมเชิงปฏิบัติใด ๆ ที่ค้นหาโครงสร้างเฉพาะจะถูก จำกัด เฉพาะกับโครงสร้างที่มีการกำหนดรหัสแบบยาก คุณสามารถแก้ไข 7z เพื่อรับรู้ลำดับเฉพาะนี้ แต่โครงสร้างที่เฉพาะเจาะจงนี้จะเกิดขึ้นในชีวิตจริงบ่อยแค่ไหน? ไม่เพียงพอที่จะรับประกันเวลาที่ใช้ในการตรวจสอบอินพุตสำหรับอินพุตนี้

ในทางปฏิบัติแล้วเราสามารถเข้าใจถึงคอมเพรสเซอร์ที่สมบูรณ์แบบในฐานะอัลกอริทึมที่พยายามสร้างโปรแกรมที่สั้นที่สุดที่ให้ผลลัพธ์ที่กำหนด ไม่จำเป็นต้องพูดว่าไม่มีวิธีการทำเช่นนี้ แม้ว่าคุณจะลองใช้การแจงนับที่โหดร้ายของโปรแกรมที่เป็นไปได้ทั้งหมดและตรวจสอบว่าพวกเขาสร้างผลลัพธ์ที่ต้องการ ( ไม่ใช่ความคิดที่บ้าอย่างสิ้นเชิง ) คุณจะพบปัญหา Haltingซึ่งหมายความว่าคุณจะต้องยกเลิกการทดลองใช้หลังจากจำนวนหนึ่ง ของขั้นตอนการดำเนินการก่อนที่คุณจะรู้ว่าโปรแกรมนี้ไม่สามารถสร้างผลลัพธ์ที่ต้องการได้อย่างแน่นอน

แผนผังการค้นหาสำหรับวิธีการแบบ brute-force เติบโตแบบทวีคูณด้วยความยาวของโปรแกรมและไม่เหมาะสำหรับทุกคนยกเว้นโปรแกรมที่ง่ายที่สุด (เช่น 5-7 คำสั่งที่ยาว)


n

1
nnn+1n1แต่ละขั้นตอนและอื่น ๆ ซึ่งหมายความว่าคุณเรียกใช้ทุกโปรแกรมสำหรับหลายขั้นตอนที่ไม่ได้จำกัด ขอบเขตไว้ล่วงหน้าแต่ไม่ต้องรอให้โปรแกรมหนึ่งหยุดก่อนที่จะลองอีกครั้ง
David Richerby

ดีเครื่องมือเช่น Mathematica หาฟังก์ชั่นสำหรับหลายลำดับ ...
ราฟาเอล

3

อัตราส่วนการบีบอัดขึ้นอยู่กับตัวขยายการบีบอัดเป้าหมาย หากตัวขยายการบีบอัดไม่สามารถถอดรหัสหมายเลข 4 ไบต์ตามลำดับได้อย่างกระชับมากกว่า 4 ไบต์ต่อหมายเลขแสดงว่าคุณเป็น SOL

มีหลายสิ่งที่จะอนุญาตให้เข้ารหัสตัวเลขตามลำดับ ตัวอย่างเช่นการเข้ารหัสที่แตกต่างกัน คุณใช้เวลา n ไบต์ในแต่ละครั้งจากนั้นให้ผลต่างหรือ xor ของบิตแล้วบีบอัดผลลัพธ์ นี่จะเพิ่มตัวเลือก 4 ตัวที่นี่เพื่อลองใช้กับทุก ๆ ไบต์: ตัวตนa'[i] = a[i]; ความแตกต่างa'[i] = a[i-1]-a[i]; ความแตกต่างกลับa'[i] = a[i]-a[i-1]; a'[i] = a[i]^a[i-1]และการ XOR นั่นหมายถึงการเพิ่ม 2 บิตเพื่อเลือกวิธีการและจำนวนไบต์สำหรับ 3 จาก 4 ตัวเลือก

อย่างไรก็ตามข้อมูลทั้งหมดไม่ใช่ลำดับของเร็กคอร์ดที่มีความยาวคงที่ การเข้ารหัสที่แตกต่างกันนั้นไม่สมเหตุสมผล (ยกเว้นว่าคอมเพรสเซอร์สามารถพิสูจน์ได้อย่างชัดเจนว่ามันใช้งานได้กับข้อมูลสักเล็กน้อย)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.