Qual è il modo più efficiente per memorizzare un intervallo numerico?


29

Questa domanda riguarda quanti bit sono necessari per memorizzare un intervallo. O in altri termini, per un dato numero di bit, qual è l'intervallo massimo che può essere memorizzato e come?

Immagina di voler memorizzare un sottointervallo compreso nell'intervallo 0-255.

Quindi, ad esempio, 45-74.

Possiamo archiviare l'esempio sopra come due byte senza segno, ma mi sembra che ci debba essere una ridondanza di informazioni lì. Sappiamo che il secondo valore è maggiore del primo, quindi nel caso in cui il primo valore sia grande, sono necessari meno bit per il secondo valore e nel caso in cui il secondo valore sia grande, sono necessari meno bit per il primo .

Sospetto che qualsiasi tecnica di compressione produrrebbe un risultato marginale, quindi potrebbe essere una domanda migliore porre "qual è l'intervallo massimo che può essere memorizzato in un byte?". Questo dovrebbe essere più grande di quello che si può ottenere memorizzando i due numeri separatamente.

Esistono algoritmi standard per fare questo tipo di cose?


devi anche memorizzare l'inizio dell'intervallo?
Ewan,

@Ewan Non seguo davvero. Nell'esempio sopra, 45 è l'inizio (il minimo) e 74 è la fine (il massimo) ed entrambi devono essere memorizzati.
rghome

2
così è la domanda quanto spazio richiede un tipo che può memorizzare qualsiasi intervallo. o quanto spazio richiede un tipo che può contenere 45-74?
Ewan,

1
Mentre pensare a questo è certamente buono, spero sicuramente che tu non lo faccia in applicazioni reali. Il motivo è che la quantità di complessità delle applicazioni reali è così grande che dobbiamo accettare meno del 100% di codice ottimizzato .... Ecco perché esistevano i compilatori.
NoChance,

3
@rghome, sono d'accordo, anche il requisito più semplice produce centinaia di righe di codice. Ciascuno è soggetto a errori. Personalmente, pagherei per l'hardware piuttosto che aumentare la complessità del software.
NoChance,

Risposte:


58

Basta contare il numero di possibili intervalli. Esistono 256 intervalli con limite inferiore 0 (0-0, 0-1, ... 0-254, 0-255), 255 intervalli con limite inferiore 1, ... e infine 1 intervallo con limite inferiore 255 (255- 255). Quindi il numero totale è (256 + 255 + ... + 1) = 257 * 128 = 32.896. Poiché questo è leggermente superiore a 2 15 = 32.768, avrai comunque bisogno di almeno 16 bit (2 byte) per memorizzare queste informazioni.

In generale, per i numeri da 0 a n-1, il numero di intervalli possibili è n * (n + 1) / 2. Questo è inferiore a 256 se n è 22 o meno: n = 22 offre 22 * ​​23/2 = 253 possibilità. Quindi un byte è sufficiente per i sottointervalli 0-21 .

Un altro modo di esaminare il problema è il seguente: la memorizzazione di una coppia di numeri interi nell'intervallo da 0 a n-1 è quasi la stessa della memorizzazione di una sottofase di 0- (n-1) più un singolo bit che determina se il primo numero è inferiore o superiore al secondo. (La differenza viene dal caso in cui entrambi gli interi sono uguali, ma questa possibilità diventa sempre più piccola man mano che n aumenta di dimensioni.) Ecco perché con questa tecnica puoi risparmiare solo un po 'e probabilmente il motivo principale per cui viene usato raramente.


Grazie. Il numero di bit richiesti per n intervalli è log (n) / log2. Inserendo tutto in Wolfram Alpha mi ha dato la seguente formula compatibile con Excel per il calcolo del valore massimo per il sottointervallo per un dato numero di bit: = INT ((SQRT (POWER (2, N + 3) + 1) - 1) / 2 )
rghome

9
Il TLDR è che guadagni circa mezzo bit, quindi in generale non vale davvero la pena comprimerlo.
rghome

Sì, tende a un po 'per N grande, ma non vale davvero la pena.
Glorfindel,

Cordiali saluti, N + 3 nell'equazione sembra strano, ma una potenza di 2 viene dalla tua equazione e le altre due provengono dalla parte 4ac della formula quadratica.
rghome,

1
A proposito, il tuo conteggio sconta l'intervallo vuoto, per il quale valgono tutte le combinazioni non conteggiate. Quindi n * (n + 1) / 2 + 1! Un minuscolo cambiamento.
Deduplicatore l'

17

Per un numero così piccolo di bit, è impossibile salvare molti bit come Glorfindel ha sottolineato . Tuttavia, se il dominio che stai utilizzando ha qualche bit in più, puoi ottenere risparmi significativi per il caso medio codificando gli intervalli con il valore iniziale e un delta.

Supponiamo che il dominio sia il numero intero, quindi 32 bit. Con l'approccio ingenuo, sono necessari 64 bit (inizio, fine) per memorizzare un intervallo.

Se passiamo a una codifica di (inizio, delta), possiamo costruire da questo la fine dell'intervallo. Sappiamo che nel peggiore dei casi, l'inizio è 0 e il delta ha 32 bit.

2 ^ 5 è 32, quindi codifichiamo la lunghezza del delta in cinque bit (nessuna lunghezza zero, aggiungi sempre 1) e la codifica diventa (inizio, lunghezza, delta). Nel peggiore dei casi, questo costa 32 * 2 + 5 bit, quindi 69 bit. Quindi nel peggiore dei casi, se tutti gli intervalli sono lunghi, questa è peggio della codifica ingenua.

Nel migliore dei casi, costa 32 + 5 + 1 = 38 bit.

Ciò significa che se devi codificare molti intervalli e tali intervalli coprono solo una piccola parte del tuo dominio, alla fine utilizzi meno spazio in media utilizzando questa codifica. Non importa come vengono distribuiti gli inizi, poiché l'avvio richiederà sempre 32 bit, ma non importa come vengono distribuite le lunghezze degli intervalli. Se più lunghezze hai, migliore è la compressione, più intervalli hai che coprono l'intera lunghezza del dominio, peggiore sarà questa codifica.

Tuttavia, se si hanno molti intervalli raggruppati attorno a punti di partenza simili (ad esempio perché si ottengono valori da un sensore), è possibile ottenere risparmi ancora maggiori. È possibile applicare la stessa tecnica al valore iniziale e utilizzare un bias per compensare il valore iniziale.

Supponiamo che tu abbia 10000 intervalli. Gli intervalli sono raggruppati attorno a un determinato valore. Si codifica il bias con 32 bit.

Usando l'approccio ingenuo, occorrerebbero 32 * 2 * 10 000 = 640 000 bit per memorizzare tutti questi intervalli.

La codifica del bias richiede 32 bit e la codifica di ogni intervallo richiede nel migliore dei casi quindi 5 + 1 + 5 + 1 = 12 bit, per un totale di 120 000 + 32 = 120 032 bit. Nel peggiore dei casi, sono necessari 5 + 32 + 5 + 32 bit, quindi 74 bit, per un totale di 740 032 bit.

Ciò significa che otteniamo 10 000 valori su un dominio che impiega 32 bit per la codifica

  • 120 032 bit con la codifica delta intelligente nel migliore dei casi
  • 640.000 bit con inizio ingenuo, codifica finale, sempre (nessun caso migliore o peggiore)
  • 740 032 bit con la codifica delta intelligente nel caso peggiore

Se si considera la codifica ingenua come base, ciò significa un risparmio fino all'81,25% o fino al 15,625% in più.

A seconda della modalità di distribuzione dei valori, tali risparmi sono significativi. Conosci il tuo dominio aziendale! Scopri cosa vuoi codificare.

Come estensione, puoi anche cambiare il bias. Se si analizzano i dati e si identificano gruppi di valori, è possibile ordinare i dati in bucket e codificare ciascuno di questi bucket separatamente, con la propria distorsione. Ciò significa che è possibile applicare questa tecnica non solo agli intervalli raggruppati attorno a un singolo valore iniziale, ma anche agli intervalli raggruppati attorno a più valori.

Se i tuoi punti di partenza sono distribuiti equamente, questa codifica non funziona molto bene.

Questa codifica è ovviamente estremamente negativa da indicizzare. Non puoi semplicemente leggere il valore x-esimo. Può praticamente essere letto solo in sequenza. Ciò è appropriato in alcune situazioni, ad es. Streaming in rete o archiviazione di massa (ad es. Su nastro o HDD).

Valutare i dati, raggrupparli e scegliere la distorsione corretta può essere un lavoro sostanziale e potrebbe richiedere una messa a punto per risultati ottimali.


8

Questo tipo di problema è l'oggetto del seminario di Claude Shannon, A Mathematical Theory of Communication , che introduceva la parola "bit" e la compressione dei dati più o meno inventata.

L'idea generale è che il numero di bit utilizzati per codificare un intervallo è inversamente proporzionale alla probabilità che si verifichi tale intervallo. Ad esempio, supponiamo che l'intervallo 45-74 appaia circa 1/4 del tempo. Puoi dire che la sequenza 00 corrisponde a 45-74. Per codificare l'intervallo 45-74, si genera "00" e ci si ferma.

Supponiamo anche che gli intervalli 99-100 e 140-155 compaiano ciascuno circa 1/8 del tempo. È possibile codificare ciascuno di essi con una sequenza di 3 bit. Qualsiasi 3 bit funzionerà finché non iniziano con "00", che è già stato riservato per l'intervallo 45-74.

00: 45-74
010: 99-100
101: 140-155

Puoi continuare in questo modo fino a quando ogni intervallo possibile ha una codifica. L'intervallo meno probabile potrebbe richiedere oltre 100 bit. Ma va bene perché raramente appare.

Ci sono gli algoritmi per trovare l'ottimale di codifica. Non cercherò di spiegarli qui, ma puoi trovarne di più visitando il link sopra o cercando "Teoria dell'informazione", "Codifica Shannon-fano" o "Codifica Huffman".

Come altri hanno sottolineato, è probabilmente meglio memorizzare il numero iniziale e la differenza tra il numero iniziale e quello finale. Dovresti usare una codifica per l'inizio e un'altra per la differenza, poiché hanno diverse distribuzioni di probabilità (e immagino che quest'ultima sia più ridondante). Come suggerito dal poligono, l'algoritmo migliore dipende dal tuo dominio.


1
Sì, il dominio aziendale è davvero importante. In realtà abbiamo considerato l'utilizzo della codifica Huffmann per i pregiudizi per la data di inizio, ma alla fine abbiamo deciso di non farlo dopo aver eseguito alcune analisi statistiche sui dati del mondo reale. La semplicità di utilizzo della stessa codifica per polarizzazione e delta era più importante dell'aggiunta di Huffmann in cima, inoltre è necessario inviare anche l'intero albero di Huffmann. È comunque una buona idea tenere a mente la codifica di Huffmann.
Poligono

1

Per espandere la risposta di @Glorfindel:

Come n → ∞, (n - 1) → n. Pertanto, Ω (intervalli) → n² / 2 e log (Ω (intervalli)) → (2n - 1). Poiché la codifica ingenua richiede 2n bit, la compressione massima asintotica salva solo 1 bit.


1

C'è una risposta simile, ma per ottenere una compressione ottimale è necessario:

  1. Un metodo di codifica entropia ottimale (leggi sulla codifica aritmetica e l'equivalente essenzialmente (stesso rapporto di compressione, un po 'più veloce ma anche più difficile da capire) ANS )
  2. Quante più informazioni possibili sulla distribuzione dei dati. Fondamentalmente, questo non implica solo "indovinare" quanto spesso può apparire un numero, ma spesso è possibile escludere certe possibilità. Ad esempio, è possibile escludere intervalli di dimensione negativa e possibilmente 0, a seconda di come si definisce un intervallo valido. Se hai più intervalli da codificare contemporaneamente, puoi ordinarli, ad esempio in ordine di larghezza decrescente, o aumentando il valore iniziale / finale, ed escludere un sacco di valori (ad esempio, se garantisci un ordine diminuendo la larghezza, l'intervallo precedente aveva una larghezza di 100 e il valore iniziale per il prossimo è 47, devi solo considerare le possibilità fino a 147 per i valori finali).

È importante sottolineare che il numero 2 significa che vuoi codificare le cose in modo tale che i valori più informativi (per bit codificati) vengano prima. Ad esempio, mentre ho suggerito di codificare un elenco ordinato "così com'è", di solito sarebbe più intelligente codificarlo come "albero binario" - cioè se sono ordinati per larghezza e hai lenelementi, inizia con la codifica dell'elemento len/2. Dire che aveva larghezza w. Ora conosci tutti gli elementi prima che abbia larghezza da qualche parte in [0, w], e tutti gli elementi dopo hanno larghezza da qualche parte in [w, max val che accetti]. Ripeti in modo ricorsivo (suddividendo nuovamente ogni mezzo elenco a metà, ecc.) Fino a quando non hai coperto gli lenelementi (a meno che non sia corretto, ti consigliamo di codificarelenprima quindi non devi preoccuparti di terminare i token). Se "valore massimo che accetti" è veramente aperto, potrebbe essere consigliabile codificare prima il valore più alto che appare effettivamente nei tuoi dati, ovvero l'ultimo elemento, quindi eseguire il partizionamento binario. Ancora una volta, qualunque cosa sia più istruttiva per bit prima.

Inoltre, se stai codificando prima la larghezza dell'intervallo e conosci il valore massimo possibile con cui hai a che fare, ovviamente puoi escludere tutti i valori iniziali che lo farebbero traboccare ... hai l'idea. Trasforma e ordina i tuoi dati in modo tale da poter dedurre il più possibile sul resto dei dati mentre li decodifichi, e un algoritmo di codifica entropia ottimale assicurerà che non stai sprecando bit che codificano informazioni che "già conosci" .

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.