Related
I'm creating "Boggle" in python, and I have a list of tuples that represent coordinates on a game board:
all_coordinates = [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2), (0, 3), (1, 3), (2, 3)]
I'm trying to create a new list of lists of tuples that will represent all possible paths on the board.
It'd look something like this:
[[(0,0),(1,0)], ... , [(0,0),(1,0),(2,0),(2,1)] , ... , [(2, 1), (2, 2), (2, 3)], ...]
I tried using itertools.combinations and itertools.permutations but it doesn't seem to do the job, for example the following path:
[(2,1),(1,1),(1,0),(2,0)]
does not appear on the list.
This particular function doesn't necessarily have to output 'valid' paths (valid = moving one step horizontally, vertically or diagonally each time), just all of the possible combinations from the tuples representing the board. I have a function that checks if a certain path returns a valid word. I'm trying to print out all possible paths that return a valid word on the board.
itertools.permutations does indeed produce all the permutations, including the [(2,1),(1,1),(1,0),(2,0)] one that you said was missing. Note that each call to permutations gets you all the permutations of a particular length:
>>> all_coordinates = [(0, 0), (1, 0), (2, 0), (0, 1), (1, 1), (2, 1), (0, 2), (1, 2), (2, 2), (0, 3), (1, 3), (2, 3)]
>>> from itertools import permutations
>>> ((2,1),(1,1),(1,0),(2,0)) in permutations(all_coordinates, 4)
True
If you want to see all the permutations from, say, length 2 to length 4, try:
for k in range(2, 5):
for p in permutations(all_coordinates, k):
print(p)
The resulting sequence is very long; as others have pointed out, you might want to come up with another method for generating paths that only include adjacent coordinates (e.g. a breadth-first search).
(edit) Just for fun, here's a very quick DFS approach to building all the paths up to length 4 by looking only at adjacent coordinates:
>>> def print_paths(path):
... print(path)
... if len(path) >= 4:
... return
... x, y = path[-1]
... for dx in range(-1, 2):
... for dy in range(-1, 2):
... c = x + dx, y + dy
... if c not in path and c in all_coordinates:
... print_paths(path + [c])
...
>>> print_paths([(2, 1)])
[(2, 1)]
[(2, 1), (1, 0)]
[(2, 1), (1, 0), (0, 0)]
[(2, 1), (1, 0), (0, 0), (0, 1)]
[(2, 1), (1, 0), (0, 0), (1, 1)]
[(2, 1), (1, 0), (0, 1)]
[(2, 1), (1, 0), (0, 1), (0, 0)]
[(2, 1), (1, 0), (0, 1), (0, 2)]
[(2, 1), (1, 0), (0, 1), (1, 1)]
[(2, 1), (1, 0), (0, 1), (1, 2)]
[(2, 1), (1, 0), (1, 1)]
[(2, 1), (1, 0), (1, 1), (0, 0)]
[(2, 1), (1, 0), (1, 1), (0, 1)]
[(2, 1), (1, 0), (1, 1), (0, 2)]
[(2, 1), (1, 0), (1, 1), (1, 2)]
[(2, 1), (1, 0), (1, 1), (2, 0)]
[(2, 1), (1, 0), (1, 1), (2, 2)]
[(2, 1), (1, 0), (2, 0)]
[(2, 1), (1, 0), (2, 0), (1, 1)]
[(2, 1), (1, 1)]
[(2, 1), (1, 1), (0, 0)]
[(2, 1), (1, 1), (0, 0), (0, 1)]
[(2, 1), (1, 1), (0, 0), (1, 0)]
[(2, 1), (1, 1), (0, 1)]
[(2, 1), (1, 1), (0, 1), (0, 0)]
[(2, 1), (1, 1), (0, 1), (0, 2)]
[(2, 1), (1, 1), (0, 1), (1, 0)]
[(2, 1), (1, 1), (0, 1), (1, 2)]
[(2, 1), (1, 1), (0, 2)]
[(2, 1), (1, 1), (0, 2), (0, 1)]
[(2, 1), (1, 1), (0, 2), (0, 3)]
[(2, 1), (1, 1), (0, 2), (1, 2)]
[(2, 1), (1, 1), (0, 2), (1, 3)]
[(2, 1), (1, 1), (1, 0)]
[(2, 1), (1, 1), (1, 0), (0, 0)]
[(2, 1), (1, 1), (1, 0), (0, 1)]
[(2, 1), (1, 1), (1, 0), (2, 0)]
(...)
I'm trying to understand why it is that the combinations of -let's say- 0 and 1 are only: [(0, 0), (0, 1), (1, 1)] and why (1,0) is not included.
The same goes for all the combinations of pairs of 0,1,2,3.
I would like to get: (0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3)
But my code is only giving me: [(0, 0), (0, 1), (0, 2), (0, 3), (1, 1), (1, 2), (1, 3), (2, 2), (2, 3), (3, 3)]
My/the code, which I got from realpython.com/python-itertools/ :
import itertools as com
x = list(com.combinations_with_replacement([0,1,2,3], 2))
How can I edit the code so that it prints all the desired combinations?
You would want to use itertools.product since you want the cartesian product:
>>> import itertools
>>> list(itertools.product([0, 1, 2, 3], repeat=2))
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3)]
Note you don't want to use itertools.permutations since the output will not contain entries like (0, 0), (1, 1), (2, 2), or (3, 3) since each element in the iterable is only used once.
In order to get all possible combinations, you need to use the product function:
import itertools as com
x = list(com.product([0,1,2,3], repeat=2))
print(x)
As stated in Python docs, this is the same as:
x = [(y,z) for y in [0,1,2,3] for z in [0,1,2,3]]
This will return:
[(0, 0), (0, 1), (0, 2), (0, 3), (1, 0), (1, 1), (1, 2), (1, 3), (2, 0), (2, 1), (2, 2), (2, 3), (3, 0), (3, 1), (3, 2), (3, 3)]
how should I make my check for conflict work and go through all possible ways so as to return the optimal way??
objectiv: return optimal coordinates solution without conflict for the instance e.g (0,0) , (0,2)..
what we have an example (n*m)=(5*5):
A for Vertically: height=a=3, width=b=2
B for horizontally:height=a=2, width=b=3
if A=(0,0) means: e.g (0,0) till (0+height,O+width) must fit in n*m and must not be occupied
coordinates={'C': [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]}
so i want to test as from (0,0) if A or B passes recursively and return an optimal solution at the end, i have tried this in form of a tree it didn't work, since i always need to check on already solved solutions to know if next coordinate fits or not
dic= dict({"A": [], "B": []})
p={'C': [(0, 0), (0, 1), (0, 2), (0, 3), (0, 4), (1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (2, 0), (2, 1), (2, 2), (2, 3), (2, 4), (3, 0), (3, 1), (3, 2), (3, 3), (3, 4), (4, 0), (4, 1), (4, 2), (4, 3), (4, 4)]}
def enumOptimal(p,dic,ii):
if A : dic['A'].append(A)
enumOptimal(next p,dic,ii)
if B : dic['B'].append(B)
enumOptimal(next p,dic,ii)
enum(next Point, dic)#in case neither A nor B fits
if ii >len(p): print dic
I have a list of tuples:
[(0, 1), (0, 1), (0, 0), (0, 0), (1, 0), (1, 0), (1, 1), (1, 0), (1, 0), (2, 0), (2, 1), (2, 0), (3, 0), (3, 1), (3, 1), (3, 0), (3, 0), (4, 0), (4, 1), (4, 0), (4, 1), (4, 1), (5, 0), (5, 0), (5, 1), (5, 1)]
and I want to sum the right-side of tuples where the left-side is equal, and to put it in another tuples-list, so for the above list i'll get:
[(0,2),(1,1),(2,1),(3,2),(4,3),(5,2)]
I tried this:
k=0
for i,TCtup in enumerate(wordsMatchingList):
if wordsMatchingList[i][0]==k:
TC_matches+=wordsMatchingList[i][1]
print("k: {} /// TC_matches: {}".format(k,TC_matches)) #for checking
else:
groupedWordsMatchingList.append(tuple((k,TC_matches)))
TC_matches=0
k+=1
but from k=1 it just loop one time less for every k because of the else condition.
thank you
If your tuples are guaranteed to come in order like this—all the (0, x), then all the (1, x), etc.—you can use groupby:
>>> xs = [(0, 1), (0, 1), (0, 0), (0, 0), (1, 0), (1, 0), (1, 1), (1, 0), (1, 0), (2, 0), (2, 1), (2, 0), (3, 0), (3, 1), (3, 1), (3, 0), (3, 0), (4, 0), (4, 1), (4, 0), (4, 1), (4, 1), (5, 0), (5, 0), (5, 1), (5, 1)]
>>> from itertools import groupby
>>> from operator import itemgetter
>>> groups = groupby(xs, key=itemgetter(0))
>>> ys = [(key, sum(map(itemgetter(1), group))) for key, group in groups]
If they aren't, but you can sort them (you have a list, not just an arbitrary iterable, and it isn't so huge that log-linear time will be too expensive):
>>> groups = groupby(sorted(xs, key=itemgetter(0)), key=itemgetter(0))
If you can't sort them, you can manually build up the totals as you go:
>>> from collections import Counter
>>> totals = Counter()
>>> for k, v in xs:
... totals[k] += v
>>> ys = list(totals.items())
yet another way,
t.sort(key=lambda x: x[0]) #sort before groupby (required)
g=itertools.groupby(t, lambda x: x[0])
new_l = []
for k,v in g:
new_l.append((k, sum([x[1] for x in v])))
Another approach is using a defaultdict (from collections) and to iterate the list of tuples.
from collections import defaultdict
lst = [(0, 1), (0, 1), (0, 0), (0, 0), (1, 0), (1, 0), (1, 1), (1, 0), (1, 0), (2, 0), (2, 1), (2, 0), (3, 0), (3, 1), (3, 1), (3, 0), (3, 0), (4, 0), (4, 1), (4, 0), (4, 1), (4, 1), (5, 0), (5, 0), (5, 1), (5, 1)]
d = defaultdict(int)
for (u,v) in lst:
d[u]+=v
# list(d.items()) [(0, 2), (1, 1), (2, 1), (3, 2), (4, 3), (5, 2)]
I'd recommend using a library with a groupby function. pandas, for instance, can be useful
>>> s = pd.DataFrame(list_)
>>> s.groupby(0, as_index=False).sum().agg(tuple,1).tolist()
[(0, 2), (1, 1), (2, 1), (3, 2), (4, 3), (5, 2)]
In [5]: [(j, sum([i[1] for i in a if i[0] == j])) for j in set([i[0] for i in a])]
Out[5]: [(0, 2), (1, 1), (2, 1), (3, 2), (4, 3), (5, 2)]
lst = [(0, 1), (0, 1), (0, 0), (0, 0), (1, 0), (1, 0), (1, 1), (1, 0), (1, 0), (2, 0), (2, 1), (2, 0), (3, 0), (3, 1), (3, 1), (3, 0), (3, 0), (4, 0), (4, 1), (4, 0), (4, 1), (4, 1), (5, 0), (5, 0), (5, 1), (5, 1)]
[(i,sum([q[1] for q in lst if q[0] == i])) for i in range(lst[-1][0]+1)]
gives:
[(0,2),(1,1),(2,1),(3,2),(4,3),(5,2)]
I want to apply the 4 color theorem ie. each neighbor polygons on a map should have a different color. The theorem state that only 4 colors is needed for any kind of map.
As input I have an array of polygon containing id and color id and a graph array of adjacent polygons. As output I want to associate a color id between 0-3 (or at maximum 0-4) to each polygons ensuring that adjacent polygons have different color id.
I have the following code (from this question) that returns a different color id for each neighboor, but it does not ensure that the minimum number of colors is used.
#array of input polygon tuples (id, color_id) color_id is None at beginning
input_polygons = [(0, None), (1, None), (2, None), (3, None), (4, None), (5, None), (6, None), (7, None), (8, None)]
#graph array of neighbors ids
adjacent_polygons_graph = [[0, 1], [0, 2], [0, 3], [0, 4], [0, 5], [1, 0], [2, 0], [2, 3], [3, 0], [3, 2], [3, 6], [3, 8], [4, 0], [4, 5], [4, 6], [5, 0], [5, 4], [6, 3], [6, 4], [6, 7], [7, 6], [8, 3]]
colored = []
for polygon in input_polygons:
nbrs = [second for first, second in adjacent_polygons_graph if first == polygon[0]]
used_colors = []
for nbr in nbrs:
used_colors += [second for first, second in colored if first == nbr]
polygon[1]=[color for color in range(10) if color not in used_colors][0]
colored.append(polygon)
I would like the script to reduce at maximum the number of colors (4 or 5) using brute force loop or other method.
Here is the graph coloring code extracted from the code in my math.stackexchange answer. That code is admittedly a little complicated because it uses Knuth's Algorithm X for two tasks: solving the trominoe packing problem, and then solving the coloring problem for each packing problem solution.
This code finds 24 3-color solutions or 756 4-color for your sample data in less than 1 second. There are actually more solutions, but I arbitrarily set the colors for the first 2 nodes to reduce the total number of solutions, and to speed up the searching algorithm a little.
''' Knuth's Algorithm X for the exact cover problem,
using dicts instead of doubly linked circular lists.
Written by Ali Assaf
From http://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
and http://www.cs.mcgill.ca/~aassaf9/python/sudoku.txt
Python 2 / 3 compatible
Graph colouring version
'''
from __future__ import print_function
from itertools import product
def solve(X, Y, solution):
if not X:
yield list(solution)
else:
c = min(X, key=lambda c: len(X[c]))
Xc = list(X[c])
for r in Xc:
solution.append(r)
cols = select(X, Y, r)
for s in solve(X, Y, solution):
yield s
deselect(X, Y, r, cols)
solution.pop()
def select(X, Y, r):
cols = []
for j in Y[r]:
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].remove(i)
cols.append(X.pop(j))
return cols
def deselect(X, Y, r, cols):
for j in reversed(Y[r]):
X[j] = cols.pop()
for i in X[j]:
for k in Y[i]:
if k != j:
X[k].add(i)
#Invert subset collection
def exact_cover(X, Y):
newX = dict((j, set()) for j in X)
for i, row in Y.items():
for j in row:
newX[j].add(i)
return newX
def colour_map(nodes, edges, ncolours=4):
colours = range(ncolours)
#The edges that meet each node
node_edges = dict((n, set()) for n in nodes)
for e in edges:
n0, n1 = e
node_edges[n0].add(e)
node_edges[n1].add(e)
for n in nodes:
node_edges[n] = list(node_edges[n])
#Set to cover
coloured_edges = list(product(colours, edges))
X = nodes + coloured_edges
#Subsets to cover X with
Y = dict()
#Primary rows
for n in nodes:
ne = node_edges[n]
for c in colours:
Y[(n, c)] = [n] + [(c, e) for e in ne]
#Dummy rows
for i, ce in enumerate(coloured_edges):
Y[i] = [ce]
X = exact_cover(X, Y)
#Set first two nodes
partial = [(nodes[0], 0), (nodes[1], 1)]
for s in partial:
select(X, Y, s)
for s in solve(X, Y, []):
s = partial + [u for u in s if not isinstance(u, int)]
s.sort()
yield s
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
input_polygons = [
(0, None), (1, None), (2, None), (3, None), (4, None),
(5, None), (6, None), (7, None), (8, None),
]
#graph array of neighbors ids
adjacent_polygons_graph = [
(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (1, 0), (2, 0), (2, 3),
(3, 0), (3, 2), (3, 6), (3, 8), (4, 0), (4, 5), (4, 6),
(5, 0), (5, 4), (6, 3), (6, 4), (6, 7), (7, 6), (8, 3),
]
# Extract the nodes list
nodes = [t[0] for t in input_polygons]
# Get an iterator of all solutions with 3 colours
all_solutions = colour_map(nodes, adjacent_polygons_graph, ncolours=3)
# Print all solutions
for count, solution in enumerate(all_solutions, start=1):
print('%2d: %s' % (count, solution))
output
1: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 1), (7, 0), (8, 1)]
2: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 1), (7, 0), (8, 0)]
3: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 1), (7, 2), (8, 1)]
4: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 1), (7, 2), (8, 0)]
5: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 0), (7, 2), (8, 0)]
6: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 0), (7, 1), (8, 0)]
7: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 0), (7, 2), (8, 1)]
8: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 2), (5, 1), (6, 0), (7, 1), (8, 1)]
9: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 1), (5, 2), (6, 0), (7, 1), (8, 1)]
10: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 1), (5, 2), (6, 0), (7, 1), (8, 0)]
11: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 1), (5, 2), (6, 0), (7, 2), (8, 0)]
12: [(0, 0), (1, 1), (2, 1), (3, 2), (4, 1), (5, 2), (6, 0), (7, 2), (8, 1)]
13: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 2), (7, 0), (8, 0)]
14: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2), (5, 1), (6, 0), (7, 2), (8, 0)]
15: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 0), (7, 2), (8, 0)]
16: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2), (5, 1), (6, 0), (7, 1), (8, 0)]
17: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 0), (7, 1), (8, 0)]
18: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 2), (7, 1), (8, 0)]
19: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 2), (7, 1), (8, 2)]
20: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 2), (7, 0), (8, 2)]
21: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2), (5, 1), (6, 0), (7, 2), (8, 2)]
22: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 0), (7, 2), (8, 2)]
23: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 2), (5, 1), (6, 0), (7, 1), (8, 2)]
24: [(0, 0), (1, 1), (2, 2), (3, 1), (4, 1), (5, 2), (6, 0), (7, 1), (8, 2)]
If you just want a single solution, replace the last for loop with
output_polygons = next(all_solutions)
print(output_polygons)
FWIW, here's some code that can be used to create a diagram from the graph data.
# Create a Graphviz DOT file from graph data
colors = {
None: 'white',
0: 'red', 1: 'yellow',
2: 'green', 3: 'blue'
}
def graph_to_dot(nodes, edges, outfile=sys.stdout):
outfile.write('strict graph test{\n')
outfile.write(' node [style=filled];\n')
for n, v in nodes:
outfile.write(' {} [fillcolor={}];\n'.format(n, colors[v]))
outfile.write('\n')
for u, v in edges:
outfile.write(' {} -- {};\n'.format(u, v))
outfile.write('}\n')
You can call it like this:
# Produce a DOT file of the colored graph
with open('graph.dot', 'w') as f:
graph_to_dot(output_polygons, adjacent_polygons_graph, f)
It can also be used to produce a DOT file of the input_polygons.
To generate a PNG image file from the DOT file using the Graphviz neato utility, you can do this (in Bash).
neato -Tpng -ograph.png graph.dot
Here's the DOT file for the first solution above:
strict graph test{
node [style=filled];
0 [fillcolor=red];
1 [fillcolor=yellow];
2 [fillcolor=yellow];
3 [fillcolor=green];
4 [fillcolor=green];
5 [fillcolor=yellow];
6 [fillcolor=yellow];
7 [fillcolor=red];
8 [fillcolor=yellow];
0 -- 1;
0 -- 2;
0 -- 3;
0 -- 4;
0 -- 5;
1 -- 0;
2 -- 0;
2 -- 3;
3 -- 0;
3 -- 2;
3 -- 6;
3 -- 8;
4 -- 0;
4 -- 5;
4 -- 6;
5 -- 0;
5 -- 4;
6 -- 3;
6 -- 4;
6 -- 7;
7 -- 6;
8 -- 3;
}
And here's the PNG: