c# - Sudoku validity check algorithm: ¿cómo funciona este código?
.net (6)
Comprueba si los valores de la matriz son únicos. Para hacer esto, crea un entero - bandera - y establece bits en la bandera de acuerdo a los valores en la matriz de valores. Comprueba si un bit en particular ya está establecido; si lo es, entonces hay un duplicado y falla. De lo contrario, establece el bit.
Aquí hay un desglose:
public static bool IsValid(int[] values) {
int flag = 0; // <-- Initialize your flags; all of them are set to 0000
foreach (int value in values) { // <-- Loop through the values
if (value != 0) { // <-- Ignore values of 0
int bit = 1 << value; // <-- Left-shift 1 by the current value
// Say for example, the current value is 4, this will shift the bit in the value of 1
// 4 places to the left. So if the 1 looks like 000001 internally, after shifting 4 to the
// left, it will look like 010000; this is how we choose a specific bit to set/inspect
if ((flag & bit) != 0) return false; // <-- Compare the bit at the
// position specified by bit with the corresponding position in flag. If both are 1 then
// & will return a value greater than 0; if either is not 1, then & will return 0. E.g.
// if flag = 01000 and bit = 01000, then the result will be 01000. If flag = 01000 and
//bit = 00010 then the result will be 0; this is how we check to see if the bit
// is already set. If it is, then we''ve already seen this value, so return false, i.e. not
// a valid solution
flag |= bit; // <-- We haven''t seen this value before, so set the
// corresponding bit in the flag to say we''ve seen it now. e.g. if flag = 1000
// and bit = 0100, after this operation, flag = 1100
}
}
return true; // <-- If we get this far, all values were unique, so it''s a valid
// answer.
}
Estaba leyendo una pregunta publicada aquí: algoritmo de Sudoku en C #
Y una de las soluciones publicadas fue esta pieza de código.
public static bool IsValid(int[] values) {
int flag = 0;
foreach (int value in values) {
if (value != 0) {
int bit = 1 << value;
if ((flag & bit) != 0) return false;
flag |= bit;
}
}
return true;
}
La idea es que detectará duplicados en la matriz de valores; pero estoy abrumado por lo mucho que no sé. ¿Puede alguien explicarme esto?
EDIT: Gracias a todos. Tantas respuestas geniales, no sé cómo seleccionar una. Ahora tiene perfecto sentido.
Interesante. Almacena los números que ya encontró al establecer ese bit en el entero de bandera. Ejemplo:
- Encontró un 4
- Desplace luego el número 1 por 4 bits, lo que da como resultado la matriz de bits 00010000b
- O bien, en el flag-int (que antes era 0), el flag-int es 00010000b
- Encontró un 2
- Desplace luego el número 1 por 2 bits, lo que da como resultado la matriz de bits 00000100b
- O bien, en la bandera-int (que anteriormente era 00010000b), la bandera-int es 00010100b
También comprueba cada número si ese bit ya está establecido en el indicador-int.
La idea es establecer el nth
bit de un número, donde n
es el valor de celda. Dado que los valores de sudoku varían de 1 a 9, todos los bits encajan dentro de un rango de 0-512. Con cada valor, verifique si el bit nth
ya está establecido, y si es así, hemos encontrado un duplicado. De lo contrario, establezca el nth
bit en nuestro número de cheque, en este caso, la flag
, para acumular los números que ya se han utilizado. Es una forma mucho más rápida de almacenar datos que una matriz.
Realmente una buena idea
Básicamente, utiliza una bandera int
(inicialmente establecida en cero) como una "matriz de bits"; para cada valor, comprueba si el bit correspondiente en el indicador está establecido, y si no lo está, lo establece.
Si, en cambio, esa posición de bit ya está establecida, sabe que ya se ha visto el valor correspondiente, por lo que la pieza de Sudoku no es válida.
Más en detalle:
public static bool IsValid(int[] values)
{
// bit field (set to zero => no values processed yet)
int flag = 0;
foreach (int value in values)
{
// value == 0 => reserved for still not filled cells, not to be processed
if (value != 0)
{
// prepares the bit mask left-shifting 1 of value positions
int bit = 1 << value;
// checks if the bit is already set, and if so the Sudoku is invalid
if ((flag & bit) != 0)
return false;
// otherwise sets the bit ("value seen")
flag |= bit;
}
}
// if we didn''t exit before, there are no problems with the given values
return true;
}
Vamos a trabajar a través de él. values = 1,2,3,2,5
Iteración 1:
bit = 1 << 1
bit = 10
if(00 & 10 != 00)
false
flag |= bit
flag = 10
Iteración 2:
bit = 1 << 2
bit = 100
if(010 & 100 != 000)
false
flag |= bit
flag = 110
Iteración 3:
bit = 1 << 3
bit = 1000
if(0110 & 1000 != 0000)
false
flag |= bit
flag = 1110
Iteración 4:
bit = 1 << 2
bit = 100
if(1110 & 0100 != 0000)
TRUE
Esto se evalúa como verdadero, lo que significa que encontramos un doble y devolvemos falso
int bit = 1 << value; //left bit shift - selects the bit that corresponds to value
if ((flag & bit) != 0) return false; //bitwise AND - checks the flag to see whether bit is already set
flag |= bit; // bitwise OR - sets the bit in the flag