Mescolamento casuale#1584

Mescolamento casuale (Editoriale)

Inizialmente abbiamo l'array \{1, 2, ..., n\}. Successivamente viene eseguita la procedura di mescolamento. Ci viene data la permutazione finale . Dobbiamo trovare la probabilità che sia proprio questa quella che otteniamo dopo l'esecuzione della procedura.

La probabilità cercata è uguale a:

\frac{numero~di~modi~per~ottenere~A}{numero~totale~di~modi~di~mescolamento}

Ad ogni passo del mescolamento viene scelto un numero . Il numero di varianti per la scelta del numero è uguale a . Anche il numero di passi del mescolamento è . Quindi esistono n^n sequenze di scelte. Notiamo che sequenze di scelte diverse possono portare alla stessa permutazione finale. Questo perché n^n è molto più grande di .

Inseriamo nell'array la sequenza \{1, 2, ..., n\}. Esamineremo tutte le possibili varianti di scelta (r_1, r_2, ..., r_n), modellando in tutti i possibili processi di mescolamento (1 \le r_i \le n). Notiamo che una sequenza fissata di numeri r_1, r_2, ..., r_n determina univocamente quali scambi verranno eseguiti ad ogni passo, e quindi anche l'array finale dopo il completamento della procedura. Se dopo l'applicazione di tutti gli scambi abbiamo ottenuto l'array , consideriamo il tentativo riuscito. Supponiamo che tali tentativi risultino essere . Allora la probabilità cercata è uguale a

s / n^n

Implementiamo la funzione shuffle, che effettua l'enumerazione ricorsiva di tutte le possibili continuazioni del processo di mescolamento e conta quante di esse portano alla permutazione obiettivo .

La funzione ha un parametro — il numero dell'iterazione corrente (parte da ). Questo significa che i primi scambi sono già stati eseguiti, e l'array memorizza lo stato ottenuto dopo queste operazioni.

Per rientrare nel limite di tempo, applichiamo il seguente taglio (pruning). Supponiamo che si stia eseguendo l'iterazione con numero , e l'array corrente differisca dall'array obiettivo in posizioni. Rimangono da eseguire n - pos scambi. Notiamo che con un solo scambio si possono sistemare al massimo due elementi nelle loro posizioni corrette. Di conseguenza, in totale nei passi rimanenti si possono correggere al massimo 2 \cdot (n - pos) posizioni.

Se invece diff > 2 \cdot (n - pos), allora ottenere da l'array è già impossibile. In questo caso l'ulteriore enumerazione non ha senso, e il ramo corrente della ricorsione può essere interrotto immediatamente.

Implementazione dell'algoritmo

Dichiariamo gli array di lavoro:

int a[10], cur[10];

La funzione shuffle esamina ricorsivamente tutte le possibili continuazioni del processo di mescolamento, contando quante di esse porteranno alla permutazione obiettivo .

void shuffle(int pos) 
{

Sono state eseguite tutte le operazioni. Resta da verificare — se l'array ottenuto coincide con quello richiesto . Se coincide, significa che abbiamo trovato un altro scenario riuscito. In questo caso incrementiamo .

  if (pos == n) 
  {
    for (int i = 0; i < n; i++)
      if (cur[i] != a[i]) return;
    countGood++;
    return;
  }

Nella variabile contiamo in quante posizioni l'array corrente differisce da quello richiesto .

  int diff = 0;
  for (int i = 0; i < n; i++)
    if (cur[i] != a[i]) diff++;

Al momento ci restano da eseguire n - pos passi. Con un solo scambio si possono correggere al massimo due posizioni errate. Se già a questo punto le differenze sono maggiori, allora proseguire con la ricorsione non ha senso.

Semplicemente smettiamo di considerare questo ramo dell'enumerazione.

  if (diff > 2 * (n - pos)) return;

Esaminiamo tutti i possibili valori di .

  for (int r = 0; r < n; r++) 
  {
    swap(cur[pos], cur[r]);
    shuffle(pos + 1);
    swap(cur[pos], cur[r]);
  }
}

Parte principale del programma. Leggiamo il numero — la dimensione dell'array .

while (scanf("%d", &n) == 1)
{

Leggiamo l'array di input . La modellazione inizia sempre con l'array \{1, 2, ..., n\}. Lo inseriamo in .

  for (int i = 0; i < n; i++)
  {
    scanf("%d", &a[i]);
    cur[i] = i + 1;
  }

Avviamo l'enumerazione. Nella variabile contiamo quante sequenze di scelte casuali porteranno all'array .

  countGood = 0;
  shuffle(0);

Calcoliamo e stampiamo la risposta.

  res = countGood / pow(n, n);
  printf("%.6lf\n", res);
}