Algorithm to generate Diagonal Latin Square matrix - algorithm

I need for given N create N*N matrix which does not have repetitions in rows, cells, minor and major diagonals and values are 1, 2 , 3, ...., N.
For N = 4 one of matrices is the following:
1 2 3 4
3 4 1 2
4 3 2 1
2 1 4 3

Problem overview
The math structure you described is Diagonal Latin Square. Constructing them is the more mathematical problem than the algorithmic or programmatic.
To correctly understand what it is and how to create you should read following articles:
Latin squares definition
Magic squares definition
Diagonal Latin square construction <-- p.2 is answer to your question with proof and with other interesting properties
Short answer
One of the possible ways to construct Diagonal Latin Square:
Let N is the power of required matrix L.
If there are exist numbers A and B from range [0; N-1] which satisfy properties:
A relativly prime to N
B relatively prime to N
(A + B) relatively prime to N
(A - B) relatively prime to N
Then you can create required matrix with the following rule:
L[i][j] = (A * i + B * j) mod N

It would be nice to do this mathematically, but I'll propose the simplest algorithm that I can think of - brute force.
At a high level
we can represent a matrix as an array of arrays
for a given N, construct S a set of arrays, which contains every combination of [1..N]. There will be N! of these.
using an recursive & iterative selection process (e.g. a search tree), search through all orders of these arrays until one of the 'uniqueness' rules is broken
For example, in your N = 4 problem, I'd construct
S = [
[1,2,3,4], [1,2,4,3]
[1,3,2,4], [1,3,4,2]
[1,4,2,3], [1,4,3,2]
[2,1,3,4], [2,1,4,3]
[2,3,1,4], [2,3,4,1]
[2,4,1,3], [2,4,3,1]
[3,1,2,4], [3,1,4,2]
// etc
]
R = new int[4][4]
Then the algorithm is something like
If R is 'full', you're done
Evaluate does the next row from S fit into R,
if yes, insert it into R, reset the iterator on S, and go to 1.
if no, increment the iterator on S
If there are more rows to check in S, go to 2.
Else you've iterated across S and none of the rows fit, so remove the most recent row added to R and go to 1. In other words, explore another branch.
To improve the efficiency of this algorithm, implement a better data structure. Rather than a flat array of all combinations, use a prefix tree / Trie of some sort to both reduce the storage size of the 'options' and reduce the search area within each iteration.

Here's a method which is fast for N <= 9 : (python)
import random
def generate(n):
a = [[0] * n for _ in range(n)]
def rec(i, j):
if i == n - 1 and j == n:
return True
if j == n:
return rec(i + 1, 0)
candidate = set(range(1, n + 1))
for k in range(i):
candidate.discard(a[k][j])
for k in range(j):
candidate.discard(a[i][k])
if i == j:
for k in range(i):
candidate.discard(a[k][k])
if i + j == n - 1:
for k in range(i):
candidate.discard(a[k][n - 1 - k])
candidate_list = list(candidate)
random.shuffle(candidate_list)
for e in candidate_list:
a[i][j] = e
if rec(i, j + 1):
return True
a[i][j] = 0
return False
rec(0, 0)
return a
for row in generate(9):
print(row)
Output:
[8, 5, 4, 7, 1, 6, 2, 9, 3]
[2, 7, 5, 8, 4, 1, 3, 6, 9]
[9, 1, 2, 3, 6, 4, 8, 7, 5]
[3, 9, 7, 6, 2, 5, 1, 4, 8]
[5, 8, 3, 1, 9, 7, 6, 2, 4]
[4, 6, 9, 2, 8, 3, 5, 1, 7]
[6, 3, 1, 5, 7, 9, 4, 8, 2]
[1, 4, 8, 9, 3, 2, 7, 5, 6]
[7, 2, 6, 4, 5, 8, 9, 3, 1]

Related

How to optimize this for loop faster than O(N^3)?

My for loop prints all the consecutive subsequence of a list. For example, suppose a list contains [0, 1,2,3,4,5,6,7,8,9]. It prints,
0
0,1
0,1,2
0,1,2,3
........
0,1,2,3,4,5,6,7,8,9
1
1,2
1,2,3
1,2,3,4,5,6,7,8,9
........
8
8,9
9
for i in range(10)
for j in range(i, 10):
subseq = []
for k in range(i, j+1):
subseq.append(k)
print(subseq)
The current algorithmic complexity of this for loop is O(N^3). Is there any way to make this algorithm any faster?
I don't know Python (this is Python, right?), but something like this will be a little faster version of O(N^3) (see comments below):
for i in range(10):
subseq = []
for j in range(i, 10):
subseq.append(j)
print(subseq)
Yes, that works:
[0]
[0, 1]
[0, 1, 2]
[0, 1, 2, 3]
[0, 1, 2, 3, 4]
[0, 1, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5, 6]
[0, 1, 2, 3, 4, 5, 6, 7]
[0, 1, 2, 3, 4, 5, 6, 7, 8]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1]
[1, 2]
...
[7, 8]
[7, 8, 9]
[8]
[8, 9]
[9]
It’s not possible to do this in less than O(n3) time because you’re printing a total of O(n3) items. Specifically, split the array in quarters and look at the middle two quarters of the array. Pick any element there - say, the one at position k. That will be printed in at least n2 / 4 different subarrays: pick any element in the first quarter, any element in the last quarter, and the subarray between those elements will contain the element at position k.
This means that any of the n / 2 items in the middle two quarters gets printed at least n2 / 4 times, so you print at least n3 / 8 total values. There’s no way to do that in better than O(n3) time.

Find all unique sets of k non-negative integers summing to n

The accepted answer to this question provides an implementation of an algorithm that given two numbers k and n can generate all combinations (excluding permutations) of k positive integers which sum to n.
I'm looking for a very similar algorithm which essentially calculates the same thing except that the requirement that k > 0 is dropped, i.e. for k = 3, n = 4, the output should be
[0, 0, 0, 4], [0, 0, 1, 3], ... (in any order).
I have tried modifying the code snippet I linked but I have so far not had any success whatsoever. How can I efficiently implement this? (pseudo-code would be sufficient)
def partitions(Sum, K, lst, Minn = 0):
'''Enumerates integer partitions of Sum'''
if K == 0:
if Sum == 0:
print(lst)
return
for i in range(Minn, min(Sum + 1, Sum + 1)):
partitions(Sum - i, K - 1, lst + [i], i)
partitions(6, 3, [])
[0, 0, 6]
[0, 1, 5]
[0, 2, 4]
[0, 3, 3]
[1, 1, 4]
[1, 2, 3]
[2, 2, 2]
This code is quite close to linked answer idea, just low limit is 0 and correspondingly stop value n - size + 1 should be changed
You could use the code provided on the other thread provided as is.
Then you want to get all of the sets for set size 1 to k, and if your current set size is less than k then pad with 0's i.e
fun nonZeroSums (k, n)
for i in 1 to k
[pad with i - k 0's] concat sum_to_n(i, n)

Find all combinations in a 3x3 matrix following some rules

Given a 3x3 matrix:
|1 2 3|
|4 5 6|
|7 8 9|
I'd like to calculate all the combinations by connecting the numbers in this matrix following these rules:
the combinations width are between 3 and 9
use one number only once
you can only connect adjacent numbers
Some examples: 123, 258, 2589, 123654, etc.
For example 1238 is not a good combination because 3 and 8 are not adjacent. The 123 and the 321 combination is not the same.
I hope my description is clear.
If anyone has any ideas please let me know. Actually I don't know how to start :D. Thanks
This is a search problem. You can just use straightforward depth-first-search with recursive programming to quickly solve the problem. Something like the following:
func search(matrix[N][M], x, y, digitsUsed[10], combination[L]) {
if length(combination) between 3 and 9 {
add this combination into your solution
}
// four adjacent directions to be attempted
dx = {1,0,0,-1}
dy = {0,1,-1,0}
for i = 0; i < 4; i++ {
next_x = x + dx[i]
next_y = y + dy[i]
if in_matrix(next_x, next_y) and not digitsUsed[matrix[next_x][next_y]] {
digitsUsed[matrix[next_x][next_y]] = true
combination += matrix[next_x][next_y]
search(matrix, next_x, next_y, digitsUsed, combination)
// At this time, sub-search starts with (next_x, next_y) has been completed.
digitsUsed[matrix[next_x][next_y]] = false
}
}
}
So you could run search function for every single grid in the matrix, and every combinations in your solution are different from each other because they start from different grids.
In addition, we don't need to record the status which indicates one grid in the matrix has or has not been traversed because every digit can be used only once, so grids which have been traversed will never be traversed again since their digits have been already contained in the combination.
Here is a possible implementation in Python 3 as a a recursive depth-first exploration:
def find_combinations(data, min_length, max_length):
# Matrix of booleans indicating what values have been used
visited = [[False for _ in row] for row in data]
# Current combination
comb = []
# Start recursive algorithm at every possible position
for i in range(len(data)):
for j in range(len(data[i])):
# Add initial combination element and mark as visited
comb.append(data[i][j])
visited[i][j] = True
# Start recursive algorithm
yield from find_combinations_rec(data, min_length, max_length, visited, comb, i, j)
# After all combinations with current element have been produced remove it
visited[i][j] = False
comb.pop()
def find_combinations_rec(data, min_length, max_length, visited, comb, i, j):
# Yield the current combination if it has the right size
if min_length <= len(comb) <= max_length:
yield comb.copy()
# Stop the recursion after reaching maximum length
if len(comb) >= max_length:
return
# For each neighbor of the last added element
for i2, j2 in ((i - 1, j), (i, j - 1), (i, j + 1), (i + 1, j)):
# Check the neighbor is valid and not visited
if i2 < 0 or i2 >= len(data) or j2 < 0 or j2 >= len(data[i2]) or visited[i2][j2]:
continue
# Add neighbor and mark as visited
comb.append(data[i2][j2])
visited[i2][j2] = True
# Produce combinations for current starting sequence
yield from find_combinations_rec(data, min_length, max_length, visited, comb, i2, j2)
# Remove last added combination element
visited[i2][j2] = False
comb.pop()
# Try it
data = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
min_length = 3
max_length = 9
for comb in find_combinations(data, min_length, max_length):
print(c)
Output:
[1, 2, 3]
[1, 2, 3, 6]
[1, 2, 3, 6, 5]
[1, 2, 3, 6, 5, 4]
[1, 2, 3, 6, 5, 4, 7]
[1, 2, 3, 6, 5, 4, 7, 8]
[1, 2, 3, 6, 5, 4, 7, 8, 9]
[1, 2, 3, 6, 5, 8]
[1, 2, 3, 6, 5, 8, 7]
[1, 2, 3, 6, 5, 8, 7, 4]
[1, 2, 3, 6, 5, 8, 9]
[1, 2, 3, 6, 9]
[1, 2, 3, 6, 9, 8]
[1, 2, 3, 6, 9, 8, 5]
[1, 2, 3, 6, 9, 8, 5, 4]
[1, 2, 3, 6, 9, 8, 5, 4, 7]
...
Look at all the combinations and take the connected ones:
import itertools
def coords(n):
"""Coordinates of number n in the matrix."""
return (n - 1) // 3, (n - 1) % 3
def adjacent(a, b):
"""Check if a and b are adjacent in the matrix."""
ai, aj = coords(a)
bi, bj = coords(b)
return abs(ai - bi) + abs(aj - bj) == 1
def connected(comb):
"""Check if combination is connected."""
return all(adjacent(a, b) for a, b in zip(comb, comb[1:]))
for width in range(3, 10):
for comb in itertools.permutations(range(1, 10), width):
if connected(comb):
print(comb)

Computing number of sequences

I saw the following problem that I was unable to solve. What kind of algorithm will solve it?
We have been given a positive integer n. Let A be the set of all possible strings of length n where characters are from the set {1,2,3,4,5,6}, i.e. the results of dice thrown n times. How many elements of A contains at least one of the following strings as a substring:
1, 2, 3, 4, 5, 6
1, 1, 2, 2, 3, 3
4, 4, 5, 5, 6, 6
1, 1, 1, 2, 2, 2
3, 3, 3, 4, 4, 4
5, 5, 5, 6, 6, 6
1, 1, 1, 1, 1, 1
2, 2, 2, 2, 2, 2
3, 3, 3, 3, 3, 3
4, 4, 4, 4, 4, 4
5, 5, 5, 5, 5, 5
6, 6, 6, 6, 6, 6
I was wondering some kind of recursive approach but I got only mess when I tried to solve the problem.
I suggest reading up on the Aho-Corasick algorithm. This constructs a finite state machine based on a set of strings. (If your list of strings is fixed, you could even do this by hand.)
Once you have a finite state machine (with around 70 states), you should add an extra absorbing state to mark when any of the strings has been detected.
Now you problem is reduced to finding how many of the 6**n strings end up in the absorbing state after being pushed through the state machine.
You can do this by expressing the state machine as a matrix . Entry M[i,j] tells the number of ways of getting to state i from state j when one letter is added.
Finally you compute the matrix raised to the power n applied to an input vector that is all zeros except for a 1 in the position corresponding to the initial state. The number in the absorbing state position will tell you the total number of strings.
(You can use the standard matrix exponentiation algorithm to generate this answer in O(logn) time.)
What's wrong with your recursive approach, can you elaborate on that, anyway this can be solved using a recursive approach in O(6^n), but can be optimized using dp, using the fact that you only need to track the last 6 elements, so it can be done in O ( 6 * 2^6 * n) with dp.
rec (String cur, int step) {
if(step == n) return 0;
int ans = 0;
for(char c in { '1', '2', '3', '4', '5', '6' } {
if(cur.length < 6) cur += c
else {
shift(cur,1) // shift the string to the left by 1 step
cur[5] = c // add the new element to the end of the string
}
if(cur in list) ans += 1 + rec(cur, step+1) // list described in the question
else ans += rec(cur, step+1)
}
return ans;
}

How to generate matrix where sum of rows is a permutation of sum of columns?

For a given integer N, I want to generate a matrix of order NxN where sum of rows is some permutation of sum of column.
For example:
3
0 2 3
4 0 1
1 3 0
row sums are 5, 5, 4
col sums are 5, 5, 4
both are permutations of each other.
How to generate such matrix for any given N ?
PS:
I know that diagonal matrix, symmetric matrix would work here and the matrices like this
3
1 0 0
0 0 1
0 1 0
but i want to make a bit random matrix.
You could start with a matrix that fulfills the requirement but without the permutation aspect: so the sum for a particular row should equal the sum of the column with the same index. For example, the zero matrix would do.
Then randomly choose a set of columns. Iterate those columns, and choose the row to be the index of the previous column from that list (so the row will start out with the index of the last column in the list). This produces a cycle of elements such that if you increase the values of all of them with an equal constant, the sum-requirement is maintained. This constant can be 1 or any other integer (although 0 would not be very useful).
Repeat this as many times as you wish, until you feel it is scrambled enough. You could for instance decide to repeat this n² times.
Finally, you can shuffle the rows, to increase the randomness: the row sums now correspond with a permutation of the column sums.
Here is Python code:
import random
def increment(a):
i = 1 # the increment that will be applied. Could also be random
# choose a random list of distinct columns:
perm = random.sample(range(len(a)), random.randint(1,len(a)-1))
row = perm[-1]
# cycle through them and increment the values to keep the balance
for col in perm:
a[row][col] += i
row = col
return a
### main ###
n = 7
# create square matrix with only zeroes
a = [[0 for i in range(n)] for j in range(n)]
# repeat the basic mutation that keeps the sum property in tact:
for i in range(n*n): # as many times as you wish
increment(a)
# shuffle the rows
random.shuffle(a)
A run produced this matrix:
[[6, 5, 7, 7, 5, 2, 1],
[6, 1, 7, 6, 2, 5, 1],
[6, 1, 0, 4, 3, 5, 4],
[6, 2, 5, 1, 6, 2, 4],
[1, 3, 4, 2, 8, 3, 6],
[1, 7, 0, 3, 3, 10, 1],
[1, 4, 2, 3, 1, 6, 1]]
I used this check just before the row shuffle to make sure the sum property was in tact:
# test that indeed the sums are OK
def test(a):
for i in range(len(a)):
if sum(a[i]) != sum([a[j][i] for j in range(len(a))]):
print('fail at ', i)
One method to get fairly random looking ones is as follows:
First create a random symmetric matrix. Such a matrix will have its row sums equal its column sums.
Note that if any two rows are swapped then its row sums are permuted but its column sums are left alone. Similarly if any two columns are swapped then its column sums are permuted but its row sums are left alone. Thus -- if you randomly swap random rows and swap random columns a large number of times, the row and column sums will be permutations of each other but the original symmetry will be hidden.
A Python proof of concept:
import random
def randSwapRows(matrix):
i,j = random.sample(list(range(len(matrix))),2)
matrix[i], matrix[j] = matrix[j], matrix[i]
def randSwapColumns(matrix):
i,j = random.sample(list(range(len(matrix))),2)
for row in matrix:
row[i],row[j] = row[j],row[i]
def randSpecialMatrix(n):
matrix = [[0]*n for i in range(n)]
for i in range(n):
for j in range(i,n):
matrix[i][j] = random.randint(0,n-1)
matrix[j][i] = matrix[i][j]
#now swap a lot of random rows and columns:
for i in range(n**2):
randSwapRows(matrix)
randSwapColumns(matrix)
return matrix
#test:
matrix = randSpecialMatrix(5)
for row in matrix: print(row)
print('-'*15)
print('row sums: ' + ', '.join(str(sum(row)) for row in matrix))
print('col sums: ' + ', '.join(str(sum(column)) for column in zip(*matrix)))
Typical output:
[3, 2, 2, 0, 3]
[3, 1, 0, 2, 3]
[4, 1, 3, 3, 4]
[2, 0, 3, 3, 4]
[0, 0, 2, 1, 1]
---------------
row sums: 10, 9, 15, 12, 4
col sums: 12, 4, 10, 9, 15
Note that even though this is random looking it isn't really random in the sense of uniformly chosen from the set of all 5x5 matrices with entries in 0-4 which satisfy the desired property. Without a hit and miss approach of randomly generating matrices until you get such a matrix, I don't see any way to get uniform distribution.

Resources