Est-il possible de récupérer efficacement un nombre non entier de bits d'entropie

a marqué ce sujet comme résolu.

Salut à tous,

En voyant le dernier article sur les tirages aléatoires, ça m’a refait penser à un problème que j’ai plusieurs fois essayé de résoudre sans y arriver: comment récupérer efficacement un nombre non entier de bits d’entropie.

Par exemple, le choix d’un élément parmi 3 de manière uniforme équivaut à $\log_2(3) \approx 1.585$ bits d’entropie (valeur obtenu à partir de la formule de Shannon). Il semble donc logique que 2 bits d’entropie soit suffisant pour faire ce choix. Cependant, la méthode naïve de générer un nombre entre 0 et 3 (inclus) et d’appliquer un modulo 3 ne fonctionne pas puisqu’elle génère un biais: la valeur 0 à une probabilité de 0.5 d’arriver alors que les deux autres ont une probabilité 0.25.

La solution habituelle est de générer un nombre entre 0 et 3 (inclus), de prendre la valeur généré si elle est égale à 0, 1 ou 2 et de recommencer l’opération si elle est égale à 3. En pseudo C, ça donne quelque chose comme:

1
2
3
4
int random3() {
  int r = random4();
  return r < 3 ? r : random3();
}

En notant $E_3$ la consommation moyenne d’entropie de cette méthode, on a $E_3 = 3/4\times 2 + 1/4\times (2+E_3)$. Au final, $E_3 = 8/3 \approx 2.667$. On a donc une méthode qui consomme en moyenne beaucoup plus d’entropie qu’elle n’en génère.

Cependant, en groupant un peu les choses, on peut utiliser 5 bits d’entropie (une valeur entre 0 et 31 inclus) pour générer potentiellement 3 sorties de random3 (pour les valeurs de 0 à 26 inclus). On a alors $3*E = 27/32\times 5 + 5/32 \times (5 + 3*E)$ qui donne $E = 160/81 \approx 1.975$ par sortie de random3. En utilisant $2^8$ et $3^5$, on arrive même à faire tomber le coût à environ $1.686$. Il est donc tout à fait possible d’optimiser la quantité d’entropie utilisé pour faire notre choix uniforme entre 3 éléments. Cependant, cela ne se généralise pas très bien. Par exemple, si je veux

J’ai essayé d’imaginer un moyen qui permettrait de récupérer cette entropie "manquante" en imaginant un processus qui va en quelques sortes générer deux "flux" d’entropie: un premier flux qui donne 0 ou 1 avec une probabilité respectives 3/4 et 1/4 et un deuxième flux qui donne 0, 1 ou 2 de manière uniforme. Toujours en pseudo C:

1
2
3
4
5
6
7
8
9
void random_flux(flux* f1, flux* f2) {
  int r = random4();
  if (r < 3) {
    f1.send(0);
    f2.send(r);
  } else {
    f1.send(1);
  }
}

En moyenne, cette fonction génère bien en moyenne 2 bits d’entropie: le premier flux en reçoit environ $0.811$ par appel de random_flux et le deuxième en reçoit $1.585*3/4 = 1.189$ par appel de random_flux. Cependant, je ne vois pas trop comment convertir le premier flux en un flux binaire aléatoire uniforme ou un flux ternaire aléatoire uniforme (équivalent au deuxième flux) sans perte d’entropie.

D’un point de vue théorique, le problème initial revient à prendre un nombre réel aléatoire choisi uniformément entre 0 et 1 en écriture binaire et à le convertir en écriture ternaire. Cependant, cette méthode pose quelques soucis. Premièrement, il n’est pas possible de borner le nombre de "décimale" binaire nécessaire pour sortir une "décimale" ternaire. Deuxièmement, j’ai l’intuition que même sans être dans un cas dégénéré où il faut un temps fini pour sortir la prochaine décimale, la quantité de mémoire nécessaire à ce calcul va croître de manière non bornée au fur et à mesure que l’on consomme des "décimales" binaires.

Finalement, est-ce qu’il existe un algorithme qui fonctionne en temps fini qui permet de convertir un flux binaire aléatoire uniforme en un flux ternaire aléatoire uniforme. Si oui, en existe-il un qui n’a pas de perte d’entropie (ou dont la perte d’entropie tend vers 0)?

Je n’ai pas tout suivi, mais la question finale m’a titillé.

Je pense que ce que tu cherches, c’est proche de ça :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
k, i, j, sum0 est un entier 
ln2, ln3, lnmax0 est un réel 
ln2 = Log(2)
ln3 = Log(3)

POUR k = 1 A 1000         // ou boucle infinie ...
    i = hasard_0_1()  // renvoie 0 ou 1
    lnmax0 += ln2
    sum0 = sum0 *2 + i
    SI lnmax0 >= ln3 ALORS 
        j = modulo(sum0,3)
        sum0 = ( sum0 -j ) /3   
        print ( j)   
        lnmax0 -= ln3
    FIN
FIN

EDIT : Après une nuit de sommeil, voici un correctif :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
n2 est un entier = 2
n3 est un entier = 3
k, i, j, sum0 est un entier 
ln2, ln3, lnmax0 est un réel 
ln2 = Log(n2)
ln3 = Log(n3)

POUR k = 1 A 1000         // ou boucle infinie ...
    i = hasard(n2)  // renvoie un nombre entre 0 et n2-1
    lnmax0 += ln2
    sum0 = sum0 *n2 + i
    SI lnmax0 >= ln3 ALORS 
            SI sum0 <= n3 ALORS
        j = modulo(sum0,n3)
        sum0 = ( sum0 -j ) /n3  
        print ( j)   
        lnmax0 -= ln3
             SINON
                SI lnmax0 >= 2* ln3 ALORS
                   SI sum0 <= n3*n3 ALORS
              j = modulo(sum0,n3)
              sum0 = ( sum0 -j ) /n3    
              print ( j)   
              lnmax0 -= ln3
                      j = modulo(sum0,n3)
              sum0 = ( sum0 -j ) /n3    
              print ( j)   
              lnmax0 -= ln3
                    SINON   
                      ... 
                    FIN
                FIN
             FIN
    FIN
FIN

Dans la partie laissée en suspens, il faut boucler sur toutes les puissances de 3.

J’ai sorti 2 et 3 de l’algorithme, je les ai mis dans 2 variables car ça marche pour n’importe quel couple de nombres : transformer un flux de random(a) en un flux de random(b), avec b>a.

+0 -0

Je me suis mal exprimé. Ce que je cherche, ce n’est pas de trouver une méthode pour trouver un algorithme qui a une perte d’entropie proportionnellement aussi faible que je veux. Comme tu l’as montré, ce n’est pas très difficile.

Ce que je cherche, c’est plutôt d’avoir un algorithme qui va être en moyenne constant (en mémoire et temps de calcul) et qui as une perte d’entropie absolue bornée (ce qui implique que la proportion d’entropie perdue sur l’entropie utilisé va tendre vers 0). Le premier algorithme que j’ai montré satisfait le première partie, mais pas la non perte d’entropie.

EDIT: en fait, je pense que j’ai mal compris ton code. Je vais regarder ça un peu plus en détail.

+0 -0

Après une nouvelle nuit de sommeil, c’est sûr, ce code n’est pas bon.

La base est bonne :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
k, i, j, sum0 est un entier 
ln2, ln3, lnmax0 est un réel 
ln2 = Log(2)
ln3 = Log(3)

POUR k = 1 A 1000         // ou boucle infinie ...
    i = hasard_0_1()  // renvoie 0 ou 1
    lnmax0 += ln2
    sum0 = sum0 *2 + i
    SI lnmax0 >= ln3 ALORS 
         ?
    FIN
FIN

Mais le traitement à faire dans la partie centrale est fausse. Par exemple, j’ai tiré 2 fois à pile ou face.

Si j’ai sorti 00 ou 11 alors je peux écrire dans le flux de sortie 0 dans le premier cas, et 2 dans le second cas. Mais si j’ai tiré 0 puis 1, je ne sais pas quoi écrire dans le flux de sortie (0 ou 1) et si j’ai tiré 1 puis 0, idem, je ne sais pas quoi écrire (1 ou 2). Je ne vais pas avoir beaucoup de temps aujourd’hui ni demain, mais d’ici ce week-end, je suis à peu près sûr d’avoir une solution correcte.

En effet, si la sortie est 00 ou 11, tu peux écrire 0 ou 2 dans le flux de sortie, mais si supprime complètement les deux bits d’entrée, tu vas quand même perdre de l’entropie.

Si je comprends bien, ce que tu veux faire reviens à prendre un nombre réel entre 0 et 1 écris en base 2 et à l’écrire en base 3. Le problème, c’est qu’il ne sera probablement pas possible de générer une nouvelle décimale sans connaître toutes les décimales d’entrées déjà utilisés. Ça reste cependant la piste qui me semble le plus facilement faisable.

Pour écrire un tel algorithme, on peux utiliser des intervalles. Au début, on a un intervalle d’entrée [0, 1] et un intervalle de sortie [0, 1] qu’il faut diviser en 3. Lorsque l’on reçoit le premier bit d’entrée (disons 0), l’intervalle d’entrée se réduit à [0, 1/2] qui n’est toujours pas contenu entièrement dans un des 3 tiers de l’intervalle de sortie. En recevant le deuxième bit d’entrée (disons 0), l’intervalle d’entrée se réduit à [0, 1/4] qui est entièrement contenu dans [0, 1/3]. On peut donc écrire dans la sortie 0. On continue en recevant 1, l’intervalle d’entrée devient [1/8, 1/4]. On reçoit ensuite 0 et l’intervalle d’entrée devient [1/8, 3/16] qui est contenu dans [1/9, 2/9], on peut donc écrire en sortie 1. L’algorithme peut se continuer ainsi de manière indéfinie.

Intuitivement, l’entropie dans le flux de sortie plus celle contenue dans l’état interne de la fonction de transformation est strictement égal à celle reçu en entrée. J’aimerais bien le prouver, mais les calculs ne me donnent pas ce résultat :( . Voici le détail des calculs, si quelqu’un veux vérifier:

Entropie nécessaire pour sortir un nouveau nombre au début: $-3 \times \frac{1}{3} \times \log_2(\frac{1}{3}) \approx 1.585$.

Entropie nécessaire pour sortir un nouveau nombre après avoir reçu le premier bit d’entrée: $- \frac{2}{3} \times \log_2(\frac{2}{3}) - \frac{1}{3} \times \log_2(\frac{1}{3}) \approx 0.918$.

Différence entre les deux: $\approx 0.6667$. Je m’attendais à ce que la différence soit exactement 1 sachant qu’on a reçu 1 bit d’entropie. Par contre, le fait que ce soit $\frac{1}{3}$ me laisse penser que j’ai dû faire une erreur bête quelque part.

D’un autre côté, découper l’intervalle de sortie autrement, par exemple en coupant en 2 chacun des 3 tiers ne donne pas le même résultat, ce qui confirme que j’ai fait une erreur quelque part.

On a en revanche 2 problèmes: si notre entrée est telle que l’intervalle d’entrée est toujours à cheval entre deux tiers de l’intervalle de sortie. Je suppose que plus on reçois de nombre en entrée, plus la probabilité d’un tel événement deviens petit (de la même manière que mon tout premier algorithme n’est pas garanti de finir s’il reçoit toujours 3). Le deuxième problème, qui est le problème majeur est que le stockage des deux intervalles va consommer de plus en plus de mémoire puisque les fractions ne peuvent pas se simplifier.

L’idée est exactement celle-ci. Il y a en effet un risque d’accumuler des nombres … sans rien recracher, mais la probabilité est très faible.

Par exemple , le premier chiffre et 0 et le second est 1. Je ne peux rien écrire dans le flux de sortie, parce que l’intervalle ’couvert’ est de part et d’autre d’1/3. à l’étape suivante, la probabilité de toujours être dans l’intervalle qui contient 1/3 est de 0.5. Et cette probabilité se divise par 2 à chaque étape. Avoir 20 fois de suite ou 30 fois de suite pas de chance, ça va forcément arriver si l’algorithme tourne des millions de fois. Mais on va accumuler une série de 20 ou 30 bits , et quand le tirage gagnant sort, on pourra envoyer dans le flux de sortie plein de nombres.

En terme d’occupation de mémoire, je ne vois pas pourquoi il y aurait des problèmes.

Banni

En terme d’occupation de mémoire, je ne vois pas pourquoi il y aurait des problèmes.

Je ne vois pas quoi dire plus que Berdes. Pour stocker les intervalles, il va falloir une mémoire non bornée.

Sinon j’ai l’impression que c’est pas possible. Si on veut une consommation mémoire finie, on va avoir une chaine de Markov finie. Quand on va attendre très longtemps, cette chaine va se stabiliser (en rentrant potentiellement dans un cycle au niveau des probas mais ça fait pareil). Les temps d’occupation de chaque nœud vont être rationnels. On a une fonction $f$ prenant un état et retournant un mot sur $\{0,1,2\}$ (la sortie de l’algorithme). L’espérance de la longueur de ce mot doit être $\log_3(2)$. Mais ce n’est pas possible puisque l’espérance de la longueur de $f$ est rationnelle. Je suis allé vite, j’espère que ce n’est pas une bêtise.

On peut très bien avoir une fonction qui ne peut prendre que des valeurs rationnelles, mais dont l’espérance est irrationnelle. Ce n’est pas contradictoire.

Je ne vois pas quoi dire plus que Berdes. Pour stocker les intervalles, il va falloir une mémoire non bornée.

blo yhg

Je ne suis pas s$ur de comprendre cette phrase. Si l’idée est : on va traiter des réels, et on a besoin d’une précision ’infinie’ pour traiter le problème à la perfection alors Ok. Si c’est une autre interprétation, alors je ne saisis pas.

Banni

Je ne suis pas s$ur de comprendre cette phrase. Si l’idée est : on va traiter des réels, et on a besoin d’une précision ’infinie’ pour traiter le problème à la perfection alors Ok. Si c’est une autre interprétation, alors je ne saisis pas.

C’est bien ça !

On peut très bien avoir une fonction qui ne peut prendre que des valeurs rationnelles, mais dont l’espérance est irrationnelle. Ce n’est pas contradictoire.

Oui, mais ici on est sur un espace de probabilité fini dont chaque probabilité est rationnelle. On reste donc dans les rationnels quand on fait la moyenne de la longueur de $f$. Pour chaque état de l’algorithme, la proportion de temps passée à cet état tend vers un nombre rationnel. Cela vient du fait qu’on résout un système linéaire à coefficients rationnels pour trouver ces nombres.

Plus précisément, à chaque fois qu’on exécute l’algorithme, on va au bout d’un moment tomber dans une sous-chaine irréductible (dans laquelle il y a une probabilité non nulle de se rendre de n’importe quel nœud à n’importe quel autre en un certain nombre de pas). Alors il existe une unique mesure de proba invariante sur cette sous-chaine et les proportions de temps passés en chaque état convergent vers cette mesure. Mais cette mesure se trouve comme unique solution d’équations rationnelle, donc elle est rationnelle (l’inverse d’une matrice à coefficients rationnels l’est aussi).

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte