Computer Science Department - University of Puerto Rico
Prepared by: José R. Ortiz-Ubarri
A Latin square is an n × n array filled with n different symbols, each occurring exactly once in each row and exactly once in each column.
A | B | C |
C | A | B |
B | C | A |
A | A | C |
C | A | B |
B | C | A |
A | B | C |
C | A | B |
A | C | A |
A♠ | K♥ | Q♦ | J♣ |
Q♣ | J♦ | A♥ | K♠ |
J♥ | Q♠ | K♣ | A♦ |
K♦ | A♣ | J♠ | Q♥ |
Error correcting codes - Latin squares that are orthogonal to each other have found an application as error correcting codes in situations where communication is disturbed by various types of noise.
Mathematical puzzles - Sudoku puzzles are a special case of Latin squares; any solution to a Sudoku puzzle is a Latin square.
By hand, is very easy to find at least one Latin Square for any n.
Algebraically with finite fields.
Exhaustive computing
Instead of using an algorithm to generate all the permutations. We will use backtracking to eliminate branches of computations and speed things a little bit more.
The backtracking algorithm work as follows:
ls_list = []
while current > start:
#find next
row_s = current/n
latin_square[current] += 1
while ls[current] in ls[row_s*n:current] and ls[current] < n:
latin_square[current]+=1
if ls[current] > n:
#back track
ls[current] = 0
current-=1
elif checkLatin(ls, current,n):
if current < size-1:
current+=1
else:
ls_list.append(ls[:])
def checkLatin(ls, current, n):
row = current/n
column = current % n
for i in range(row):
if ls[i * n + column] == ls[current]:
return False
return True
We can start by computing the set of all permutations of size for the first row of the LS
Divide set in equal parts among the processes and have the complete the search.
Have one process, the master, send one sub Latin Square to the other processes, the slaves. Once the slaves finish their computation, send back the results and wait for another sub Latin Square.
For this application we will prefer to divide the set in equal parts, because each sub permutation will have the same solution spance.
if rank == 0:
perm_list = [perm for perm in itertools.permutations([i+1 for i in range(n)])]
else:
perm_list = None
perm_list = comm.bcast(perm_list, root=0)
n1, n2 = computeIndices(len(perm_list), num_procs, rank)
# Compute LS sub problems
for i in range(n1, n2):
latin_square = list(perm_list[i]) + [0] * (size - n)
ls_list += completeLatinSquare(latin_square, current, start)
if rank == 0:
for i in range(num_procs-1):
ls = comm.recv(source=MPI.ANY_SOURCE, tag=MPI.ANY_TAG, status=stat)
ls_list += ls
# Save, display the results HERE
else:
comm.send(ls_list, dest=0)
def computeIndices(size, nprocs, rank):
portion = size / nprocs
residue = size % nprocs
if rank <= residue:
n1 = rank * (portion+1)
else:
n1 = rank * portion + residue
n2 = n1 + portion
if rank < residue:
n2 += 1
return n1, n2
Wikipedia, http://en.wikipedia.org/wiki/Latin_square
MPI4py, http://mpi4py.scipy.org/
Python, www.python.org