Generating all unique combinations for "drive ya nuts" puzzle - python

A while back I wrote a simple python program to brute-force the single solution for the drive ya nuts puzzle.
(source: tabbykat.com)
The puzzle consists of 7 hexagons with the numbers 1-6 on them, and all pieces must be aligned so that each number is adjacent to the same number on the next piece.
The puzzle has ~1.4G non-unique possibilities: you have 7! options to sort the pieces by order (for example, center=0, top=1, continuing in clockwise order...). After you sorted the pieces, you can rotate each piece in 6 ways (each piece is a hexagon), so you get 6**7 possible rotations for a given permutation of the 7 pieces. Totalling: 7!*(6**7)=~1.4G possibilities. The following python code generates these possible solutions:
def rotations(p):
for i in range(len(p)):
yield p[i:] + p[:i]
def permutations(l):
if len(l)<=1:
yield l
else:
for perm in permutations(l[1:]):
for i in range(len(perm)+1):
yield perm[:i] + l[0:1] + perm[i:]
def constructs(l):
for p in permutations(l):
for c in product(*(rotations(x) for x in p)):
yield c
However, note that the puzzle has only ~0.2G unique possible solutions, as you must divide the total number of possibilities by 6 since each possible solution is equivalent to 5 other solutions (simply rotate the entire puzzle by 1/6 a turn).
Is there a better way to generate only the unique possibilities for this puzzle?

To get only unique valid solutions, you can fix the orientation of the piece in the center. For example, you can assume that that the "1" on the piece in the center is always pointing "up".
If you're not already doing so, you can make your program much more efficient by checking for a valid solution after placing each piece. Once you've placed two pieces in an invalid way, you don't need to enumerate all of the other invalid combinations.

If there were no piece in the centre, this would be easy. Simply consider only the situations where piece 0 is at the top.
But we can extend that idea to the actual situation. You can consider only the situations where piece i is in the centre, and piece (i+1) % 7 is at the top.

I think the search space is quite small, though the programming might be awkward.
We have seven choices for the centre piece. Then we have 6 choices for the
piece above that but its orientation is fixed, as its bottom edge must match the top edge of the centre piece, and similarly whenever we choose a piece to go in a slot, the orientation is fixed.
There are fewer choices for the remaining pieces. Suppose for
example we had chosen the centre piece and top piece as in the picture; then the
top right piece must have (clockwise) consecutive edges (5,3) to match the pieces in
place, and only three of the pieces have such a pair of edges (and in fact we've already
chosen one of them as the centre piece).
One could first off build a table with a list
of pieces for each edge pair, and then for each of the 42 choices of centre and top
proceed clockwise, choosing only among the pieces that have the required pair of edges (to match the centre piece and the previously placed piece) and backtracking if there are no such pieces.
I reckon the most common pair of edges is (1,6) which occurs on 4 pieces, two other edge pairs ((6,5) and (5,3)) occur on 3 pieces, there are 9 edge pairs that occur on two pieces, 14
that occur on 1 piece and 4 that don't occur at all.
So a very pessimistic estimate of the number of choices we must make is
7*6*4*3*3*2 or 3024.

Related

Finding local minima in 2D array, in O(N) time

Given an NxN matrix where all numbers are unique, find ANY local minima in O(N) time. Here is an example of the matrix. It's given in
lst = [[30,100,20,19,18],
[29,101,21,104,17],
[28,102,22,105,16],
[27,103,23,106,15],
[26,25,24,107,14]]
So the way to solve it in O(N) time is to essentially split the list into 4 sections ("windows"), find the smallest element in the green (middle row & column) and compare it with the next "neighboring" smallest value. If not smaller than it's neighbor, recurse to either top left, top right, bottom left, bottom right, depending on which position that smaller than mid_smallest is in.
(slide 20)
https://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf
I have everything written out so far, but I'm really really struggling to find a way to recurse through one of the 4 sections: Here's the pseudocode:
The portion I'm having a difficulty with is the recursion part ex: Find_find_minimum(Top-left_sub_matrix)
How can I recurse through the top left/right or bottom left/right matrix without creating another matrix?
Solved it. I modularized my function with different inputs as indicators.

Algorithm to count unit triangles in large composite shape

I need to write a brute-force algorithm to count the number of unit triangles in a complex shape. The shape is created each iteration by adding triangles to surround all outer edges.
The shape in iteration number n would look as above and the outputs would be 1 4 10 respectively.
Unfortunately I do not really know where to begin, first thought was to create 2 classes; a triangle and a grid class consisting of multiple triangles. However adding the outer triangles would prove difficult to do past n = 3 as some edge pairs will only need 1 shared unit triangle.
Any thoughts?
Nevermind solution was simpler than I ever imagined as triangles added increase by 3 every time a simple for loop to add from n=1 worked easily enough.

Solution to variation of the N Queens puzzle

I wrote a python script to solve the N Queens puzzle. I made a function which, provided n, will return the first solution it finds for n queens by using backtracking. With a small modification it is possible to make the function find and return all solutions by exhausting the search space. It works well for n between 1 and 23. After 23 it starts to take some time to find a single solution.
I was wondering if it is possible to find a solution with a further constraint by extending the "attack range" of the queen. A queen in chess can attack horizontally, vertically, and on the diagonals. For my modification the queen can also attack the adjacent squares to the left and the right of the diagonal ones. As a consequence of this behavior, each queen must be 4 squares away from the next queen, instead of 3 squares for the normal puzzle.
In the following image, the blue squares are the normal queen range, and the green squares represent the new attack range: New queen attack range.
I made a new function which takes into account this new constraint. However, after running my code, I haven't been able to find any solutions for any number up to 23, and after 24 it takes a lot of time.
So my question is: does anyone know if there is a solution at all for this problem? Which is the smallest number for which there is a solution?
If anyone has done this before, I'm sure their code will be better and faster than mine, but I can provide the code if needed.
Thanks in advance!
With these super queens, you will no longer be able to fit N queens on an NxN board, other than the trivial 1x1 board. One way to see this is that there are 2N-1 diagonals (let's use lower left to upper right) on an NxN board. Each queen will be attacking 3 diagonals, except if they are in the corner they will attack 2 diagonals.
Let's say we have one queen in the corner, occupying 2 diagonals. Then e.g. on an 8x8 board we have 13 diagonals left which can be used by floor(13/3) queens or 4 queens. So at most we could have 5 queens on an 8x8 board. I don't know if this is a tight upper bound.

How to determine the minimum number to totally break a connected rings?

It's a question on checkio - Break Rings, but I only can use a bad way with O(n*2^n) complexity by testing all possible break ways and find the minimum one.
The problem:
A blacksmith gave his apprentice a task, ordering them to make a selection of rings. The apprentice is not yet skilled in the craft and as a result of this, some (to be honest, most) of rings came out connected together. Now he’s asking for your help separating the rings and deciding how to break enough rings to free so as to get the maximum number of rings possible.
All of the rings are numbered and you are told which of the rings are connected. This information is given as a sequence of sets. Each set describes the connected rings. For example: {1, 2} means that the 1st and 2nd rings are connected. You should count how many rings we need to break to get the maximum of separate rings. Each of the rings are numbered in a range from 1 to N, where N is total quantity of rings.
https://static.checkio.org/media/task/media/0d98b24304034e2e9017ba00fc51f6e3/example-rings.svg
example-rings
(sorry I don't know how to change the svg in mac to a photo.)
In the above image you can see the connections: ({1,2},{2,3},{3,4},{4,5},{4,6},{6,5}). The optimal solution here would be to break 3 rings, making 3 full and separate rings. So the result is 3.
Input: Information about the connected rings as a tuple of sets with integers.
Output: The number of rings to break as an integer.
It works only when the test case is small so it is not practical(I guess it even can't pass the test)
from functools import reduce
import copy
def break_rings(rings):
max_ring = max(reduce(set.union,rings))
rings = list(rings)
possible_set = [list(bin(i)[2:].rjust(max_ring,'0')) for i in range(2**max_ring)]
possible_set = [list(map(int,j)) for j in possible_set]
min_result = max_ring
for test_case in possible_set:
tmp = copy.copy(rings)
tmp2 = copy.copy(rings)
for index, value in enumerate(test_case):
if value:
for set_connect in tmp:
if index+1 in set_connect and set_connect in tmp2:
tmp2.remove(set_connect)
if not tmp2:
min_result = min(sum(test_case),min_result)
return min_result
So, I think it must thinking about the algorithm about the graph, but i just don't know what kind of problem I am facing.
Can you help me improve the algorithm?
Thank you for looking this problem!
You can think of this as a type of graph problem called vertex cover.
Draw a graph with a vertex for each ring, and an edge for each connection, i.e. each pair of joined rings.
Your task is to disconnect the rings with minimum breakages. A connection is broken if the ring at either edge is broken. In other words, you need to choose a set of rings (vertices) such that every connection (edge) is incident to one to the chosen rings.
This is exactly the vertex cover problem.
Unfortunately, vertex cover is NP-complete so there is not any non-exponential algorithm currently known.
I would recommend improving the speed of your algorithm by rejecting bad cases earlier. For example, use a backtracking algorithm that decides for each ring whether to break it or not. If you chose to not break it, you can immediately conclude a lot of other rings must be broken.

Performance of a "fuzzy" Jaccard index implementation

I'm a trying to calculate a kind of fuzzy Jaccard index between two sets with the following rationale: as the Jaccard index, I want to calculate the ratio between the number of items that are common to both sets and the total number of different items in both sets. The problem is that I want to use a similarity function with a threshold to determine what what counts as the "same" item being in both sets, so that items that are similar:
Aren't counted twice in the union
Are counted in the intersection.
I have a working implementation here (in python):
def fuzzy_jaccard(set1, set2, similarity, threshold):
intersection_size = union_size = len(set1 & set2)
shorter_difference, longer_difference = sorted([set2 - set1, set1 - set2], key=len)
while len(shorter_difference) > 0:
item1, item2 = max(
itertools.product(longer_difference, shorter_difference),
key=lambda (a, b): similarity(a, b)
)
longer_difference.remove(item1)
shorter_difference.remove(item2)
if similarity(item1, item2) > threshold:
union_size += 1
intersection_size += 1
else:
union_size += 2
union_size = union_size + len(longer_difference)
return intersection_size / union_size
The problem here is the this is quadratic in the size of the sets, because in itertools.product I iterate in all possible pairs of items taken one from each set(*). Now, I think I must do this because I want to match each item a from set1 with the best possible candidate b from set2 that isn't more similar to another item a' from set1.
I have a feeling that there should be a O(n) way of doing that I'm not grasping. Do you have any suggestions?
There are other issues two, like recalculating the similarity for each pair once I get the best match, but I don't care to much about them.
I doubt there's any way that would be O(n) in the general case, but you can probably do a lot better than O(n^2) at least for most cases.
Is similarity transitive? By this I mean: can you assume that distance(a, c) <= distance(a, b) + distance(b, c)? If not, this answer probably won't help. I'm treating similarities like distances.
Try clumping the data:
Pick a radius r. Based on intuition, I suggest setting r to one-third of the average of the first 5 similarities you calculate, or something.
The first point you pick in set1 becomes the centre of your first clump. Classify the points in set2 as being in the clump (similarity to the centre point <= r) or outside the clump. Also keep track of points that are within 2r of the clump centre.
You can require that clump centre points be at least a distance of 2r from each other; in that case some points may not be in any clump. I suggest making them at least r from each other. (Maybe less if you're dealing with a large number of dimensions.) You could treat every point as a clump centre but then you wouldn't save any processing time.
When you pick a new point, first compare it with the clump centre points (even though they're in the same set). Either it's in an already existing clump, or it becomes a new clump centre, (or perhaps neither if it's between r and 2r of a clump centre). If it's within r of a clump centre, then compare it with all points in the other set that are within 2r of that clump centre. You may be able to ignore points further than 2r from the clump centre. If you don't find a similar point within the clump (perhaps because the clump has no points left), then you may have to scan all the rest of the points for that case. Hopefully this would mostly happen only when there aren't many points left in the set. If this works well, then in most cases you'd find the most similar point within the clump and would know that it's the most similar point.
This idea may require some tweaking.
If there are a large number of dimenstions involved, then you might find that for a given radius r, frustratingly many points are within 2r of each other while few are within r of each other.
Here's another algorithm. The more time-consuming it is to calculate your similarity function (as compared to the time it takes to maintain sorted lists of points) the more index points you might want to have. If you know the number of dimensions, it might make sense to use that number of index points. You might reject a point as a candidate index point if it's too similar to another index point.
For each of the first point you use and any others you decide to use as index points, generate a list of all the remaining points in the other set, sorted in order of distance from the index point,
When you're comparing a point P1 to points in the other set, I think you can skip over sets for two possible reasons. Consider the most similar point P2 you've found to P1. If P2 is similar to an index point then you can skip all points which are sufficiently dissimilar from that index point. If P2 is dissimilar to an index point then you can skip over all points which are sufficiently similar to that index point. I think in some cases you can skip over some of both types of point for the same index point.

Categories

Resources