Czy istnieje skuteczny sposób na wygenerowanie losowej kombinacji N liczb całkowitych, która:
- każda liczba całkowita znajduje się w przedziale [
min
,max
], - liczby całkowite mają sumę
sum
, - liczby całkowite mogą występować w dowolnej kolejności (np. losowej), oraz
- kombinacja jest wybierana równomiernie ze wszystkich kombinacji, które spełniają inne wymagania?
Czy istnieje podobny algorytm dla kombinacji losowych, w którym liczby całkowite muszą występować w posortowanej kolejności według ich wartości (a nie w dowolnej kolejności)?
(Wybranie odpowiedniej kombinacji ze średnią mean
to szczególny przypadek, jeśli sum = N * mean
. Ten problem jest równoważny z wygenerowaniem jednolitego losowego podziału sum
na N części, z których każda jest w przedziale [ min
, max
] i pojawia się w dowolnej kolejności lub w uporządkowanej kolejności według ich wartości, zależnie od przypadku).
Wiem, że ten problem można rozwiązać w następujący sposób dla kombinacji, które pojawiają się w losowej kolejności (EDYCJA [27 kwietnia]: Zmodyfikowany algorytm.):
Jeśli
N * max < sum
lubN * min > sum
, nie ma rozwiązania.Jeśli
N * max == sum
istnieje tylko jedno rozwiązanie, w którym wszystkieN
liczby są równemax
. JeśliN * min == sum
istnieje tylko jedno rozwiązanie, w którym wszystkieN
liczby są równemin
.Użyj algorytmu podanego w Smith and Tromble („Sampling from the Unit Simplex”, 2004), aby wygenerować N losowych liczb całkowitych nieujemnych z sumą
sum - N * min
.Dodaj
min
do każdego numeru wygenerowanego w ten sposób.Jeśli dowolna liczba jest większa niż
max
, przejdź do kroku 3.
Jednak ten algorytm jest wolny, jeśli max
jest znacznie mniejszy niż sum
. Na przykład, zgodnie z moimi testami (z implementacją powyższego przypadku specjalnego mean
), algorytm średnio odrzuca -
- około 1,6 próbek, jeśli
N = 7, min = 3, max = 10, sum = 42
, ale - około 30,6 próbek, jeśli
N = 20, min = 3, max = 10, sum = 120
.
Czy istnieje sposób zmodyfikowania tego algorytmu, aby był skuteczny w przypadku dużych N, a jednocześnie spełniał powyższe wymagania?
EDYTOWAĆ:
Alternatywą zasugerowaną w komentarzach jest skuteczny sposób tworzenia prawidłowej kombinacji losowej (spełniającej wszystkie wymagania oprócz ostatniego):
- Oblicz
X
, liczba poprawnych kombinacji możliwe biorąc pod uwagęsum
,min
imax
. - Wybierz
Y
jednolitą losową liczbę całkowitą w[0, X)
. - Konwertuj („unrank”)
Y
na prawidłową kombinację.
Czy istnieje jednak wzór do obliczania liczby prawidłowych kombinacji (lub permutacji) i czy istnieje sposób na konwersję liczby całkowitej na prawidłową kombinację? [EDYCJA (28 kwietnia): To samo dla permutacji niż kombinacji].
EDYCJA (27 kwietnia):
Po przeczytaniu Devroye's Non-Uniform Random Variate Generation (1986), mogę potwierdzić, że jest to problem z generowaniem losowej partycji. Również Ćwiczenie 2 (szczególnie część E) na stronie 661 jest istotne dla tego pytania.
EDYCJA (28 kwietnia):
Jak się okazało, algorytm, który podałem, jest jednolity, gdzie liczby całkowite są podawane w kolejności losowej , w przeciwieństwie do sortowania według ich wartości . Ponieważ oba problemy są przedmiotem ogólnego zainteresowania, zmodyfikowałem to pytanie, aby uzyskać kanoniczną odpowiedź na oba problemy.
Poniższego kodu Ruby można użyć do zweryfikowania potencjalnych rozwiązań dla jednolitości (gdzie algorithm(...)
jest algorytm kandydujący):
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
EDYCJA (29 kwietnia): Ponownie dodano kod Ruby bieżącej implementacji.
Poniższy przykład kodu podano w języku Ruby, ale moje pytanie jest niezależne od języka programowania:
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
}
źródło
sum
iN
mają praktycznie nieograniczony (w granicach rozsądku). Szukam kanonicznej odpowiedzi, ponieważ podstawowy problem pojawia się w wielu pytaniach dotyczących przepełnienia stosu, w tym tym i tym . @ גלעדברקןOdpowiedzi:
Oto moje rozwiązanie w Javie. Jest w pełni funkcjonalny i zawiera dwa generatory:
PermutationPartitionGenerator
dla nieposortowanych partycji iCombinationPartitionGenerator
dla posortowanych partycji. Twój generator zaimplementowano również w klasieSmithTromblePartitionGenerator
do porównania. KlasaSequentialEnumerator
wylicza wszystkie możliwe partycje (nieposortowane lub posortowane, w zależności od parametru) w kolejności sekwencyjnej. Dodałem dokładne testy (w tym przypadki testowe) dla wszystkich tych generatorów. W większości przypadków implementacja jest łatwa do wyjaśnienia. Jeśli masz jakieś pytania, odpowiem na nie za kilka dni.Możesz to wypróbować na Ideone .
źródło
Oto algorytm z PermutationPartitionGenerator Johna McClane'a, w innej odpowiedzi na tej stronie. Ma dwie fazy, mianowicie fazę konfiguracji i fazę próbkowania i generuje
n
liczby losowe w [min
,max
] z sumąsum
, gdzie liczby są wymienione w kolejności losowej.Faza instalacji: Po pierwsze, tabela rozwiązań jest budowana przy użyciu następujących wzorów (
t(y, x)
gdziey
jest w [0,n
] ix
jest w [0,sum - n * min
]):Tutaj t (y, x) przechowuje względne prawdopodobieństwo, że suma
y
liczb (w odpowiednim zakresie) będzie równax
. Prawdopodobieństwo to odnosi się do wszystkich t (y, x) z tym samymy
.Faza próbkowania: Tutaj generujemy próbkę
n
liczb. Ustaws
sięsum - n * min
, a następnie dla każdej pozycjii
, poczynającn - 1
i cofając się do 0:v
na losową liczbę całkowitą w [0, t (i + 1, s)).r
namin
.v
.v
pozostaje 0 lub więcej, odejmij t (i, s-1) odv
, dodaj 1 dor
i odejmij 1 ods
.i
w próbce jest ustawiona nar
.EDYTOWAĆ:
Wygląda na to, że przy trywialnych zmianach w powyższym algorytmie możliwe jest, aby każda liczba losowa używała osobnego zakresu zamiast używać tego samego zakresu dla wszystkich:
Każda liczba losowa na pozycjach
i
∈ [0,n
) ma minimalną wartość min (i) i maksymalną wartość max (i).Niech
adjsum
=sum
- Σmin (i).Faza konfiguracji: Po pierwsze, tabela rozwiązań jest budowana przy użyciu następujących wzorów (
t(y, x)
gdziey
jest w [0,n
] ix
jest w [0,adjsum
]):Faza próbkowania jest wtedy dokładnie taka sama jak poprzednio, z wyjątkiem tego, że ustawiliśmy
s
naadjsum
(raczej niżsum - n * min
) i ustawiliśmyr
na min (i) (zamiastmin
).EDYTOWAĆ:
W przypadku CombinationPartitionGenerator Johna McClane'a etapy konfiguracji i próbkowania są następujące.
Faza konfiguracji: Po pierwsze, tabela rozwiązań jest budowana przy użyciu następujących wzorów (
t(z, y, x)
gdziez
jest w [0,n
],y
jest w [0,max - min
] ix
jest w [0,sum - n * min
]):Faza próbkowania: Tutaj generujemy próbkę
n
liczb. Ustaws
nasum - n * min
imrange
domax - min
, a następnie dla każdej pozycjii
, zaczynając odn - 1
i cofając się do 0:v
na losową liczbę całkowitą w [0, t (i + 1, mrange, s)).mrange
na min (mrange
,s
)mrange
ods
.r
namin + mrange
.i
,mrange
,s
) zv
.v
pozostałości 0 lub więcej, dodanie 1 dos
odjąć od 1r
i 1 zmrange
, a następnie odjąć t (i
,mrange
,s
) zv
.i
w próbce jest ustawiona nar
.źródło
Nie testowałem tego, więc nie jest to tak naprawdę odpowiedź, po prostu coś, co jest zbyt długie, aby zmieściło się w komentarzu. Zacznij od tablicy, która spełnia pierwsze dwa kryteria, i baw się nią, aby nadal spełniała pierwsze dwa, ale jest o wiele bardziej losowa.
Jeśli średnia jest liczbą całkowitą, początkowa tablica może wynosić [4, 4, 4, ... 4] lub może [3, 4, 5, 3, 4, 5, ... 5, 8, 0] lub coś takiego prostego. Dla średniej 4,5 spróbuj [4, 5, 4, 5, ... 4, 5].
Następnie wybierz parę liczb
num1
inum2
, w tablicy. Prawdopodobnie pierwszą liczbę należy wybrać w kolejności, ponieważ w przypadku losowania Fisher-Yates drugą liczbę należy wybierać losowo. Biorąc pierwszy numer w kolejności, każdy numer jest wybierany co najmniej raz.Teraz obliczyć
max-num1
inum2-min
. Są to odległości od dwóch liczb do granicmax
imin
. Ustawlimit
na mniejszą z dwóch odległości. Jest to maksymalna dozwolona zmiana, która nie spowoduje, że jedna lub druga liczba przekroczy dopuszczalne limity. Jeślilimit
wynosi zero, pomiń tę parę.Wybierz losową liczbę całkowitą z zakresu [1,
limit
]: zadzwońchange
. Pomijam 0 z zakresu możliwych do odebrania, ponieważ nie ma to wpływu. Testowanie może wykazać, że można uzyskać lepszą losowość, włączając ją; Nie jestem pewny.Teraz ustaw
num1 <- num1 + change
inum2 <- num2 - change
. Nie wpłynie to na średnią wartość, a wszystkie elementy tablicy nadal mieszczą się w wymaganych granicach.Musisz przeszukać całą tablicę przynajmniej raz. Testowanie powinno pokazać, czy musisz przejść przez niego więcej niż raz, aby uzyskać coś wystarczająco losowego.
ETA: dołącz pseudokod
źródło
Jak wskazuje PO, zdolność do efektywnego unrankowania jest bardzo potężna. Jeśli jesteśmy w stanie to zrobić, wygenerowanie jednolitej dystrybucji partycji można wykonać w dwóch krokach:
[1, M]
którychM
jest całkowita liczba partycji.Poniżej skupiamy się tylko na wygenerowaniu n- tej partycji, ponieważ istnieje duża ilość informacji na temat generowania jednolitego rozkładu liczb całkowitych w danym zakresie. Oto prosty
C++
algorytm, który powinien być łatwy do przetłumaczenia na inne języki.Funkcję konia roboczego
pCount
zapewnia:Ta funkcja oparta jest na doskonałej odpowiedzi na pytanie Czy istnieje skuteczny algorytm partycjonowania liczb całkowitych z ograniczoną liczbą części? przez użytkownika @ m69_snarky_and_unwelcoming. Powyższy jest niewielką modyfikacją prostego algorytmu (ten bez zapamiętywania). Można to łatwo zmodyfikować w celu włączenia zapamiętywania dla większej wydajności.
Oto demo ideone z przykładem podanym przez OP. Możemy wygenerować 100 th leksykograficznej partycji gdzie
min = 3
,max = 10
,n = 7
, isum = 42
.Oto demo ideone, które generuje pierwsze i ostatnie 10 partycji tego samego przykładu.
Wyjaśnienie
wkrótce
źródło