Skryté pasti aritmetického kódování: Proč vaše komprese ztrácí bity

Skryté pasti aritmetického kódování: Proč vaše komprese ztrácí bity

Kvě 04, 2026 compression arithmetic-coding entropy-coding algorithms performance-optimization cloud-computing developer-experience

Skryté pasti aritmetického kódování: Proč váš kompresor ztrácí bity

Implementoval jste si aritmetické kódování? Cítil jste se jako profík. Ten algoritmus krásně převádí sekvence bitů do pravděpodobnostních intervalů. Jenže většina jednoduchých verzí z netu selhává v tichosti.

Nemyslím rychlost na CPU. Jde o poměr komprese – to je důvod, proč entropy kódování vůbec používáte.

Typická verze, co zklamala

Viděl jste to všude. Základní příklad vypadá nějak takto:

let mut left: u32 = 0;
let mut right: u32 = u32::MAX;

fn encode_bit(bit: bool, probability: f32) {
    let mid = left + ((right - left) as f32 * probability) as u32;
    if !bit {
        right = mid;
    } else {
        left = mid + 1;
    }
    
    // Vypisuj bajty, když jsou jisté
    while left >> 24 == right >> 24 {
        output_byte((left >> 24) as u8);
        left <<= 8;
        right = (right << 8) | 0xff;
    }
}

Logické, ne? Držíte interval [left, right]. Každý bit ho zúží podle pravděpodobnosti. Když znáte horní bajt, vypíšete ho a uvolníte přesnost.

Problém? Tady je skrytá nerovnost, kterou v matematice nenajdete.

Zrada bajtových hranic

V ideálním světě se všechny intervaly stejné délky chovají stejně. V 32bitech ne.

Podívejte se na příklady:

  1. Interval od left = 0 nikdy neklesne pod 2^24 bitů.
  2. Interval kolem left = 2^31 - 1 se zmrští na 2 bity.

Proč? Podmínka while left >> 24 == right >> 24 závisí na pozici, ne jen na velikosti.

Když interval přesahuje bajtovou hranici – třeba [2^31 - 1, 2^31] – stane se malinký. Pravděpodobnosti se zaokrouhlí hrubě. Bit s 95% šancí se rozdělí 50/50. Výsledek? Vypouštíte víc bitů, než teorie slíbila.

Dekodér plný pastí

Dekodér má své mouchy:

fn decode_bit(probability: f32) -> bool {
    let mid = left + ((right - left) as f32 * probability) as u32;
    if x <= mid {
        right = mid;
        bit = false;
    } else {
        left = mid + 1;
        bit = true;
    }
    
    while left >> 24 == right >> 24 {
        left <<= 8;
        right = (right << 8) | 0xff;
        x = (x << 8) | (bytes.next().unwrap() as u32);
    }
    
    bit
}

Sleduje left, right a x. Matematicky stačí délka intervalu a pozice bodu.

Proč tři proměnné? Kvůli while smyčce na přesnost. Zkoumá horní bajty, což svazuje logiku na absolutní pozici. Žádná relativní velikost.

Důsledky:

  • Více registrů
  • Horší optimalizace kompilátoru
  • Kód těžší na pochopení

Vše kvůli podmínce, co by měla řešit jen velikost.

Jak to opravit

Přepracujte přesnost. Místo vypisování podle shodných bajtů (závisí na offsetu) použijte délku intervalu (nezávisle na offsetu).

Není to jednoduché – musíte předělat celý loop. Výhoda? Komprese bez ztrát, dekodér jednodušší a rychlejší.

Proč to v NameOcean řešíme

V NameOcean pomáháme vývojářům s AI appkami na Vibe Hosting nebo optimalizovanými systémy. Komprimujete data do cloudu, API payloady nebo crunchujete datasety? Tyhle detaily rozhodují.

Väčšina bere knihovny bez prozkoumání limitů. 5-10% lepší komprese? Na petabytech nebo milionech requestů to jsou tuny dat.

Lekce: "Učebnicové" řešení často skrývá chyby. Koutejte hlouběji. Testujte nerovnosti. Top inženýři algoritmy nejen berou – rozkládají je.


Stavíte kritické appky v cloudu? Infrastruktura se násobí. Malé vylepšení na milionech requestů? Roste to rychle. Optimalizujete stack? Detaily jsou klíč.

Read in other languages:

RU BG EL UZ TR SV FI RO PT PL NB NL HU IT FR ES DE DA ZH-HANS EN