web-dev-qa-db-fra.com

Existe-t-il un moyen efficace de générer N entiers aléatoires dans une plage qui ont une somme ou une moyenne donnée?

Existe-t-il un moyen efficace de générer une combinaison aléatoire de N entiers telle que:

  • chaque entier est dans l'intervalle [min, max],
  • les entiers ont une somme de sum,
  • les entiers peuvent apparaître dans n'importe quel ordre (par exemple, ordre aléatoire), et
  • la combinaison est choisie uniformément au hasard parmi toutes les combinaisons qui satisfont aux autres exigences?

Existe-t-il un algorithme similaire pour les combinaisons aléatoires dans lequel les entiers doivent apparaître dans l'ordre trié par leurs valeurs (plutôt que dans n'importe quel ordre)?

(Choisir une combinaison appropriée avec une moyenne de mean est un cas particulier, si sum = N * mean. Ce problème équivaut à générer une partition aléatoire uniforme de sum en N parties qui sont chacun dans l'intervalle [min, max] et apparaissent dans n'importe quel ordre ou dans l'ordre trié par leurs valeurs, selon le cas.)

Je suis conscient que ce problème peut être résolu de la manière suivante pour les combinaisons qui apparaissent dans un ordre aléatoire (EDIT [27 avril]: Algorithme modifié.):

  1. Si N * max < sum Ou N * min > sum, Il n'y a pas de solution.

  2. Si N * max == sum, Il n'y a qu'une seule solution, dans laquelle tous les nombres N sont égaux à max. Si N * min == sum, Il n'y a qu'une seule solution, dans laquelle tous les nombres N sont égaux à min.

  3. tilisez l'algorithme donné dans Smith and Tromble ("Sampling from the Unit Simplex", 2004) pour générer N entiers aléatoires non négatifs avec la somme sum - N * min.

  4. Ajoutez min à chaque nombre généré de cette façon.

  5. Si un nombre est supérieur à max, passez à l'étape 3.

Cependant, cet algorithme est lent si max est bien inférieur à sum. Par exemple, d'après mes tests (avec une implémentation du cas spécial ci-dessus impliquant mean), l'algorithme rejette, en moyenne -

  • environ 1,6 échantillons si N = 7, min = 3, max = 10, sum = 42, mais
  • environ 30,6 échantillons si N = 20, min = 3, max = 10, sum = 120.

Existe-t-il un moyen de modifier cet algorithme pour qu'il soit efficace pour un grand N tout en répondant aux exigences ci-dessus?

ÉDITER:

Comme alternative suggérée dans les commentaires, un moyen efficace de produire une combinaison aléatoire valide (qui satisfait toutes les exigences sauf la dernière) est:

  1. Calculez X, le nombre de combinaisons valides possibles étant donné sum, min et max.
  2. Choisissez Y, un entier aléatoire uniforme dans [0, X).
  3. Convertit ("unank") Y en une combinaison valide.

Cependant, existe-t-il une formule pour calculer le nombre de combinaisons (ou permutations) valides, et existe-t-il un moyen de convertir un entier en une combinaison valide? [EDIT (28 avril): Idem pour les permutations plutôt que les combinaisons].

EDIT (27 avril):

Après avoir lu Devroye's Non-Uniform Random Variate Generation (1986), je peux confirmer qu'il s'agit d'un problème de génération d'un cloison. De plus, l'exercice 2 (en particulier la partie E) à la page 661 est pertinent pour cette question.

EDIT (28 avril):

Il s'est avéré que l'algorithme que j'ai donné est uniforme où les entiers impliqués sont donnés dans un ordre aléatoire , par opposition à trié par leurs valeurs . Étant donné que les deux problèmes sont d'intérêt général, j'ai modifié cette question pour rechercher une réponse canonique aux deux problèmes.

Le code suivant Ruby code peut être utilisé pour vérifier des solutions potentielles pour l'uniformité (où algorithm(...) est l'algorithme candidat):

combos={}
permus={}
mn=0
mx=6
sum=12
for x in mn..mx
  for y in mn..mx
    for z in mn..mx
      if x+y+z==sum
        permus[[x,y,z]]=0
      end
      if x+y+z==sum and x<=y and y<=z
        combos[[x,y,z]]=0
      end
    end
  end
end

3000.times {|x|
 f=algorithm(3,sum,mn,mx)
 combos[f.sort]+=1
 permus[f]+=1
}
p combos
p permus

EDIT (29 avril): Re-ajouté Ruby code de l'implémentation actuelle.

L'exemple de code suivant est donné en Ruby, mais ma question est indépendante du langage de programmation:

def posintwithsum(n, total)
    raise if n <= 0 or total <=0
    ls = [0]
    ret = []
    while ls.length < n
      c = 1+Rand(total-1)
      found = false
      for j in 1...ls.length
        if ls[j] == c
          found = true
          break
        end
      end
      if found == false;ls.Push(c);end
    end
    ls.sort!
    ls.Push(total)
    for i in 1...ls.length
       ret.Push(ls[i] - ls[i - 1])
    end
    return ret
end

def integersWithSum(n, total)
 raise if n <= 0 or total <=0
 ret = posintwithsum(n, total + n)
 for i in 0...ret.length
    ret[i] = ret[i] - 1
 end
 return ret
end

# Generate 100 valid samples
mn=3
mx=10
sum=42
n=7
100.times {
 while true
    pp=integersWithSum(n,sum-n*mn).map{|x| x+mn }
    if !pp.find{|x| x>mx }
      p pp; break # Output the sample and break
    end
 end
}

21
Peter O.

Si vous générez 0≤a≤1 des valeurs aléatoires dans la plage [l, x-1] uniformément et 1-a des valeurs aléatoires dans la plage [x, h] uniformément, la moyenne attendue serait:

m = ((l+x-1)/2)*a + ((x+h)/2)*(1-a)

Donc, si vous voulez un m spécifique, vous pouvez jouer avec a et x.

Par exemple, si vous définissez x = m: a = (h-m)/(h-l + 1).

Pour assurer une probabilité plus proche de l'uniformité pour différentes combinaisons, choisissez a ou x au hasard dans l'ensemble de solutions valides de l'équation ci-dessus. (x doit être compris entre [l, h] et doit être (proche de) un entier; N * a doit également être (proche de) un entier.

0
Lior Kogan