Soluzione del Cubo di Rubik: metodo PBF

Il metodo PBF (Partial Brute Force) è un metodo sperimentale per il risolutore automatico.

Mentre un metodo Brute Force totale richiederebbe migliaia di anni di calcoli per trovare la soluzione minima di un singolo Cubo di Rubik, il metodo parziale è un approccio metà algoritmico e metà Brute Force in sperimentazione in tempo reale su questo sito web, il cui obiettivo è quello di ridurre il risolutore automatico a calcolare un numero di mosse più accettabile dell'attuale, anche se non minimo.

L'idea è quella di riempire delle tabelle che interrogate restituiscono il numero minimo di mosse da una posizione a un'altra del Cubo di Rubik.

Per esempio la tabella completa che è stata fin'ora realizzata è quella che dal Cubo di Rubik in qualunque posizione porta alla soluzione della Croce, usata nel metodo algoritmico a strati.

Per creare questa tabella sono stati usati diversi strumenti software che hanno consentito di scandagliare tutte le possibili sequenze di mosse fino a completare la tabella. Questi strumenti saranno discussi e pubblicati su questo sito web.

La tabella realizzata è interrogabile nel Risolutore automatico attraverso il pulsante Solve the Cross.

Considerazioni generali

Un metodo di risoluzione per il Cubo di Rubik consiste, partendo dal Cubo completamente mischiato, nel passare attraverso un insieme di configurazioni stabilite fino alla configurazione Cubo risolto. Possiamo chiamare il passaggio da una configurazione all'altra sottometodo. Per ogni sottometodo l'autore del metodo sviluppa una serie di sequenze di mosse (o algoritmi) che risolvono il sottometodo in ogni caso, cioè in qualunque modo siano posizionate le piastrelle.

In una sequenza di sottometodi contigui che costituiscano un metodo di soluzione, il primo sottometodo non ha vincoli da rispettare, ma a mano a mano che si avanza nella soluzione aumentano i vincoli, dettati dalle piastrelle che sono state definitivamente sistemate fino a giungere all'ultima configurazione di vincolo completo e quindi di Cubo risolto.

L'obiettivo da raggiungere è quello di creare per ogni sottometodo una tabella completa di tutte le possibili configurazioni di partenza, che fornisce per ognuna di esse la soluzione più breve per completare il sottometodo.

L'idea è quella di partire, per ogni sottometodo, dalla configurazione di arrivo, applicare tutte le possibili sequenze di mosse e tenere nella tabella quelle sequenze che rispettano il vincolo d'inizio del sottometodo per le quali le piastrelle interessate nel vincolo di fine abbiano posizioni non già rilevate.

Per fare questa operazione in modo efficiente abbiamo bisogno di un elenco di sequenze di mosse ordinate lessicograficamente, che sia però generabile parzialmente senza la necessità di partire dal primo elemento della lista.

Posizione lessicografica delle sequenze di mosse

Come ben sappiamo sul Cubo di Rubik è possibile evidenziare 18 tipi di mosse differenti che possono essere effettuate. È utile, per la creazione del nostro metodo, definire un algoritmo che faccia corrispondere a ogni numero intero una sequenza di mosse, in modo che le sequenze più corte vengano sempre prima di quelle più lunghe, e in modo che sia un insieme completo e il meno possibile ripetitivo.

Tutte queste caratteristiche sono necessarie per fare in modo che la corrispondenza contenga il minor numero di elementi inutili, perché sia possibile creare l'algoritmo Brute Force che provi tutte le possibili sequenze di mosse nel minor tempo possibile.

Quest'ordine lessicografico è inoltre necessario per permettere di poter calcolare in spazi e tempi separati diversi gruppi di elementi della lista, in modo da sfruttare i moderni processori multicore e la programmazione distribuita.

Come unico vincolo per far sì che molte sequenze inutili non siano presenti nell'elenco poniamo che le sequenze non devono contenere mosse ripetute due volte di seguito, o, più in generale, due mosse consecutive che interessano lo stesso oggetto.

Possiamo concludere che l'elenco che vogliamo, se costituito da 6 mosse differenti rappresentate dai numeri da 1 a 6 sia di questo tipo:

  • 1. [1]
  • 2. [2]
  • 3. [3]
  • 4. [4]
  • 5. [5]
  • 6. [6]
  • 7. [1 2]
  • 8. [1 3]
  • 9. [1 4]
  • 10. [1 5]
  • 11. [1 6]
  • 12. [2 1]
  • 13. [2 3]
  • 14. [2 4]
  • 15. [2 5]
  • 16. [2 6]
  • 17. [3 1]
  • 18. [3 2]
  • 19. [3 4]
  • 20. [3 5]
  • 21. [3 6]
  • 22. [4 1]
  • 23. [4 2]
  • 24. [4 3]
  • 25. [4 5]
  • 26. [4 6]
  • 27. [5 1]
  • 28. [5 2]
  • 29. [5 3]
  • 30. [5 4]
  • 31. [5 6]
  • 32. [6 1]
  • 33. [6 2]
  • 34. [6 3]
  • 35. [6 4]
  • 36. [6 5]
  • 37. [1 2 1]
  • 38. [1 2 3]
  • 39. [1 2 4]
  • 40. [1 2 5]
  • 41. [1 2 6]
  • 42. [1 3 1]
  • 43. [1 3 2]
  • 44. [1 3 4]
  • 45. [1 3 5]
  • 46. [1 3 6]
  • 47. [1 4 1]
  • 48. [1 4 2]
  • 49. [1 4 3]
  • 50. [1 4 5]
  • 51. [1 4 6]
  • 52. [1 5 1]
  • 53. [1 5 2]
  • 54. [1 5 3]
  • 55. [1 5 4]
  • 56. [1 5 6]
  • 57. [1 6 1]
  • 58. [1 6 2]
  • 59. [1 6 3]
  • 60. [1 6 4]
  • 61. [1 6 5]
  • 62. [2 1 2]
  • 63. [2 1 3]
  • 64. [2 1 4]
  • 65. [2 1 5]
  • 66. [2 1 6]
  • 67. [2 3 1]
  • 68. [2 3 2]
  • 69. [2 3 4]
  • 70. [2 3 5]
  • 71. [2 3 6]
  • 72. [2 4 1]
  • 73. [2 4 2]
  • 74. [2 4 3]
  • 75. [2 4 5]
  • 76. [2 4 6]
  • 77. [2 5 1]
  • 78. [2 5 2]
  • 79. [2 5 3]
  • 80. [2 5 4]
  • 81. [2 5 6]
  • 82. [2 6 1]
  • 83. [2 6 2]
  • 84. [2 6 3]
  • 85. [2 6 4]
  • 86. [2 6 5]
  • 87. [3 1 2]
  • 88. [3 1 3]
  • 89. [3 1 4]
  • 90. [3 1 5]
  • 91. [3 1 6]
  • 92. [3 2 1]
  • 93. [3 2 3]
  • 94. [3 2 4]
  • 95. [3 2 5]
  • 96. [3 2 6]
  • 97. [3 4 1]
  • 98. [3 4 2]
  • 99. [3 4 3]
  • 100. [...]

Per risolvere questo problema bisogna prima risolvere un problema più semplice, cioè calcolare l'ordine lessicografico delle sequenze di una lunghezza data m.

Il ragionamento che facciamo per risolvere questo problema è: ho 6 possibilità di scelta per il primo elemento e cinque per tutti quelli successivi, poiché non posso ripetere una mossa appena fatta.

Questo approccio è stato adottato per generare le sequenze di mosse considerando le 18 mosse possibili nel Cubo di Rubik.

Il seguente metodo Java ricorsivo prende come parametri il numero della sequenza, n, e il numero di mosse della sequenza, m.

public static int[] getSequence(long n, int m) {
    int[] sequence = new int[m];
 
    if (m == 1) {
        sequence[0] = (int)(n + 1);
    } else if (m > 1) {
        int[] subsequence = getSequence(n / 15, m - 1);
     
        for (int i = 0; i < subsequence.length; i++)
            sequence[i] = subsequence[i];
     
        int new_coeff = (int)(n % 15) + 1;
    }
}

Questo metodo non tiene conto del fatto che una mossa non può essere ripetuta ma soltanto del fatto che ci sono solo 15 possibili scelte nelle mosse successiva alla prima. Una opportuna tabella può aiutarci a trasformare le sequenze generate da questo algoritmo in sequence utili.

A questo punto risolvere il caso generale richiede soltanto l'applicazione di una correzione. Per ogni numero di sequenza sappiamo che esso slitta in avanti di x posizioni quante sono le combinazioni totali di lunghezza minore. Allo scopo può essere utile una tabella già calcolata per ogni profondità m.

Commenti

Lascia un commento »

 

Lascia un commento »