Какой RLE лучше?
У меня тут в комментариях на сайте интересная дискуссия развернулась — есть ли преимущество у реализации RLE, которую я описывал, по сравнению с другим алгоритмом, который вспомнил один из читателей.
Для меня вполне очевидно, что на разных данных каждый из них может выиграть. Но что насчёт реальной жизни?
Я написал небольшую программу на Пайтоне, которая считает сколько занял бы файл, если его сжимать каждым из алгоритмов, а потом прогнал её на каждой из 7000+ файлов в моей папке «Загрузки».
Какой же итог? В 1811 случаях выиграл алгоритм, который вспомнил я, в 2152 — который вспомнил читатель, в 3193 случаях оба алгоритма сделали только хуже — файл стал длиннее.
В итоге, если бы мы писали утилиту для сжатия, можно было бы использовать все три метода (включая сохранение файла в исходном виде), указав в начале файла кодовый байт означающий метод сжатия.
from itertools import groupby
from math import ceil
import os
import sys
try:
filename = sys.argv[1]
except IndexError:
filename = __file__
def reader(filepath):
with open(filepath, "rb") as f:
while chunk := f.read(4096): yield from chunk
def mark(x, y):
if x == y: return (x, y)
c1, c2 = (32, 31) if x < y else (31, 32)
return (f'\x1B[{c1}m{x:+d}', f'\x1B[{c2}m{y:+d}')
flen = os.path.getsize(filename)
grouped_same = (tuple(x[1]) for x in groupby(reader(filename)))
chains = groupby(grouped_same, lambda it: len(it) == 1 or object())
bolk = maks = 0
for diff, x in chains:
x = tuple(x) if diff is True else tuple(x)[0]
l = len(x)
if diff is True:
bolk += l # {x} {y} {z}…
maks += l + ceil(l / (128 + 1)) # {l & 0x7F - 1} {x} {y} {z}…
else:
bolk += (2 + 1) * ceil(l / (255 + 2)) # {x} {x} {l}
maks += (1 + 1) * ceil(l / (128 + 2)) # {l & 0x7F - 2} {x}
bdiff, mdiff = mark(bolk - flen, maks - flen)
print(f'Len\t{flen}')
print(f"Bolk's\t{bolk}\t{bdiff}\x1B[0m")
print(f"Maks'\t{maks}\t{mdiff}\x1B[0m")Добавлено позднее: в комментариях есть замечания к реализации, которые я учёл позднее.
Риспект за дотошность) тут она уместна
👍
Позанудствую. В моем алгоритме есть вариативность в том какие последовательности упаковывать, а какие нет. Например:
11 11 22 22 33 33
прямолинейно по твоей реализации «моего» алгоритма кодирует так:
01 11 11 01 22 22 01 33 33
но короче можно и так записать:
85 11 11 22 22 33 33
твой вариант:
11 11 00 22 22 00 33 33 00
Т.е в моем варианте плохие последовательности можна втупую не кодировать и по сути разбить на незакодированные блоки с префиксом 7f. Тогда файл увеличится на 1/128 в самом худшем случае. А в твоем худшем случае на 50%
Хм, похоже это действительно стоило бы учесть.
Акт занудства 2: возможно твой алгоритм так много побеждал из-за того, что у него максимальна длина последовательности 255. Однако ничто не запрещает сделать ее гибкой, например при ff считать что за ним будут идти 2 байта на длину последовательности. Ну типа того как утф8 построен
Ну конечно, как и в том алгоритме, который я вспомнил. Можно сделать флаг когда мы кодируем двумя байтами.
Зануда мод: ну да, я и имел ввиду, что после введение данного расширения, по данному параметру твой алгоритм уже не будет иметь преимущества по длине последовательности и будет ещё больше проигрывать в целом.
Зато поддержал активность в комментариях)