Génération de nombres aléatoires uniformément distribués à l'aide d'une pièce

26

Vous avez une pièce. Vous pouvez le retourner autant de fois que vous le souhaitez.

Vous voulez générer un nombre aléatoire r tel que uner<br,une,bZ+ .

La distribution des nombres doit être uniforme.

C'est facile si ba=2n :

r = a + binary2dec(flip n times write 0 for heads and 1 for tails) 

Et si ba2n ?

Pratik Deoghare
la source
Utilisez l'algorithme Han-Hoshi - divisez essentiellement l'intervalle en deux, utilisez votre bit aléatoire (coin flip) pour choisir au hasard l'un des deux intervalles, puis répétez ce processus du côté que vous avez choisi jusqu'à ce que vous manquiez de bits. Cela vous donnera un intervalle uniformément distribué à partir d'une partition de la ligne réelle. Plus vous avez de flips, plus l'intervalle est précis.
zenna

Réponses:

13

Ce que vous recherchez est basé sur l' échantillonnage de rejet ou la méthode d'acceptation-rejet (notez que la page Wiki est un peu technique).

Cette méthode est utile dans ces types de situations: vous voulez choisir un objet aléatoire dans un ensemble (un entier aléatoire dans l'ensemble dans votre cas), mais vous ne savez pas comment faire cela, mais vous peut choisir un objet aléatoire dans un ensemble plus grand contenant le premier ensemble (dans votre cas, [ a , 2 k + a ] pour certains k tels que 2 k + a b ; cela correspond à k flips de pièces).[a,b][a,2k+a]k2k+abk

Dans un tel scénario, vous continuez à choisir des éléments dans l'ensemble le plus grand jusqu'à ce que vous choisissiez au hasard un élément dans l'ensemble plus petit. Si votre ensemble plus petit est suffisamment grand par rapport à votre ensemble plus grand (dans votre cas, contient au plus deux fois plus d'entiers que [ a , b ] , ce qui est assez bon), cela est efficace.[a,2k+a][a,b]

Un autre exemple: supposons que vous vouliez choisir un point aléatoire dans un cercle de rayon 1. Maintenant, il n'est pas vraiment facile de trouver une méthode directe pour cela. Nous passons à la méthode d'acceptation-rejet: nous échantillonnons des points dans un carré 1x1 englobant le cercle, et testons si le nombre que nous tirons se trouve à l'intérieur du cercle.

Alex ten Brink
la source
3
Notez que si nous rejetons des échantillons de afin d'obtenir une distribution sur B , le nombre d'itérations attendu est | A |AB(comme nous effectuons une expérience avec la distribution géométrique). |A||B|
Raphael
Je me souviens avoir vu quelque part que cela ne peut pas être fait exactement à moins que la plage soit une puissance de 2 (comme il va de soi, par exemple 1/3 n'a pas d'expansion binaire terminale).
vonbrand
7

(détails techniques: la réponse correspond à la sélection du nombre )ax<b

Puisque vous êtes autorisé à lancer votre pièce autant de fois que vous le souhaitez, vous pouvez obtenir votre probabilité aussi près que vous le souhaitez d'uniformiser en choisissant une fraction (en utilisant le radix binaire: vous retournez le pièce pour chaque chiffre après le point) et multipliez r par b - a pour obtenir un nombre compris entre 0 et [ba-1] (arrondi à un nombre entier). Ajoutez ce numéro à un et vous avez terminé.r[0,1]rbaa

Exemple : disons . 1/3 en binaire est 0,0101010101 .... Ensuite, si votre flip est compris entre 0 et 0,010101 ... votre choix est b . si elle est comprise entre 0,010101 .. et 0,10101010 ... votre choix sera un + 1 , sinon ce sera un + 2 .ba=3ba+1a+2

Si vous retournez votre pièce fois, chaque nombre entre a et b sera choisi avec probabilité 1tab.1ba±2(t+1)

A sonné.
la source
1
Cela ne donne pas une distribution uniforme. Pour certaines applications (par exemple, crypto, parfois), cela peut être très mauvais.
Gilles 'SO- arrête d'être méchant'
3
@Gilles: Il peut être fixé pour donner une distribution parfaitement uniforme en retournant jusqu'à ce qu'il ne soit plus possible que le résultat change. C'est la réponse la plus efficace.
Neil G
@NeilG Je sais que cela peut être corrigé, mais le réparer serait une partie cruciale de la réponse.
Gilles 'SO- arrête d'être méchant'
2
@ Gilles: Tu as raison. Il pourrait modifier la réponse pour dire que vous pouvez produire une distribution parfaitement uniforme si vous inversez tout en . +1 de ma part pour le meilleur cas moyen et le pire des cas. (ba)(f+2t1)(ba)(f2t1)
Neil G
@NeilG, il ne peut pas être "fixé", car il existe un ensemble assez important d'entiers qui n'ont pas de fraction binaire terminale.
vonbrand
7

Choisissez un nombre dans la prochaine plus grande puissance de la plage 2 et jetez les réponses supérieures à .ba

n = b-a;
N = round_to_next_larger_power_of_2(n)
while (1) {
  x = random(0 included to N excluded);
  if (x < n) break;
}
r = a + x;
Trevor Blackwell
la source
4
Et pourquoi ça marche?
Raphael
@Raphael êtes-vous sceptique ou voulez-vous simplement que l'affiche explique plus en détail?
Suresh
2
@Suresh: The latter. The pseudo code could be polished a bit, but it implements what other answerers explain. Without justification, this answer is not worth much on its own.
Raphael
6

DDkDdDdA/2kAA/2k1/|D|.

If you want to generate n independent uniform samples of D, then the expected number of coin tosses you need (under the optimal algorithm) is roughly nlog2|D|. More generally, if you want to generate from a distribution of entropy H, you need roughly nH random bits in expectation. An algorithm achieving this is arithmetic decoding, applied to an (ostensibly) infinite sequence of random bits.

Yuval Filmus
la source
3

If b-a is not a power of 2 then you may have to flip many coins in order to get a result. You may even never get a result, but that is unlikely in the extreme.

Methods

The simplest method is to generate a number in [a, a+2^n), where 2^n >= b-a, until one happens to land in [a, b). You throw away a lot of entropy with this method.

A more expensive method allows you to keep all of the entropy, but becomes very expensive computationally as the number of coin flips / dice rolls increases. Intuitively it is like treating the coin flips as the digits of a binary number to the right of the decimal point, converting that number from base 2 to base a-b after, and returning the digits of that number as they become 'stuck'.

Example

The following code converts rolls of a fair n-sided die into rolls of a fair m-sided die (in your case n=2, m=a-b), with increasing marginal cost as the number of rolls increases. Note the need for a Rational number type with arbitrary precision. One nice property is that converting from n-sided to m-sided and back to n-sided will return the original stream, though perhaps delayed by a couple rolls due to digits having to become stuck.

public static IEnumerable<BigInteger> DigitConversion(this IEnumerable<BigInteger> inputStream, BigInteger modIn, BigInteger modOut) {
    //note: values are implicitly scaled so the first unfixed digit of the output ranges from 0 to 1
    Rational b = 0; //offset of the chosen range
    Rational d = 1; //size of the chosen range
    foreach (var r in inputStream) {
        //narrow the chosen range towards the real value represented by the input
        d /= modIn;
        b += d * r;
        //check for output digits that have become fixed
        while (true) {
            var i1 = (b * modOut).Floor();
            var i2 = ((b + d) * modOut).Floor(); //note: ideally b+d-epsilon, but another iteration makes that correction unnecessary
            if (i1 != i2) break; //digit became fixed?
            //fix the next output digit (rescale the range to make next digit range from 0 to 1)
            d *= modOut;
            b *= modOut;
            b -= i1;
            yield return i1;
        }
    }
}
Craig Gidney
la source
"but that is unlikely in the extreme" -- the probability for this event is 0; we say it "almost surely" does not happen.
Raphael
2

Generate a binary decimal. Instead of storing it explicitly, just keep track of the min and max possible values. Once those values lie within the same integer, return that integer. Sketch of the code is below.

(Edit) Fuller explanation: Say you want to generate a random integer from 1 to 3 inclusive with 1/3 probability each. We do this by generating a random binary decimal real, x in the range (0, 1). If x < 1/3, return 1, else if x < 2/3 return 2, else return 3. Instead of generating the digits for x explicitly, we just keep track of the minimum and maximum possible values of x. Initially, the min value of x is 0 and the max is 1. If you flip heads first then the first digit of x behind the decimal point (in binary) is 1. The minimum possible value of x (in binary) then becomes 0.100000 = 1/2 and the max is 0.111111111 = 1. Now if your next flip is tails x starts with 0.10. The min possible value is 0.1000000 = 1/2 and the max is 0.1011111 = 3/4. The min possible value of x is 1/2 so you know there's no chance of returning 1 since that requires x < 1/3. You can still return 2 if x ends up being 1/2 < x < 2/3 or 3 if 2/3 < x < 3/4. Now suppose the third flip is tails. Then x must start with 0.100. Min = 0.10000000 = 1/2 and max = 0.100111111 = 5/8. Now since 1/3 < 1/2 < 5/8 < 2/3 we know that x must fall in the interval (1/3, 2/3), so we can stop generating digits of x and just return 2.

The code does essentially this except instead of generating x between 0 and 1 it generates x between a and b, but the principle is the same.

def gen(a, b):
  min_possible = a
  max_possible = b

  while True:
    floor_min_possible = floor(min_possible)
    floor_max_possible = floor(max_possible)
    if max_possible.is_integer():
      floor_max_possible -= 1

    if floor_max_possible == floor_min_possible:
      return floor_max_possible

    mid = (min_possible + max_possible)/2
    if coin_flip():
      min_possible = mid
    else:
      max_possible = mid

Remarque: J'ai testé ce code par rapport à la méthode d'acceptation / rejet et les deux produisent des distributions uniformes. Ce code nécessite moins de lancers de pièces que d'accepter le rejet, sauf lorsque b - a est proche de la puissance suivante de 2. Ex si vous voulez générer a = 0, b = 62, alors accepter / rejeter fait mieux. J'ai pu prouver que ce code ne peut utiliser en moyenne pas plus de 2 tours de pièces de plus qu'accepter / rejeter. D'après ma lecture, il semble que Knuth et Yao (1976) aient donné une méthode pour résoudre ce problème et prouvé que leur méthode est optimale dans le nombre attendu de lancers de pièces. Ils ont en outre prouvé que le nombre attendu de flips doit être supérieur à l'entropie de Shannon de la distribution. Je n'ai pas pu trouver une copie du texte du papier cependant et serais curieux de voir quelle est leur méthode. (Mise à jour: vient de trouver une exposition de Knuth Yao 1976 ici:http://www.nrbook.com/devroye/Devroye_files/chapter_fifteen_1.pdf but I have not yet read it). Someone also mentioned Han Hoshi in this thread which seems to be more general and solves it using a biased coin. See also http://paper.ijcsns.org/07_book/200909/20090930.pdf by Pae (2009) for a good discussion of the literature.

Brett
la source
1

The simple answer?

If ba is not a power of 2, then after generating r, check whether it is in-range, and if not, generate it again.

The most likely time you will have to re-generate r is when ba=2n+1 for some integer n, but even then it will have a greater than 50% chance of falling within range on each generation.

Mark Peters
la source
This doesn't seem complete.
Dave Clarke
1

This is a proposed solution for the case when b - a does not equal 2^k. It is supposed to work in fixed number of steps (no need to throw away candidates which are outside your expected range).

However, I am not sure this correct. Please critique and help describe the exact non-uniformity in this random number generator (if any), and how to measure/quantify it.

Firstly convert to equivalent problem of generating uniformly distributed random numbers in range [0, z-1] where z = b - a.

Also, let m = 2^k be the smallest power of 2 >= z.

As per the solution above, we already have a uniformly distributed random number generator R(m) in range [0,m-1] (can be done by tossing k coins, one for each bit).

    Keep a random seed s and initialize with s = R(m).   

    function random [0, z-1] :
        x = R(m) + s 
        while x >= z:
            x -= z
        s = x
        return x

The while loop runs at most 3 times, giving next random number in fixed number of steps (best case = worst case).

See a test program for numbers [0,2] here: http://pastebin.com/zuDD2V6H

vpathak
la source
This is not uniform. Take z=3. You get m=4 and the probabilities are 1/2,1/4,1/4.
Yuval Filmus
Please have a look at the pseudo code as well as the linked code more closely. It does emit 0, 1, and 2 almost with equal frequency...
vpathak
You're right, when you look at the outputs individually, they are uniform (the stationary probability is uniform). But they're not independent. If you have just output 0, say, then the next output is zero with probability 1/2 and one or two with probability 1/4 each.
Yuval Filmus
You can replace the entire function with a single line: return s = (s + R(m)) % z;
Yuval Filmus
1

Theoretically optimal algorithm

Here is an improvement of the other answer I posted. The other answer has the advantage that it is easier to extend to the more general case of generating one discrete distribution from another. In fact, the other answer is a special case of the algorithm due to Han and Hoshi.

The algorithm I will describe here is based on Knuth and Yao (1976). In their paper, they also proved that this algorithm achieves the minimum possible expected number of coin flips.

To illustrate it, consider the Rejection sampling method described by other answers. As an example, suppose you want to generate one of 5 numbers uniformly [0, 4]. The next power of 2 is 8 so you flip the coin 3 times and generate a random number up to 8. If the number is 0 to 4 then you return it. Otherwise, you throw it out and generate another number up to 8 and try again until you succeed. But when you throw out the number, you just wasted some entropy. You can instead condition on the number you threw out to reduce the number of future coin flips you'll need in expectation. Concretely, once you generate the number [0, 7], if it is [0, 4], return. Otherwise, it's 5, 6, or 7, and you do something different in each case. If it's 5, flip the coin again and return either 0 or 1 based on the flip. If it's 6, flip the coin and return either 2 or 3. If it's 7, flip the coin; if it's heads, return 4, if it's tails start over.

The leftover entropy from our initial failed attempt gave us 3 cases (5, 6, or 7). If we just throw this out, we throw away log2(3) coin flips. We instead keep it, and combine it with the outcome of another flip to generate 6 possible cases (5H, 5T, 6H, 6T, 7H, 7T) which let's us immediately try again to generate a final answer with probability of success 5/6.

Here is the code:

# returns an int from [0, b)
def __gen(b):
  rand_num = 0
  num_choices = 1

  while True:
    num_choices *= 2
    rand_num *= 2
    if coin.flip():
      rand_num += 1

    if num_choices >= b:
      if rand_num < b:
        return rand_num
      num_choices -= b
      rand_num -= b

# returns an int from [a, b)
def gen(a, b):
  return a + __gen(b - a)
Brett
la source