간단한 이진 데이터의 효율적인 압축


27

에서 이진 숫자를 포함하는 파일이 있습니다 .02n1

0000000000
0000000001
0000000010
0000000011
0000000100
...
1111111111

7z 는이 파일을 매우 효율적으로 압축하지 않았습니다 (n = 20의 경우 22MB300kB 로 압축 됨).

매우 간단한 데이터 구조를 인식하고 파일을 몇 바이트로 압축 할 수있는 알고리즘이 있습니까? 또한 CS 또는 정보 이론이 스마트 알고리즘을 연구하는 영역을 알고 싶습니다. "AI"가 너무 광범위하므로보다 구체적인 키워드를 제안하십시오.
대칭 개념은 데이터 압축에서 근본적인 역할을 수행해야하지만 검색 쿼리 "데이터 압축의 대칭"및 "데이터 압축의 그룹 이론"은 놀랍게도 거의 관련이없는 결과를 반환합니다.


11
어떤면에서는 최적의 압축 (첨가 상수까지) 인 Kolmogorov 복잡성을 확인하십시오. 불행하게도, 콜 모고 로프의 복잡성은 ... 계산할 수없는
Yuval 교수 Filmus

12
왜 그 데이터를 압축해야합니까? 필요할 때 언제든지 재생성 할 수 없습니까? (@YuvalFilmus가 언급 한 Kolmogorov 복잡성 접근법과 밀접한 관련이 있습니다. Kolmogorov 복잡성은 본질적으로 출력을 생성하는 가장 짧은 프로그램의 길이입니다).
David Richerby

7
저는 20 년 전에 고등학교에서 그러한 알고리즘을 작성했습니다. 입력이 주어지면 "몇 바이트"(최적의 시나리오에서는 약 3,500,000 : 1)로 압축되었을 것입니다. 그러나 데이터는 이처럼 거의 보이지 않으므로 이와 같은 알고리즘을 사용하는 것은 실용적이지 않습니다. 일반적인 압축 알고리즘은 단순한 패턴이 아닌 복잡한 패턴을 처리해야합니다. 누구나 선형 데이터를 저장하는 알고리즘을 작성할 수 있지만 흥미로운 데이터를 저장하는 것은 어려운 일입니다.
phyrfox

3
n = 20은 어떻게 22MB를 제공합니까? 4 바이트 정수를 사용하는 경우
4.2MB를 얻습니다

3
@JiK 오, 알았어. 그것은 단일 비트를 나타 내기 위해 8 비트를 사용하지 않는 압축의 첫 번째 개념 일 것입니다.
njzk2

답변:


27

이것은 델타 압축에 대한 명확한 사용 사례 인 것 같습니다 . 이 선험적으로 알려진 경우 이는 사소한 일입니다. 첫 번째 숫자는 그대로 저장하고 다음 각 숫자 는 이전과 의 차이 만 저장합니다 . 귀하의 경우, 이것은 줄 것입니다n

0
1
1
1
1
...

그러면 간단한 델타 길이의 그룹 (즉, 2 개) 만 있으므로 간단한 런타임 길이 인코딩을 사용 하여 공간에 저장할 수 있습니다 .O(n)O(1)

을 알 수없는 경우 가장 간단한 것은이 델타 / 런 길이 표현이 가장 짧은 단어 크기에 대한 무차별 검색입니다. 아마도 크기의 청크 를 무작위로 선택 하여 좋은 안정성을 유지하면서 을 찾는 오버 헤드를 상쇄 할 수 있습니다.nNn

DW의 "전부 또는 전무"제안과 달리, 런 길이 인코딩을 사용한 델타 압축은 실제로 저해상도 오디오와 같은 일부 실제 컨텐츠에 대해 적절한 압축 비율을 제공 할 수 있습니다. (따라서 저품질, 매우 짧은 대기 시간 및 저전력 오디오 압축에 적합합니다.)


44

물론 알고리즘이 있습니다. 내 알고리즘은 다음과 같습니다.

  1. 파일의 진수를 명령이 포함 된 경우 먼저 확인 으로 일부, . 그렇다면 0 비트 다음에 1 비트 뒤에 0 비트를 쓴다 .02n1nn

  2. 그렇지 않으면 1 비트를 쓴 다음 파일의 7z 압축을 작성하십시오.

이것은 특정 구조의 파일에 매우 효율적입니다.

요점은 데이터 압축에 무료 점심이 없다는 것입니다. 한 유형의 파일을 잘 압축하여 다른 유형의 압축을 악화시키는 압축 알고리즘을 작성할 수 있습니다. 그러나 압축 할 파일의 특성에 대해 우선적으로 알고있는 경우 해당 특정 유형의 파일에 맞게 알고리즘을 최적화 할 수 있습니다.

이 영역은 "데이터 압축"입니다. 우리의 참조 태그 및 데이터 압축에 교과서를 읽어 보시기 바랍니다.


5
압축기의 역할은 일반적인 패턴을 인식하고 활용하는 것입니다. 이 패턴이 흔하지 않거나 모호한 것은 아닙니다. 따라서 왜 악용되지 않는지 묻는 것은 자연스러운 질문입니다. 그에게 트레이드 오프가 있다고 말하거나 그 패턴을 제외한 모든 것에 실패하는 알고리즘을 제공하는 것은 총 cop-out입니다.
Mehrdad

17
물론 나에게는 드문 것 같습니다. 이는 실제 컴프레서가 극히 드물게 나타나는데, 컴프레서가 찾는 패턴의 종류와 비교할 때 매우 드 compared니다.
amalloy

17
@Mehrdad 그것은 멋진 경찰이 아닙니다 : 그것은 요점 입니다. 단순히 생성되고 단순히 확인되는 패턴 X에는 해당 패턴을 찾아 처리하는 압축 알고리즘이 있습니다. "이런 X를 다루는 압축 알고리즘이 있습니까?"에 나오는 질문에 대한 답입니다. 물론 약간 더 복잡한 패턴을 찾는 알고리즘이 항상 있습니다. 그리고 그것보다 약간 더 복잡한 패턴을 찾는 것이 있습니다 . ad infinitum . 당신의 반대는 근거가 없습니다.
David Richerby

의견은 긴 토론을위한 것이 아닙니다. 이 대화는 채팅 으로 이동 되었습니다 .
Gilles 'SO- 악마 그만해

이 원칙의 가장 큰 적용은 비트 토렌트 마그넷 링크로, 모든 크기의 파일 또는 파일 모음이 고정 된 160 비트 데이터로 간단히 표현 (압축)됩니다. 물론 해시 충돌이 발생할 위험이 있습니다.
slebetman

17

BWT (Burrows-Wheeler transform)를 사용하는 모든 것은이를 상당히 잘 압축 할 수 있어야합니다.

내 빠른 파이썬 테스트 :

>>> 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는 16 비트 입력의 경우 1114111에서 237 바이트로 줄어 듭니다.

아아, BWT는 많은 응용 프로그램에 비해 너무 느리고 메모리가 부족합니다. 특히 이것은 파이썬에서 순진한 구현입니다-내 컴퓨터에서 2 ** 20 전에 RAM이 부족합니다.

Pypy를 사용하면 전체 2 ** 20 입력을 실행할 수 있었고 BWT와 bz2를 사용하여 2611 바이트로 압축합니다. 그러나 3 분 이상 걸리고 사용 된 4GB 이상의 RAM에서 정점에 도달했습니다.

또한 불행히도이 접근법은 여전히 ​​O (2 ^ n) 출력 공간이며, 적어도 곡선 맞춤 1..20에서 나타납니다.


당신은 제거 할 수 eval수행하여 : for first in (bwt_c, nothing, lzma, zlib, gzip, bz2):fOut = first.compress(inputData).
kasperd

@ kasperd-이 경우 어떻게 이름을 인쇄합니까? 개인적으로 이름과 참조를 동기화 된 상태로 유지하는 것보다 평가를 수행하는 것이 더 간단하고 오류가 적습니다.
TLW

5
먼저 bwt 다음에 bz2가 이것을 아주 잘 압축합니다. 이것은 매우 이상한 행동 이며 아마도이 정확한 패턴 때문일 것입니다. bwt를 두 번 수행하는 경우 (bz2는 BWT를 기반으로 함) 일반적으로 압축 성능 이 저하 됩니다. 또한 오늘날 bwt는 4 times block size메모리에서 (예 : ~ 4MB), >10 MB/s많은 응용 프로그램에서 사용할 수있는 속도 (나는 bwt 라이브러리 / 압축 알고리즘의 저자 임 ) 에서 일반적으로 실행됩니다 . gzip도 압축 가능한 결과가 매우 좋습니다. 공유해 주셔서 감사합니다. bwt를 두 번 사용하는 것에 대한 연구는 없습니다.
Christoph

3
@Christoph-bz2가 BWT를 기반으로한다는 것을 알고 있습니다 ... 실제로``만 사용 bz2 ''의 효과에 대한 답변을 작성하기 시작했지만 예상대로 압축되지 않았으며 'huh ', 그리고 내 BWT가 더 나아질 지 결정했습니다. 나는 단지 출력을 위해 컴프레서를 필요로했고, '어떻게되는지 다른 컴프레서를 시도 할 수도있다'고 갔다.
TLW

1
@Christoph-다른 모습을 보았습니다. 이 데이터의 2bwt는 RLE 인코딩에 매우 적합한 것을 생성합니다 . 마찬가지로 16 비트 입력에서 0, 1, 2, ... 중첩 BWT에 필요한 RLE 쌍의 수를 세면 622591 1081343 83 ...
TLW

10

PNG 인코딩은 원하는 것을 정확하게 수행합니다. 그것은 매우 체계적인 데이터뿐만 아니라 실제 데이터에서도 작동합니다.

PNG에서 각 행은 4로 지정된 필터로 인코딩됩니다. 그중 하나는 "이 픽셀을 그 값과 그 위의 픽셀 값의 차이로 인코딩합니다"입니다. 필터링 후 DEFLATE를 사용하여 데이터를 압축합니다.

이 필터링은 Run Length Encoding을 사용하는 대신 더 강력한 DEFLATE 알고리즘을 사용하는 것을 제외하고 그의 답변에서 leftaroundabout이 언급 한 Delta Encoding 의 특정 예입니다 . 동일한 목표를 달성하지만 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 문제가 발생합니다 . 즉, 특정 번호 후에 시험 실행을 중단해야합니다. 이 프로그램이 확실히 원하는 출력을 생성 할 수 없는지 여부를 알기 전에

이러한 무차별 접근 방식의 검색 트리는 프로그램 길이에 따라 기하 급수적으로 증가하며 가장 간단한 프로그램 (5-7 명령어 길이)을 제외하고는 실용적이지 않습니다.


이런 종류의 입력을 확인하는 데 걸리는 시간뿐만 아니라 " 이진법 으로 을 센 다음 "에 사용하는 인코딩 방식에 따라 차지하는 공간이기도합니다. 압축의 다른 모든 부분에서 "이진 숫자로 계산되지 않습니다."로 주석을 달아야합니다. n
David Richerby

1
또한 중단하지 않고 "도브테일 링"기술을 사용하여 정지되지 않는 프로그램을 처리 할 수 ​​있습니다. 이를 위해, 단계마다 첫 프로그램을 실행 한 다음 단계마다 첫 번째 프로그램을 실행하는 식으로 진행됩니다. 당신이없는 여러 단계마다 프로그램을 실행하는 것이이 방법 사전 경계하지만, 다음 중 하나를 시도하기 전에 중단 하나 개의 프로그램 기다릴 필요없이. , N , N + 1 , N 1nnn+1n1
David Richerby

음, Mathematica와 같은 도구는 많은 시퀀스에 대한 기능을 찾습니다 ...
Raphael

3

압축 비율은 전적으로 대상 압축 풀기 프로그램에 따라 다릅니다. 압축 해제 기가 순차적 인 4 바이트 숫자를 숫자 당 4 바이트보다 더 컴팩트하게 디코딩 할 수 없으면 SOL입니다.

일련 번호의 인코딩을 허용하는 여러 가지가 있습니다. 예를 들어, 차등 인코딩. 한 번에 n 바이트를 취한 다음 비트의 차이 또는 xor를 가져와 결과를 압축합니다. 여기에 모든 바이트 수에 대해 시도 할 수있는 4 가지 옵션이 추가되었습니다. identity a'[i] = a[i]; 차이 a'[i] = a[i-1]-a[i]; 역차 a'[i] = a[i]-a[i-1]; 그리고 xor a'[i] = a[i]^a[i-1]. 즉, 방법을 선택하기 위해 2 비트를 추가하고 4 개 중 3 개의 옵션에 대한 바이트 수를 의미합니다.

그러나 모든 데이터가 고정 길이 레코드의 시퀀스는 아닙니다. 차등 인코딩은 그 점에 적합하지 않습니다 (컴프레서가 약간의 데이터에서 작동한다는 것을 경험적으로 증명할 수 없다면).

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.