Questa è una generalizzazione del famoso problema del compleanno : dati n=100 individui che hanno "compleanni" casuali distribuiti uniformemente in una serie di possibilità d=6 , qual è la possibilità che nessun compleanno sia condiviso da più di m=20 individui?
Un calcolo esatto produce la risposta (per raddoppiare la precisione). Traccerò la teoria e fornirò il codice per il generale n , m , d . Il tempismo asintotico del codice è O ( n 2 log ( d ) ) che lo rende adatto a un numero molto elevato di compleanni d e fornisce prestazioni ragionevoli fino a quando n è tra le migliaia. A quel punto, l'approssimazione di Poisson discussa inEstensione del paradosso del compleanno a più di 2 personedovrebbe funzionare bene nella maggior parte dei casi.0.267747907805267n,m,d.O(n2log(d))dn
Spiegazione della soluzione
La funzione generatrice di probabilità (pgf) per gli esiti di tiri indipendenti di un dado con lato d ènd
d−nfn(x1,x2,…,xd)=d−n(x1+x2+⋯+xd)n.
Il coefficiente di nell'espansione di questo multinomiale fornisce il numero di modi in cui la faccia i può apparire esattamente e i volte, i = 1 , 2 , … , d .xe11xe22⋯xeddieii=1,2,…,d.
Limitare il nostro interesse a non più di aspetti da qualsiasi faccia equivale a valutare f n modulo l'ideale che ho generato da x m + 1 1 , x m + 1 2 , … , x m + 1 d . Per eseguire questa valutazione, utilizzare il Teorema binomiale ricorsivamente per otteneremfnIXm + 11, xm + 12, ... , xm + 1d.
fn( x1, ... , xd)= ( ( x1+ ⋯ + xr)+(xr+1+xr+2+⋯+x2r))n=∑k=0n(nk)(x1+⋯+xr)k(xr+1+⋯+x2r)n -k=∑K=0n(nk)fk(x1, ... ,xr) fn - k( xr + 1, ... , x2 r)
quando è pari. Scrivendo f ( d ) n = f n ( 1 , 1 , … , 1 ) ( d termini), abbiamod= 2 rf( d)n= fn( 1 , 1 , … , 1 )d
f( 2 r )n= ∑k = 0n( nK) f( r )Kf( r )n - k.(un)
Quando è dispari, utilizzare una decomposizione analogad= 2 r + 1
fn( x1, ... , xd)= ( ( x1+⋯ + x2 r) + x2 r + 1)n=∑K= 0n( nK) fK(x1, ... , x2 r) fn - k( x2 r + 1) ,
dando
f( 2 r + 1 )n= ∑k = 0n( nK) f( 2 r )Kf( 1 )n - k.(B)
In entrambi i casi, possiamo anche ridurre tutto il modulo , che può essere facilmente eseguito a partire daio
fn( xj) ≅{ xn0n ≤ mn > mmodio,
fornendo i valori di partenza per la ricorsione,
f( 1 )n= { 10n ≤ mn > m
Ciò che rende questo efficiente è che suddividendo le variabili in due gruppi uguali di variabili r ciascuna e impostando tutti i valori delle variabili su 1 , dobbiamo valutare tutto una sola volta per un gruppo e quindi combinare i risultati. Ciò richiede il calcolo fino a n + 1 termini, ognuno dei quali necessita del calcolo O ( n ) per la combinazione. Non abbiamo nemmeno bisogno di un array 2D per memorizzare f ( r ) n , perché quando si calcola f ( d ) n , solo fdr1 ,n + 1O ( n )f( r )nf( d)n, eF ( 1 ) n sono richiesti.f( r )nf( 1 )n
Il numero totale di passaggi è uno in meno del numero di cifre nell'espansione binaria di (che conta le divisioni in gruppi uguali nella formula ( a ) ) più il numero di cifre nell'espansione (che conta tutte le volte un dispari viene rilevato un valore, che richiede l'applicazione della formula ( b ) ). Sono ancora solo i passaggi O ( log ( d ) ) .d( Un )( b )O ( log( d) )
In R
una workstation vecchia di dieci anni il lavoro è stato svolto in 0,007 secondi. Il codice è elencato alla fine di questo post. Utilizza i logaritmi delle probabilità, piuttosto che le probabilità stesse, per evitare possibili trabocchi o accumulare troppi scarichi. Questo rende possibile rimuovere il fattore nella soluzione in modo da poter calcolare i conteggi che sono alla base delle probabilità.d- n
Si noti che questa procedura comporta il calcolo dell'intera sequenza di probabilità in una volta, il che ci consente facilmente di studiare come cambiano le possibilità con n .f0, f1, ... , fnn
applicazioni
tmultinom.full
m + 1nO ( n2log( n ) registro( d) )nn
#
# The birthday problem: find the number of people where the chance of
# a collision of `m+1` birthdays first exceeds `alpha`.
#
birthday <- function(m=1, d=365, alpha=0.50) {
n <- 8
while((p <- tmultinom.full(n, m, d))[n] > alpha) n <- n * 2
return(p)
}
798birthday(7)
365
Codice
# Compute the chance that in `n` independent rolls of a `d`-sided die,
# no side appears more than `m` times.
#
tmultinom <- function(n, m, d, count=FALSE) tmultinom.full(n, m, d, count)[n+1]
#
# Compute the chances that in 0, 1, 2, ..., `n` independent rolls of a
# `d`-sided die, no side appears more than `m` times.
#
tmultinom.full <- function(n, m, d, count=FALSE) {
if (n < 0) return(numeric(0))
one <- rep(1.0, n+1); names(one) <- 0:n
if (d <= 0 || m >= n) return(one)
if(count) log.p <- 0 else log.p <- -log(d)
f <- function(n, m, d) { # The recursive solution
if (d==1) return(one) # Base case
r <- floor(d/2)
x <- double(f(n, m, r), m) # Combine two equal values
if (2*r < d) x <- combine(x, one, m) # Treat odd `d`
return(x)
}
one <- c(log.p*(0:m), rep(-Inf, n-m)) # Reduction modulo x^(m+1)
double <- function(x, m) combine(x, x, m)
combine <- function(x, y, m) { # The Binomial Theorem
z <- sapply(1:length(x), function(n) { # Need all powers 0..n
z <- x[1:n] + lchoose(n-1, 1:n-1) + y[n:1]
z.max <- max(z)
log(sum(exp(z - z.max), na.rm=TRUE)) + z.max
})
return(z)
}
x <- exp(f(n, m, d)); names(x) <- 0:n
return(x)
}
La risposta si ottiene con
print(tmultinom(100,20,6), digits=15)
,267747907805267