2011-12-22 17 views
28

I miei amici mi hanno invitato a casa a giocare a Secret Santa, dove dovremmo disegnare molto & nel ruolo di "Babbo Natale" per un amico del gruppo.Secret Santa - Generazione di permutazioni "valide"

Quindi, scriviamo tutti i nostri nomi e selezioniamo un nome a caso. Se qualcuno di noi finisce per avere il proprio nome scelto, quindi rimescoliamo e selezioniamo i nomi da capo (la logica è che uno non può essere il proprio Babbo Natale).

Ci sono sette di noi mentre suoniamo, quindi ho pensato alla definitiva "allocazione di Babbo Natale" come una permutazione di (1: 7) su se stessa, con alcune restrizioni.

Vorrei invitare diverse idee su come potremmo utilizzare Mathematica, in particolare, o qualsiasi altro linguaggio di programmazione o anche un algoritmo per:

  • List/stampare tutti i Santa-allocazioni 'valido'
  • è scalabile come il numero di amici che giocano 'Secret Santa' cresce
+2

perdona l'ignoranza, ma non risolve solo a 7! ? Numero di possibilità che è. Non il contenuto esatto di quelli. – Sheriff

+3

@Sheriff No, non è così. Sta chiedendo le permutazioni che non lasciano alcun elemento al suo posto. Per tre elementi, (123) (132) (321) (213) sono respinti, (231) e (312) sono a posto. – Szabolcs

+1

@ Sheriff, sì, davvero molto. n!sarà il numero totale di permutazioni, ma alcune di esse saranno "non valide" e dovranno essere prese in considerazione. La semplice regola è che se la persona 'i' sceglie 'i', allora questa 'permutazione' non è valida. Se 1,2,3, .. n sono persone & P (1), P (2) .. P (n) sono gli slot che selezionano, quindi per ogni 1 <= i <= n, non dovrei essere uguale a P (i). So che questa è una condizione abbastanza semplice, ma sono curioso di imparare i vari 'idiomi' che possono essere 'programmati', diciamo in Mathematica ... e vediamo se possiamo trovare qualche interessante semplificazione/modello ... – fritz

risposta

15

propongo questo:

f[s_List] := Pick[#, Inner[SameQ, #, s, Nor]] & @ [email protected] 

f @ Range @ 4 
{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2}, 
{3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}}

Questo è significativamente più veloce rispetto la funzione di Heike.

f @ Range @ 9; //Timing 
secretSanta[9]; //Timing 
{0.483, Null}
{1.482, Null}

Ignorando la trasparenza del codice, questo può essere reso ancora più volte più veloce:

f2[n_Integer] := With[{s = [email protected]}, 
    # ~Extract~ 
     SparseArray[[email protected]@BitXor[s, #] & /@ #]["NonzeroPositions"] & @ [email protected] 
    ] 

f2[9]; //Timing 
{0.162, Null}
+1

(1) Ho avuto la sensazione che SparseArray potesse essere usato per velocizzare questo. Bel lavoro. (2) Per quello che vale, appare (appare) una funzione incorporata che può fornire automaticamente il * numero * di 'squilibri', ma non i 'disallinamenti' effettivi. Vedi la funzione 'Subfactorial'. – telefunkenvf14

+2

Grazie a queste 2 "gemme" @ Mr.Wizard, anch'io ho adorato il tuo uso di SparseArray-- Ho davvero imparato molto, grazie a questo gioco! :) Buone vacanze e un nuovo anno pieno di meraviglie per TUTTI ..! – fritz

13

In Mathematica si potrebbe fare qualcosa di simile

secretSanta[n_] := 
    DeleteCases[Permutations[Range[n]], a_ /; Count[a - Range[n], 0] > 0] 

dove n è il numero di persone nel pool. Poi per esempio secretSanta[4] rendimenti

{{2, 1, 4, 3}, {2, 3, 4, 1}, {2, 4, 1, 3}, {3, 1, 4, 2}, {3, 4, 1, 2}, 
    {3, 4, 2, 1}, {4, 1, 2, 3}, {4, 3, 1, 2}, {4, 3, 2, 1}} 

Modifica

Sembra che il pacchetto Combinatorica in Mathematica in realtà ha una funzione Derangements, così si potrebbe anche fare qualcosa di simile

Needs["Combinatorica`"] 
Derangements[Range[n]] 

anche se sul mio sistema Derangements[Range[n]] è circa un fattore 2 più lento della funzione precedente.

+1

La tua funzione può essere scritta in modo più conciso: 'secretSanta [n_]: = Cases [Permutations @ Range @ n, a_ /; FreeQ [a - Range [n], 0]] ' –

29

Quello che stai cercando è chiamato derangement (un'altra bella parola latina da sapere, come dissanguamento e defenestrazione).

La frazione di tutte le permutazioni che sono disallineamenti si avvicina 1/e = circa 36,8% - quindi se si generano permutazioni casuali, basta continuare a generarle, e c'è un'alta probabilità che ne trovi una entro 5 o 10 selezioni di una permutazione casuale. (10,1% di probabilità di non trovarne uno entro 5 permutazioni casuali, ogni 5 permutazioni addizionali riduce la possibilità di non trovare un disguido da un altro fattore di 10)

This presentation è piuttosto down-to-earth e fornisce un algoritmo ricorsivo per generare direttamente le derangements, piuttosto che dover rifiutare le permutazioni che non sono squilibri.

+2

+1 per dare la parola chiave Non ho potuto Google su: derangement! – Szabolcs

+2

In effetti, questa è stata una piacevole introduzione per la comunità dello stack overflow ...! Non ho mai pensato che esistesse un termine speciale per un tale squilibrato, e sciocco (come forse sentivano i miei amici ?!) idea che ero deciso a rincorrere ... Grazie a una tonnellata per l'aiuto immediato ..! – fritz

+0

@fritz Benvenuti in StackOverflow e non dimenticate di accettare una risposta alla domanda (se ce n'è una adatta!) :-) – Szabolcs

15

Una permutazione che non associa alcun elemento a se stesso è un derangement. Con l'aumentare di n, la frazione di squilibri si avvicina alla costante 1/e. In quanto tale, richiede (in media) e cerca di ottenere un disguido, se si sceglie una permutazione a caso.

L'articolo di wikipedia include espressioni per il calcolo di valori espliciti per il piccolo n.

+1

Grazie mille per questa informazione ..! Sebbene sembrasse essere un banale "filtraggio" di alcune permutazioni dal totale n! accordi, ho avuto intuito che questo dovrebbe avere un po 'di schema in esso ...! :) Proverò a implementare alcuni modi per "enumerare" i disordini in Mathematica ed esplorare ..! Grazie mille ancora ..! – fritz

+0

@wnoise - Fai notare che mentre n aumenta, "... la frazione di squilibri si avvicina alla costante 1/e". Ciò mi ricorda una classe generale di problemi di arresto/ricerca ottimali denominati "problemi di segreteria", in cui si verifica lo stesso risultato 1/e. Se familiare, puoi commentare la relazione tra squilibri e il "problema segretario"? (Penso che questa sarebbe una buona domanda da porre formalmente da qualche parte nell'universo dello stack, ma probabilmente non su SO. Sentiti libero di raccogliere l'idea della domanda se ne valga la pena e se rispondere qui sarebbe una perdita di tempo.) – telefunkenvf14

+0

@ telefunkenvf14: I Non ho mai sentito parlare di "problemi di segreteria", quindi non ho potuto commentare. – wnoise

1

mi sono imbattuto il built-in Subfactorial funzione nella documentazione e modificato uno degli esempi da produrre:

Remove[teleSecretSanta]; 
teleSecretSanta[dims_Integer] := 
With[{spec = Range[dims]}, 
    With[{ 
    perms = Permutations[spec], 
    casesToDelete = DiagonalMatrix[spec] /. {0 -> _}}, 
    DeleteCases[perms, Alternatives @@ casesToDelete] 
    ] 
    ] 

È possibile utilizzare Subfactorial per verificare la funzione.

Length[teleSecretSanta[4]] == Subfactorial[4] 

Come nella risposta di Mr.Wizard, ho il sospetto teleSecretSanta può essere ottimizzato tramite SparseArray. Tuttavia, sono troppo ubriaco al momento per tentare questi imbrogli. (scherzando ... sono davvero troppo pigro e stupido.)

2

Questo non risponde alla tua domanda sul conteggio dei disallineamenti validi, ma fornisce un algoritmo per generarne uno (che potrebbe essere quello che vuoi) con il seguente proprietà:

  1. si garantisce che ci sia un unico ciclo in rapporto di Babbo Natale (se si gioca in 4, non si finisce con 2 Babbo coppia -> 2 cicli),
  2. funziona in modo efficiente anche con numero molto elevato di lettori,
  3. se applicato correttamente, nessuno sa di chi sia Babbo Natale,
  4. non ha bisogno di un computer, solo della carta.

Ecco l'algoritmo:

  • Ogni giocatore scrive la sua/il suo nome su una busta e mette la sua/il suo nome in un foglio piegato nella busta.
  • Un giocatore fidato (per la proprietà # 3 sopra) prende tutte le buste e le rimescola guardando sul retro (dove non è scritto alcun nome).
  • Una volta che le buste vengono mescolate abbastanza bene, guardando sempre sul retro, il giocatore fidato sposta la carta in ogni busta alla successiva.
  • Dopo aver mescolato di nuovo le buste, le buste vengono ridistribuite sul giocatore il cui nome è sopra di esse, e ogni giocatore è il Babbo Natale della persona il cui nome è nella busta.
Problemi correlati