Related
As the title states, I've implemented the N-Queens problem for all soulutions but there is one problem, it prints an empty board. I saw a solution only where we put the printboard in an if statement but without a return in printboard i fail to see how it works`
#N Queens through Backtracking with all solutions
def initialize(n):
for key in ['queen','row','col','NWtoSE','SWtoNE']:
board[key] = {}
for i in range(n):
board['queen'][i] = -1
board['row'][i] = 0
board['col'][i] = 0
for i in range (2*n-1):
#Sum of NW to SE diagonal add to constant i.e (i+j) = const
board['SWtoNE'][i] = 0
for i in range (-(n-1), n):
#Difference of SW to NE diagonal is constant i.e (j-i) = const
board['NWtoSE'][i] = 0
def isAvailable(i, j):
return (board['row'][i] == 0 and board['col'][j] == 0 and
board['NWtoSE'][j-i] == 0 and board['SWtoNE'][j+i] == 0)
def addQueen(i, j):
board['queen'][i] = j
board['row'][i] = 1
board['col'][j] = 1
board['NWtoSE'][j-i] = 1
board['SWtoNE'][j+i] = 1
def undo(i, j):
board['queen'][i] = -1
board['row'][i] = 0
board['col'][j] = 0
board['NWtoSE'][j-i] = 0
board['SWtoNE'][j+i] = 0
def printboard():
for i in board['queen'].keys():
print((i, board['queen'][i]), end = " ")
print()
def placequeen(i):
n = len(board['queen'].keys())
for j in range(n):
if(isAvailable(i, j)):
addQueen(i, j)
if i == n-1:
printboard()
else:
placequeen(i+1)
undo(i,j)
board = {}
n = int(input("Enter number of queens : "))
initialize(n)
printboard()
The following is the solution which I don't understand
if placequeen(0): printboard()
I try to optimize a simple python algorithm I made that approximately solve the Traveling Salesman Problem :
import math
import random
import matplotlib.pyplot as plt
import datetime
#Distance between two point
def distance(point1, point2):
return math.sqrt((point2[0]-point1[0])**2+(point2[1]-point1[1])**2)
#TSP TimeTraveler Algorithm
def TSP_TimeTraveler(Set_Points):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points
points = Set_Points.copy()
route = []
#Take 3 points at random
route.append(points.pop(random.randint(0,len(points)-1)))
route.insert(0,points.pop(random.randint(0,len(points)-1)))
route.insert(1,points.pop(random.randint(0,len(points)-1)))
#Calulating the initial route length
Length = distance(route[0],route[1]) + distance(route[1],route[-1]) + distance(route[-1],route[0])
#Time Traveler Algorithm
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
#Take a random point from the Set
point = points.pop(random.randint(0,len(points)-1))
###############################################################################################################
#### Finding the closest route segment by calculation all lengths posibilities and finding the minimum one ####
###############################################################################################################
Set_Lengths = []
for i in range(1,len(route)):
#Set of Lengths when the point is on each route segment except the last one
L = Length - distance(route[i-1],route[i]) + distance(route[i-1],point) + distance(point, route[i])
Set_Lengths.append((i,L))
#Adding the last length when the point is on the last segement
L = Length - distance(route[-1],route[0]) + distance(route[-1],point) + distance(point, route[0])
Set_Lengths.append((0,L))
###############################################################################################################
###############################################################################################################
#Sorting the set of lengths
Set_Lengths.sort(key=lambda k: k[1])
#Inserting the point on the minimum length segment
route.insert(Set_Lengths[0][0], point)
#Updating the new route length
Length = Set_Lengths[0][1]
#Connecting the start point with the finish point
route.append(route[0])
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
return route
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 2520
#Generating a set of random 2D points
points = []
for i in range(size):
points.append([random.uniform(0, 100),random.uniform(0, 100)])
#Solve TSP
route = TSP_TimeTraveler(points)
#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
The algorithm operate in 3 simple steps :
1/ First step I take 3 points at random from the points set and connect them as the initial route.
2/ Then each next step, I take a point at random from the set of points left. And try to find the closest segment of the route i have and connect it to it.
3/ I keep repeating step 2/ until the set of points left is empty.
Here is a gif of how the algorithm solve a set of 120 points : TimeTravelerAlgorithm.gif
I give it the name "Time Traveler" because it's operate like a greedy salesman algorithm. But instead traveling to the closest new city in the present, the greedy salesman time travel to the past to the closest city he had already visited and go visit that new city then continue his normal route.
The time traveler start a route of 3 cities, and the traveler add a new city each step in his past, until he reach a present where he visited all the cities and returned to his home city.
The algorithm give decent solutions fast for small set of points. Here is the execution time for each number of sets, all are made on a 2.6GHz dual-core Intel Core i5 processor Macbook :
120 points in around 0.03 secs
360 points in around 0.23 secs
2520 points in around 10 secs
10 000 points in around 3 mins
100 000 points in around 5 hours (Solution Map)
The algorithm is far from being optimized, because in some cases it gives cross routes which is suboptimal. And It's all made in pure python. Maybe using numpy or some advance library or even GPU can speed up the program.
I want your review and help on how to optimize it. I try to approximately solve without cross routes for set of points that can be extremely large (from 1 million to 100 billions points).
PS: My algorithm and codes are open. People from internet, feel free to use it in any project or any research you have.
Thanks for the comments. I re-implemented the algorithm using Objects, Sets and Linked list. I also removed the square root from distance function . Now the code look more clean :
import math
import random
import datetime
import matplotlib.pyplot as plt
#Distance between two point
def distance(point1, point2):
return (point2[0]-point1[0])**2 + (point2[1]-point1[1])**2
#Distance between two point
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.nextval = None
class TSP_TimeTraveler():
def __init__(self, dataval=None):
self.count = 0
self.position = None
self.length = 0
def get_position():
return self.position
def next_city():
self.position = self.position.nextval
return self.position
#adding a city to the current route with Time Traveler Algorithm :
def add_city(self, point):
node = Node(point)
if self.count <=0 :
self.position = node
elif self.count == 1 :
node.nextval = self.position
self.position.nextval = node
self.length = 2*distance(self.position.dataval,node.dataval)
else :
#Creating the traveler
traveler = self.position
c = traveler.dataval #current position
n = traveler.nextval.dataval #next position
#Calculating the length of adding the city to the path
Min_L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)
Min_Node = traveler
traveler = traveler.nextval
while traveler != self.position :
c = traveler.dataval #current position
n = traveler.nextval.dataval #next position
#Calculating the length of adding the city to the path
L = self.length-distance(c,n)+distance(c,node.dataval)+distance(node.dataval,n)
#Searching the path to the of city with minimum length
if L < Min_L :
Min_L = L
Min_Node = traveler
traveler = traveler.nextval
#Adding the city to the minimum path
node.nextval = Min_Node.nextval
Min_Node.nextval = node
self.length = Min_L
#Incrementing the number of city in the route
self.count = self.count + 1
#Get the list of the route
def getRoute(self):
result = []
traveler = self.position
result.append(traveler.dataval)
traveler = traveler.nextval
while traveler != self.position :
result.append(traveler.dataval)
traveler = traveler.nextval
result.append(traveler.dataval)
return result
def Solve(self, Set_points):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points list
points = Set_points.copy()
#Transform the list into set
points = set(tuple(i) for i in points)
#Add
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
point = points.pop()
self.add_city(point)
result = self.getRoute()
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
return result
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 120
#Generating a set of random 2D points
points = []
for i in range(size):
points.append((random.uniform(0, 100),random.uniform(0, 100)))
#Solve TSP
TSP = TSP_TimeTraveler()
route = TSP.Solve(points)
#Plot the solution
plt.scatter(*zip(*points),s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
And using PyPy instead of normal python it runs alot faster :
120 in around 0.03sec
360 in around 0.05sec
2520 in around 0.22sec
10 000 in around 2sec
100 000 in around 7min
The 100 000 case that took before 5 hours, now it's solved in 7min.
Next, I will try to implement a 2-opt with double linked list and KD-tree. So it can solve for large sets without crosses.
I improved the algorithm by adding double linked list and 2-opt at each insertion :
import math
import random
import datetime
import matplotlib.pyplot as plt
#Distance between two point
def distance(point1, point2):
return (point2[0]-point1[0])**2 + (point2[1]-point1[1])**2
#Intersection between two segments
def intersects(p1, q1, p2, q2):
def on_segment(p, q, r):
if r[0] <= max(p[0], q[0]) and r[0] >= min(p[0], q[0]) and r[1] <= max(p[1], q[1]) and r[1] >= min(p[1], q[1]):
return True
return False
def orientation(p, q, r):
val = ((q[1] - p[1]) * (r[0] - q[0])) - ((q[0] - p[0]) * (r[1] - q[1]))
if val == 0 : return 0
return 1 if val > 0 else -1
o1 = orientation(p1, q1, p2)
o2 = orientation(p1, q1, q2)
o3 = orientation(p2, q2, p1)
o4 = orientation(p2, q2, q1)
if o1 != o2 and o3 != o4:
return True
if o1 == 0 and on_segment(p1, q1, p2) : return True
if o2 == 0 and on_segment(p1, q1, q2) : return True
if o3 == 0 and on_segment(p2, q2, p1) : return True
if o4 == 0 and on_segment(p2, q2, q1) : return True
return False
#Distance Double Linked Node
class Node:
def __init__(self, dataval=None):
self.dataval = dataval
self.prevval = None
self.nextval = None
class TSP_TimeTraveler():
def __init__(self):
self.count = 0
self.position = None
self.length = 0
self.traveler = None
self.travelert_past = None
self.is_2opt = True
def get_position():
return self.position
def traveler_init(self):
self.traveler = self.position
self.travelert_past = self.position.prevval
return self.traveler
def traveler_next(self):
if self.traveler.nextval != self.travelert_past:
self.travelert_past = self.traveler
self.traveler = self.traveler.nextval
return self.traveler, False
else :
self.travelert_past = self.traveler
self.traveler = self.traveler.prevval
return self.traveler, True
#adding a city to the current route with Time Traveler Algorithm :
def add_city(self, point):
node = Node(point)
if self.count <=0 :
self.position = node
elif self.count == 1 :
node.nextval = self.position
node.prevval = node
self.position.nextval = node
self.position.prevval = self.position
self.length = 2*distance(self.position.dataval,node.dataval)
elif self.count == 2 :
node.nextval = self.position.nextval
node.prevval = self.position
self.position.nextval.prevval = node
self.position.nextval = node
self.length = 2*distance(self.position.dataval,node.dataval)
else :
#Creating the traveler
traveler = self.traveler_init()
c = traveler #current position
prev = False #inverse link
n, prev = self.traveler_next()
#Calculating the length of adding the city to the path
Min_prev = prev
Min_L = self.length-distance(c.dataval,n.dataval)+distance(c.dataval,node.dataval)+distance(node.dataval,n.dataval)
Min_Node = c
traveler = n
while traveler != self.position :
c = n #current position
n, prev = self.traveler_next()
#Calculating the length of adding the city to the path
L = self.length-distance(c.dataval,n.dataval)+distance(c.dataval,node.dataval)+distance(node.dataval,n.dataval)
#Searching the path to the of city with minimum length
if L < Min_L :
Min_prev = prev
Min_L = L
Min_Node = c
traveler = n
if Min_prev :
Min_Next_Node = Min_Node.prevval
else :
Min_Next_Node = Min_Node.nextval
node.nextval = Min_Next_Node
node.prevval = Min_Node
if Min_prev :
Min_Node.prevval = node
else :
Min_Node.nextval = node
if Min_Next_Node.nextval == Min_Node:
Min_Next_Node.nextval = node
else :
Min_Next_Node.prevval = node
self.length = Min_L
#2-OP
if self.is_2opt == True :
self._2opt(Min_Node, node, Min_Next_Node)
#Incrementing the number of city in the route
self.count = self.count + 1
#apply the 2opt to a-b-c
def _2opt(self, a, b, c):
traveler = self.traveler_init()
c1 = a
c2 = b
n1 = b
n2 = c
c = traveler #current position
t_prev = False
n, t_prev = self.traveler_next()
traveler = n
while traveler != self.position :
cross = False
if (c.dataval != c1.dataval and c.dataval != c2.dataval and n.dataval != c1.dataval and n.dataval != c2.dataval) and intersects(c.dataval, n.dataval, c1.dataval, c2.dataval):
self._2optswap(c,n,c1,c2)
cross = True
a = n
n = c1
c2 = a
if (c.dataval != n1.dataval and c.dataval != n2.dataval and n.dataval != n1.dataval and n.dataval != n2.dataval) and intersects(c.dataval, n.dataval, n1.dataval, n2.dataval):
self._2optswap(c,n,n1,n2)
cross = True
a = n
n = n1
n2 = a
if cross:
return
c = n #current position
n, t_prev = self.traveler_next()
traveler = n
#swap between the crossed segment a-b and c-d
def _2optswap(self, a, b, c, d):
if a.nextval == b :
a.nextval = c
else :
a.prevval = c
if b.prevval == a :
b.prevval = d
else :
b.nextval = d
if c.nextval == d :
c.nextval = a
else :
c.prevval = a
if d.prevval == c :
d.prevval = b
else :
d.nextval = b
self.length = self.length - distance(a.dataval,b.dataval) - distance(c.dataval,d.dataval) + distance(a.dataval,c.dataval) + distance(b.dataval,d.dataval)
#Get the list of the route
def getRoute(self):
result = []
traveler = self.traveler_init()
result.append(traveler.dataval)
traveler, prev = self.traveler_next()
while traveler != self.position :
result.append(traveler.dataval)
traveler, prev = self.traveler_next()
result.append(traveler.dataval)
return result
def Solve(self, Set_points, with_2opt = True):
print("Solving TSP")
#For calculating execution time
time_start = datetime.datetime.now()
#Copy the set points list
points = Set_points.copy()
#Transform the list into set
points = set(tuple(i) for i in points)
#Add
while len(points)>0 :
print("Points left : ", len(points),' ', end="\r")
point = points.pop()
self.add_city(point)
result = self.getRoute()
#For calculating execution time
time_end = datetime.datetime.now()
delta = (time_end-time_start).total_seconds()
L=0
for i in range(len(result)-1):
L = L + math.sqrt((result[i-1][0]-result[i][0])**2 + (result[i-1][1]-result[i][1])**2)
print("Points left : ", len(points),' Done ',)
print("Execution time : ", delta, "secs")
print("Average time per point : ", 1000*delta/len(Set_points), "msecs")
print("Length : ", L)
return result
#######################
#Testing the Algorithm#
#######################
#Size of the set
size = 1000
#Generating a set of random 2D points
points = []
for i in range(size):
points.append((random.uniform(0, 100),random.uniform(0, 100)))
#Solve TSP
TSP = TSP_TimeTraveler()
route = TSP.Solve(points, with_2opt = True)
plt.scatter(*zip(*route), s=5)
plt.plot(*zip(*route))
plt.axis('scaled')
plt.show()
Now the solution give fast results with no cross routes.
With PyPy it solves 100,000 points with no cross routes in 30 min.
Now Im working on implementing the KD-tree to solve for large sets.
I'm trying to do the polish notation challenge on kattis.com. Thing is, I feel I have done everything they asked for and I've tried fixing everything I could think of. I even looked up some other's solutions and while theirs are more clean I want to continue on mine as I am learning.
Why is it that for example this person's code works but not mine?
Here is my current code:
import sys
case = 1
valid_ints = set([str(i) for i in range(-10,11)])
def simplify(index, myLine, processed):
while index+1 > 0:
if (myLine[index] == "+" or myLine[index] == "-" or myLine[index] == "*") and index < len(myLine)-2:
if myLine[index+1] in valid_ints and myLine[index+2] in valid_ints:
try:
processed = myLine[index+3:] + processed
a = str(myLine[index+1] + myLine[index] + myLine[index+2])
processed.insert(0, str(eval(a)))
del myLine[index:]
except:
processed = [myLine[index], myLine[index+1], myLine[index+2]] + processed
del myLine[index:]
elif len(myLine) < 3:
processed = myLine + processed
del myLine[index]
index -= 1
processed = myLine + processed
return processed
for line in sys.stdin:
myLine = line.split()
processed = []
index = len(myLine)-1
savedprocessed = []
processed = simplify(index, myLine, processed)
while True:
if savedprocessed == processed:
break
else:
savedprocessed = []
savedprocessed += processed
processed = simplify(len(processed)-1, processed, [])
result = " ".join(savedprocessed)
print("Case " + str(case) + ": " + result)
case += 1
if case > 5:
break
You're bringing some other language style to Python, that's unnecessary because Python is more flexible.
I've simplified as much as I can here.
Split the input string on white spaces and iterate over the tokens.
For every operator in the expression, push a list onto the stack and append the operator and its operands to the list.
Now pop each list off the stack and process the list
def simplify(exp):
stack1 = []
ops = set('+*-')
for token in exp.split():
if token in ops:
stack1.append([])
stack1[-1].append(token)
stack2 = []
while stack1:
top = stack1.pop()
while len(top) < 3 and stack2:
top.append(stack2.pop())
if any(x.isalpha() for x in top):
simplified = ' '.join(top)
else:
top[0], top[1] = top[1], top[0]
simplified = str(eval(''.join(top)))
stack2.append(simplified)
return simplified
exp = '* - 6 + x -6 - - 9 6 * 0 c'
print(exp)
simplify(exp)
Output;
* - 6 + x -6 - - 9 6 * 0 c
* - 6 + x -6 - - 3 * 0 c
I just create benchmark to compare speed of two implementation of Quick sort.
Iterative and recursion.
I expected than recursive will be slower, but I got that plot (blue is rec):
It's possible that recursion is faster? Maybe I just do some mistake in my code?
Just in case I pase my code.
import time
import random
import sys
arrayList = []
arr = [random.randint(1,15000) for _ in range(1000)]
numbersList = [100000, 300000, 500000, 900000, 1000000, 1500000]
numbersForBenchmark = []
for i in range(len(numbersList)):
arr = [random.randint(1,15000) for _ in range(numbersList[i])]
numbersForBenchmark.append(arr)
print(numbersForBenchmark)
recursionTimeArray = []
iterationTimeArray = []
arrRe = arr
arrIt = arr
def partition(lst, start, end):
pos = start
for i in range(start, end):
if lst[i] < lst[end]: # in your version it always goes from 0
lst[i],lst[pos] = lst[pos],lst[i]
pos += 1
lst[pos],lst[end] = lst[end],lst[pos] # you forgot to put the pivot
# back in its place
return pos
def quick_sort_recursive(lst, start, end):
if start < end: # this is enough to end recursion
pos = partition(lst, start, end)
quick_sort_recursive(lst, start, pos - 1)
quick_sort_recursive(lst, pos + 1, end)
#print(lst)
def iter(arr,l,h):
i = ( l - 1 )
x = arr[h]
for j in range(l , h):
if arr[j] <= x:
# increment index of smaller element
i = i+1
arr[i],arr[j] = arr[j],arr[i]
arr[i+1],arr[h] = arr[h],arr[i+1]
return (i+1)
def quickSortIterative(arr,l,h):
size = h - l + 1
stack = [0] * (size)
top = -1
top = top + 1
stack[top] = l
top = top + 1
stack[top] = h
while top >= 0:
# Pop h and l
h = stack[top]
top = top - 1
l = stack[top]
top = top - 1
p = iter( arr, l, h )
if p-1 > l:
top = top + 1
stack[top] = l
top = top + 1
stack[top] = p - 1
if p+1 < h:
top = top + 1
stack[top] = p + 1
top = top + 1
stack[top] = h
for i in range(len(numbersForBenchmark)):
arrRe = numbersForBenchmark[i][:]
arrIt = numbersForBenchmark[i][:]
n = len(arrIt)
start = time.time()
quickSortIterative(arrIt, 0, n-1)
end = time.time()
ITime = end - start
iterationTimeArray.append(ITime)
try:
n = len(arrRe)
start = time.time()
quick_sort_recursive(arrRe,0,n-1)
end = time.time()
rekTime = end - start
recursionTimeArray.append(rekTime)
except RecursionError as re:
print('Sorry but this maze solver was not able to finish '
'analyzing the maze: {}'.format(re.args[0]))
print("REK time", recursionTimeArray)
print("ITER TIME", iterationTimeArray)
# evenly sampled time at 200ms intervals
import matplotlib.pyplot as plt
plt.plot([10,100,500,1000,5000,8000 ], recursionTimeArray,[10,100,500,1000,5000,8000], iterationTimeArray)
plt.show()
The plots look OK, but I expected a completely different result. Hence my doubts about the results.
Now, finding the shortest sequence of flips in pancake sorting is alone NP-hard, yet I'd like to find each and all of them, and count them.
Meaning for each permutation I'd like to find all the sequences of prefix reversals that restores the identity but not longer than the shortest one.
Here's what I've got so far:
#!/bin/env python3
# coding: utf-8
from math import factorial
import itertools
from multiprocessing import cpu_count, Manager, Pool
import numpy
import scipy.sparse
def flip(x, value):
return tuple(value[:x][::-1] + value[x:])
def rank(perm):
n = len(perm)
fact = factorial(n)
r = 0
for i in range(n):
fact //= n - i
r += len([x for x in perm[i:] if x < perm[i]]) * fact
return r
def unrank(i, items):
its = items[:]
perm = []
n = len(items)
fact = factorial(n)
r = i % fact
while its:
fact //= n
c, r = divmod(r, fact)
perm.append(its.pop(c))
n -= 1
return tuple(perm)
def get_colex_row(r, n, _fact):
row = scipy.sparse.dok_matrix((
1, _fact[n - 1]), dtype=numpy.int8)
perm = unrank(r, [i for i in range(n)])
for i in range(n):
column = r - r % _fact[i] + rank(perm[:-i - 2:-1])
row[0, column] = i + 1
return row
def get_colex_matrix(n):
fact = [factorial(i) for i in range(1, n + 1)]
m = scipy.sparse.dok_matrix(
(fact[n - 1], fact[n - 1]), dtype=numpy.int8)
items = [_ for _ in range(1, n + 1)]
for r in range(fact[n - 1]):
row = get_colex_row(r, n, fact)
m[r] = row
return m
def get_distance(n, items):
nfact = factorial(n)
stack = {unrank(i, items) for i in range(nfact)}
m = get_colex_matrix(n)
distance = {unrank(nfact - 1, items)[::-1] : 0}
new_distance = {nfact - 1}
d = 0
while distance.keys() != stack:
new_new_distance = set()
d += 1
for visiting in new_distance:
for i in range(2, n + 1):
key_index = m[visiting].tolist().index(i)
key = unrank(key_index, items)[::-1]
if key not in distance:
distance[key] = d
new_new_distance.add(key_index)
new_distance = new_new_distance
return distance
def get_paths_serial(items):
n = len(items)
nfact = factorial(n)
stack = {unrank(i, items) for i in range(nfact)}
m = get_colex_matrix(n)
distance = {unrank(nfact - 1, items)[::-1]: {()}}
new_distance = {nfact - 1}
while distance.keys() != stack:
new_new_distance = set()
for visiting_index in new_distance:
for i in range(2, n + 1):
key_index = m[visiting_index].tolist().index(i)
key = unrank(key_index, items)[::-1]
visiting = unrank(visiting_index, items)[::-1]
paths = distance[visiting]
prev_sample = next(iter(paths))
if key not in distance:
distance[key] = {path + (i,) for path in paths}
new_new_distance.add(key_index)
else:
curr_sample = next(iter(distance[key]))
if len(prev_sample) + 1 < len(curr_sample):
print("Shouldn't happen!")
distance[key] = {path + (i,) for path in paths}
elif len(prev_sample) + 1 == len(curr_sample):
distance[key] |= {path + (i,) for path in paths}
else:
# not relevant
pass
new_distance = new_new_distance
return distance
def _worker(ns, index):
row = get_colex_row(index, ns.n, ns.fact).toarray().tolist()[0]
visiting = unrank(index, ns.items)[::-1]
paths = ns.distance[visiting]
prev_sample = next(iter(paths))
out = {}
my_new_distance = set()
for i in range(2, ns.n + 1):
key_index = row.index(i)
key = unrank(key_index, ns.items)[::-1]
if key not in ns.distance:
out[key] = {path + (i,) for path in paths}
my_new_distance.add(key_index)
else:
curr_sample = next(iter(ns.distance[key]))
if len(prev_sample) + 1 < len(curr_sample):
print("Shouldn't happen!")
out[key] = {path + (i,) for path in paths}
elif len(prev_sample) + 1 == len(curr_sample):
out[key].update(path + (i,) for path in paths)
return my_new_distance, out
def get_paths_parallel(items):
n = len(items)
fact = [factorial(i) for i in range(1, n + 1)]
distance = {unrank(fact[n - 1] - 1, items)[::-1]: {()}}
stack = {unrank(i, items) for i in range(fact[n - 1])}
already_visited = set()
visiting = {fact[n - 1] - 1}
mgr = Manager()
namespace = mgr.Namespace()
namespace.fact = fact
namespace.distance = distance
namespace.items = items
namespace.n = n
with Pool(2 * cpu_count()) as pool:
while distance.keys() != stack:
result = pool.starmap(_worker, ((namespace, job)
for job in visiting))
visiting = set()
for next_to_visit, visited in result:
visiting |= next_to_visit
for k, v in visited.items():
if k in distance:
distance[k] |= v
else:
distance[k] = v
visiting -= already_visited
already_visited |= visiting
namespace.distance = distance
return distance
def colex(value, other):
for i in range(len(value) - 1, 0, -1):
if value[i] == other[i]:
continue
return value[i] > other[i]
return False
def ordered_by(order_cmp):
'Convert a cmp= function into a key= function'
if order_cmp is None:
return None
class K(object):
def __init__(self, obj):
self.value = obj
def __gt__(self, other):
if len(self.value) != len(other.value):
assert "Not the same length"
return order_cmp(self.value, other.value)
return K
def get_ordered(n, order):
return sorted(itertools.permutations(range(1, n + 1)),
key=ordered_by(order))
def get_matrix(n, order=None):
stack = get_ordered(n, order)
m = numpy.zeros((len(stack), len(stack)), numpy.int8)
for i,s in enumerate(stack):
for x in range(1, n + 1):
m[i, stack.index(flip(x, s))] = x
return m
I'm not sure what I'm doing wrong, but get_paths_parallel runs slower than get_paths_serial, please help!
I really should (and probably will soon) document my code better.
So for the time being, I'll say a few additional words:
It uses co-lexicographic ordering to rank the permutations and to find the indices in the adjacency matrix. Where I store the length of the flip that transforms the permutations, e.g. A(i,j) = k if performing a k length prefix reversal on the permutation with rank i results the ranked j permutation. In order to save on memory instead of storing the whole matrix I generate the rows on demand and limit the access by excluding already visited ones also I'm using scipy.sparse.dok_matrix for the same reason.
Other than these it's simply floods the graph till all permutations are reached.
There are some functions that doesn't use all or any of the consideration above like get_matrix, but presented only to validate that others, like get_colex_matrix are working as intended.
I'm creating the key function in a little bit convoluted manner, but that's just because I've tried other sorting before I've settled on co-lex.
Using multiprocessing.Manager to share data between processes makes them slow down.
Solution is to copy the needed data into each process's memory space (passing them as argument) or to use global variables for them.
Also using scipy.sparse.dok_matrix is overkill, dict would do.
I'll grab the literature I've found on the subject and link it hare later.