Find all combinations in a 3x3 matrix following some rules - algorithm

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)

Related

Exhaust list of elements randomly without sorting them randomly first

If I have a list of 10K elements, and I want to randomly iterate through all of them, is there an algorithm that lets me access each element randomly, without just sorting them randomly first?
In other words, this would not be ideal:
const sorted = list
.map(v => [math.random(), v])
.sort((a,b) => a[0]- b[0]);
It would be nice to avoid the sort call and the mapping call.
My only idea would be to store everything in a hashmap and access the hash keys randomly somehow? Although that's just coming back to the same problem, afaict.
Just been having a play with this and realised that the Fisher-Yates shuffle works well "on-line". For example, if you've got a large list you don't need to spend the time to shuffle the whole thing before you start iterating over items, or, equivalently, you might only need a few items out of a large list.
I didn't see a language tag in the question, so I'll pick Python.
from random import randint
def iterrand(a):
"""Iterate over items of a list in a random order.
Additional items can be .append()ed arbitrarily at runtime."""
for i, ai in enumerate(a):
j = randint(i, len(a)-1)
a[i], a[j] = a[j], ai
yield a[i]
This is O(n) in the length of the list and by allowing .append()s (O(1) in Python) the list can be built in the background.
An example use would be:
l = [0, 1, 2]
for i, v in enumerate(iterrand(l)):
print(f"{i:3}: {v:<5} {l}")
if v < 4:
l.append(randint(1, 9))
which might produce output like:
0: 2 [2, 1, 0]
1: 3 [2, 3, 0, 1]
2: 1 [2, 3, 1, 1, 0]
3: 0 [2, 3, 1, 0, 1, 3]
4: 1 [2, 3, 1, 0, 1, 3, 7]
5: 7 [2, 3, 1, 0, 1, 7, 7, 3]
6: 7 [2, 3, 1, 0, 1, 7, 7, 3]
7: 3 [2, 3, 1, 0, 1, 7, 7, 3]
8: 2 [2, 3, 1, 0, 1, 7, 7, 3, 2]
9: 3 [2, 3, 1, 0, 1, 7, 7, 3, 2, 3]
10: 2 [2, 3, 1, 0, 1, 7, 7, 3, 2, 3, 2]
11: 7 [2, 3, 1, 0, 1, 7, 7, 3, 2, 3, 2, 7]
Update: To test correctness, I'd do something like:
# trivial tests
assert list(iterrand([])) == []
assert list(iterrand([1])) == [1]
# bigger uniformity test
from collections import Counter
# tally 1M draws
c = Counter()
for _ in range(10**6):
c[tuple(iterrand([1, 2, 3, 4, 5]))] += 1
# ensure it's uniform
assert all(7945 < v < 8728 for v in c.values())
# above constants calculated in R via:
# k<-120;p<-0.001/k;qbinom(c(p,1-p), 1e6, 1/k))
Fisher-Yates should do the trick as good as any, this article is really good:
https://medium.com/#oldwestaction/randomness-is-hard-e085decbcbb2
the relevant JS code is very short and sweet:
const fisherYatesShuffle = (deck) => {
for (let i = deck.length - 1; i >= 0; i--) {
const swapIndex = Math.floor(Math.random() * (i + 1));
[deck[i], deck[swapIndex]] = [deck[swapIndex], deck[i]];
}
return deck
}
to yield results as you go, so you don't have to iterate through the list twice, use generator function like so:
const fisherYatesShuffle = function* (deck) {
for (let i = deck.length - 1; i >= 0; i--) {
const swapIndex = Math.floor(Math.random() * (i + 1)); // * use ;
[deck[i], deck[swapIndex]] = [deck[swapIndex], deck[i]];
yield deck[i];
}
};
(note don't forget some of those semi-colons, when the next line is bracket notation).

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.

How to get the Nth arrangement in a Combinatoric sequence and vice-versa?

how do I get the Nth arrangement out of all possible combinations of arranging 4 indistinguishable balls in 3 distinct buckets. if Bl = number of balls and Bk = number of buckets e.g. for Bl = 4, Bk = 3 the possible arrangements are :
004,013,022,031,040,103,112,121,130,202,211,220,301,310,400 .
the first arrangement(N=0) is 004(i.e. bucket 1 = 0 balls, bucket 2 = 0 balls, bucket 3 = 4 balls) and the last(N=14) is 400. so say I have 103 N would be equal to 5. I want to be able to do
int Bl=4,Bk=3;
getN(004,Bl,Bk);// which should be = 0
getNthTerm(8,Bl,Bk);// which should be = 130
P.S: max number of terms for the sequence is (Bl+Bk-1)C(Bk-1) where C is the combinatorics/combination operator. Obtained from stars and bars
As far as I know, there is no faster way of doing this than combinatorial decomposition which takes roughly O(Bl) time.
We simply compute the number of balls which go into the each bucket for the selected index, working one bucket at a time. For each possible assignment to the bucket we compute the number of possible arrangements of the remaining balls and buckets. If the index is less than that number, we select that arrangement; otherwise we put one more ball in the bucket and subtract the number of arrangements we just skipped from the index.
Here's a C implementation. I didn't include the binom function in the implementation below. It's usually best to precompute the binomial coefficients over the range of values you are interested in, since there won't normally be too many. It is easy to do the computation incrementally but it requires a multiplication and a division at each step; while that doesn't affect the asymptotic complexity, it makes the inner loop much slower (because of the divide) and increases the risk of overflow (because of the multiply).
/* Computes arrangement corresponding to index.
* Returns 0 if index is out of range.
*/
int get_nth(long index, int buckets, int balls, int result[buckets]) {
int i = 0;
memset(result, 0, buckets * sizeof *result);
--buckets;
while (balls && buckets) {
long count = binom(buckets + balls - 1, buckets - 1);
if (index < count) { --buckets; ++i; }
else { ++result[i]; --balls; index -= count; }
}
if (balls) result[i] = balls;
return index == 0;
}
There are some interesting bijections that can be made. Finally, we can use ranking and unranking methods for the regular k-combinations, which are more common knowledge.
A bijection from the number of balls in each bucket to the ordered multiset of choices of buckets; for example: [3, 1, 0] --> [1, 1, 1, 2] (three choices of 1 and one choice of 2).
A bijection from the k-subsets of {1...n} (with repetition) to k-subsets of {1...n + k − 1} (without repetition) by mapping {c_0, c_1...c_(k−1)} to {c_0, c_(1+1), c_(2+2)...c_(k−1+k−1)} (see here).
Here's some python code:
from itertools import combinations_with_replacement
def toTokens(C):
return map(lambda x: int(x), list(C))
def compositionToChoice(tokens):
result = []
for i, t in enumerate(tokens):
result = result + [i + 1] * t
return result
def bijection(C):
result = []
k = 0
for i, _c in enumerate(C):
result.append(C[i] + k)
k = k + 1
return result
compositions = ['004','013','022','031','040','103','112',
'121','130','202','211','220','301','310','400']
for c in compositions:
tokens = toTokens(c)
choices = compositionToChoice(tokens)
combination = bijection(choices)
print "%s --> %s --> %s" % (tokens, choices, combination)
Output:
"""
[0, 0, 4] --> [3, 3, 3, 3] --> [3, 4, 5, 6]
[0, 1, 3] --> [2, 3, 3, 3] --> [2, 4, 5, 6]
[0, 2, 2] --> [2, 2, 3, 3] --> [2, 3, 5, 6]
[0, 3, 1] --> [2, 2, 2, 3] --> [2, 3, 4, 6]
[0, 4, 0] --> [2, 2, 2, 2] --> [2, 3, 4, 5]
[1, 0, 3] --> [1, 3, 3, 3] --> [1, 4, 5, 6]
[1, 1, 2] --> [1, 2, 3, 3] --> [1, 3, 5, 6]
[1, 2, 1] --> [1, 2, 2, 3] --> [1, 3, 4, 6]
[1, 3, 0] --> [1, 2, 2, 2] --> [1, 3, 4, 5]
[2, 0, 2] --> [1, 1, 3, 3] --> [1, 2, 5, 6]
[2, 1, 1] --> [1, 1, 2, 3] --> [1, 2, 4, 6]
[2, 2, 0] --> [1, 1, 2, 2] --> [1, 2, 4, 5]
[3, 0, 1] --> [1, 1, 1, 3] --> [1, 2, 3, 6]
[3, 1, 0] --> [1, 1, 1, 2] --> [1, 2, 3, 5]
[4, 0, 0] --> [1, 1, 1, 1] --> [1, 2, 3, 4]
"""

Algorithm to generate Diagonal Latin Square matrix

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]

merging a queue with elements in ascending order and a stack with elements in descending order

Suppose I have a queue with elements in ascending order, i.e. head < 2nd < 3rd < ... < tail
and a stack with elements in descending order, i.e. top > 2nd > 3rd > ...
and their size differ at most 1 (they could be of same size).
What is the most efficient way of merging them together into the same queue (or stack) as a single sorted sequence without additional stack/queue?
It seems that the best I thought of so far is a quadratic algorithm that is basically selection sort, and it doesn't really take advantage of the fact that the queue and the stack are pre-sorted and their size. I am wondering if we can do better?
Just merge in place, placing your result at the end of the queue. Keep a count so you know when to finish. This is O(n).
This does it in python. I haven't used queue or stack classes but you'll see the q lists are FIFO and the s lists are LIFO!
The most interesting debug case is enabled so you can see the output.
def sort(Q, S, debug=False):
q = Q
s = S
size = len(Q) + len(S)
handled = 0
while handled < size:
move_from_queue = len(s) == 0 or (len(q) > 0 and q[0] < s[0])
last_from_stack = (handled == size-1) and (len(s) == 1)
if move_from_queue and not last_from_stack:
q_front = q[0]
q = q[1:] + [q_front]
msg = 'moved q[0]=%d to end, q=%r' % (q_front, q)
else:
(s_top, s) = (s[0], s[1:])
q += [s_top]
msg = 'popped s[0]=%d to end of q=%r,s=%r' % (s_top, q, s)
handled += 1
if debug:
print 'Debug-Step %d: %s' % (handled, msg)
return (q, s)
def test_sort(Q, S, debug=False):
print 'Pre Q: %r' % Q
print 'Pre S: %r' % S
(Q, S) = sort(Q, S, debug)
print 'Sorted: %r' % Q
print
if __name__ == "__main__":
test_sort([1, 3, 7, 9], [2, 5, 5])
test_sort([1, 3, 7], [2, 5, 5])
test_sort([1, 3, 7], [2, 5, 5, 9], True)
test_sort([], [])
test_sort([1], [])
test_sort([], [1])
Output:
Pre Q: [1, 3, 7, 9]
Pre S: [2, 5, 5]
Sorted: [1, 2, 3, 5, 5, 7, 9]
Pre Q: [1, 3, 7]
Pre S: [2, 5, 5]
Sorted: [1, 2, 3, 5, 5, 7]
Pre Q: [1, 3, 7]
Pre S: [2, 5, 5, 9]
Debug-Step 1: moved q[0]=1 to end, q=[3, 7, 1]
Debug-Step 2: popped s[0]=2 to end of q=[3, 7, 1, 2],s=[5, 5, 9]
Debug-Step 3: moved q[0]=3 to end, q=[7, 1, 2, 3]
Debug-Step 4: popped s[0]=5 to end of q=[7, 1, 2, 3, 5],s=[5, 9]
Debug-Step 5: popped s[0]=5 to end of q=[7, 1, 2, 3, 5, 5],s=[9]
Debug-Step 6: moved q[0]=7 to end, q=[1, 2, 3, 5, 5, 7]
Debug-Step 7: popped s[0]=9 to end of q=[1, 2, 3, 5, 5, 7, 9],s=[]
Sorted: [1, 2, 3, 5, 5, 7, 9]
Pre Q: []
Pre S: []
Sorted: []
Pre Q: [1]
Pre S: []
Sorted: [1]
Pre Q: []
Pre S: [1]
Sorted: [1]

Resources