Existe-t-il un moyen efficace de générer une combinaison aléatoire de N entiers telle que:
min
, max
],sum
,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é.):
Si N * max < sum
Ou N * min > sum
, Il n'y a pas de solution.
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
.
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
.
Ajoutez min
à chaque nombre généré de cette façon.
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 -
N = 7, min = 3, max = 10, sum = 42
, maisN = 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:
X
, le nombre de combinaisons valides possibles étant donné sum
, min
et max
.Y
, un entier aléatoire uniforme dans [0, X)
.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
}
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.