Finding longest path using recursion with a set of rules - python

For a bit of background, I'm not a computer scientist or programmer at all (studied physics in college where I picked up some python). My problem is to find the longest path through a matrix with a given set of rules. An example matrix would look something like this:
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 1, 1, 0, 0],
[0, 0, 0, 0, 0],
[1, 0, 0, 1, 0],
The rules are as follows:
Start on a given "1" position, these are the valid positions.
Each jump must be to another valid position on the same row or column (it's possible to jump over "0"s).
Consecutive jumps can not be in the same direction (horizontal/vertical) unless jumping to and from a position on-diagonal.
No position can be used twice.
A valid path on the example matrix would look like:
(5,4),(5,1),(3,1),(3,3),(3,2),(2,2)
An invalid path because of rule #3 would look like:
(3,1),(3,2),(3,3)
whereas the following is possible:
(3,1),(3,3),(3,2)
Though I have a bit of experience with python, I've never tried recursion (I'm pretty sure that's how to tackle this), and I can't seem to find any help online that's at my level.

There are several solutions for this. I would suggest first converting the grid to a more object oriented structure, i.e. an undirected graph with nodes for where there are 1s in the input.
Then I would distinguish three kinds of edges in that graph:
Those where one of the end points is on a diagonal ("special" edges)
Those where the above is not true, but nodes are in the same row
Those where the above is not true, but nodes are in the same column
While recurring, you would always consider the special edges, and in addition to that, the edges that are in one of the other two sets of edges (based on the previous direction taken).
Here is an implementation:
class Node:
def __init__(self, y, x, size):
self.x = x
self.y = y
self.coord = (y, x)
self.diagonal = x == y or size - 1 - y
# Separate lists of neighbors: vertical, horizontal.
# Third list is for when this node or neighbor is on diagonal
self.neighbors = [[], [], []]
def addNeighbor(self, node, direction):
self.neighbors[direction].append(node)
class Maze:
def __init__(self, grid):
def addedge(a, b):
direction = 2 if a.diagonal or b.diagonal else int(a.x == b.x)
a.addNeighbor(b, direction)
b.addNeighbor(a, direction)
# alternative grid having Node references:
self.nodes = [[None] * len(grid) for _ in grid]
colNodes = [[] for _ in grid]
for y, row in enumerate(grid):
rowNodes = []
for x, cell in enumerate(row):
if cell: # only create nodes for when there is a 1 in the grid
node = Node(y, x, len(grid))
for neighbor in rowNodes + colNodes[x]:
addedge(node, neighbor)
rowNodes.append(node)
colNodes[x].append(node)
self.nodes[y][x] = node
def findpath(self, y, x):
def recur(node, neighbors):
visited.add(node)
longest = [node.coord]
# always visit "special" neighbors
# (i.e. those on diagonal or all vert/horiz when node is on diagonal)
for neighbor in node.neighbors[2] + neighbors:
if not neighbor in visited:
# toggle direction when going further
path = recur(neighbor, node.neighbors[1-int(node.x == neighbor.x)])
if len(path) >= len(longest):
longest = [node.coord] + path
visited.remove(node)
return longest
node = self.nodes[y][x]
if not node:
raise "Cannot start from that position"
visited = set()
# look in both directions of starting node
return recur(node, node.neighbors[0] + node.neighbors[1])
grid = [
[0, 0, 0, 0, 0],
[0, 1, 0, 0, 0],
[1, 1, 1, 0, 0],
[0, 0, 0, 0, 0],
[1, 0, 0, 1, 0]
]
maze = Maze(grid)
path = maze.findpath(2, 0)
print(path) # output: [(2, 0), (2, 2), (2, 1), (1, 1)]
path = maze.findpath(4, 3)
print(path) # output: [(4, 3), (4, 0), (2, 0), (2, 2), (2, 1), (1, 1)]
Note that the coordinates in this solution are zero-based, so the first row has number 0, ...etc.
See it run on repl.it

You can use recursion with a generator:
d = [[0, 0, 0, 0, 0], [0, 1, 0, 0, 0], [1, 1, 1, 0, 0], [0, 0, 0, 0, 0], [1, 0, 0, 1, 0]]
_d = {1:lambda a, b:'f' if b[-1] > a[-1] else 'b', 0:lambda a, b:'u' if b[0] > a[0] else 'd'}
def paths(start, _dir, c = []):
yield c
_options = [(a, b) for a in range(len(d)) for b in range(len(d[0])) if (a, b) not in c and d[a][b]]
if _options:
for a, b in _options:
if a == start[0] or b == start[-1]:
r = _d[a == start[0]](start, (a, b))
if _dir is None or r != _dir:
yield from paths((a, b), r, c+[(a, b)])
print(max(list(paths((4, 3), None, [(4, 3)])), key=len))
Output:
[(4, 3), (4, 0), (2, 0), (2, 2), (2, 1), (1, 1)]

Related

Move a shape inside a list of lists

I have the following list of lists representing a matrix:
space = [
[0, 1, 1, 0],
[0, 1, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 0],
]
The number 1s represent an upside down L (like a gamma, "Γ"). How can I make this "object" move to the right, left, up and down as if it was a block? I move it with "asdw" keys.
Important: I am not able to use numpy, so thats makes the work way more difficult.
This is my failed method to make a RIGHT direction movement (btw it doesnt make the movement correctly), but i dont think its the best way and dont really think I can escalate it to other movements:
def show_space(space):
for line in space:
print(line)
x = input("Movement: ")
if x == 'd':
for i in range(4):
for j in range(4):
if space[i][j] == 1:
try:
if space[i][j+1] == 0:
space[i][j] = 0
space[i][j+1] = 1
if space[i][j+1] == 1 and space[i][j+2] == 0:
space[i][j] = 0
space[i][j+2] = 1
except IndexError:
pass
show_space(space)
Is there any other method I could try? Or correct the one Im using? Thanks in advance
EDIT:
The gamma not only should be able to move right up down left, it should also be able to move 90 degrees, mirror itself, and all possible shapes that form can take. So if i had to hardcode all possible gamma or L combinations, i would have to hardcode 48 possibilities. I dont know wether hardcoding that is the optimal way to be honest.
Im not saying hardcoding the postions is not acceptable, it could definitely be a solution, but I just dont feel like its the correct way. i may be wrong of course.
What do you think?
Here's how I'd suggest doing something like this:
Calculate all the indices for each gamma shape in the matrix, kept stored in a dictionary with the each corner index tuple as the dictionary keys, then whenever the corner moves, figure out the indices that should be 1s, and assign to a copy of a matrix of zeros.
positions = {(0, 0): ((0, 1), (1, 0), (2, 0)),
(0, 1): ((0, 2), (1, 1), (2, 1)),
(0, 2): ((0, 3), (1, 2), (2, 2)),
(1, 0): ((1, 1), (2, 0), (3, 0)),
(1, 1): ((1, 2), (2, 1), (3, 1)),
(1, 2): ((1, 3), (2, 2), (3, 2))}
def move_gamma(corner):
board = [[0 for _ in range(4)] for _ in range(4)]
try:
for (i, j) in (corner, *positions[corner]):
board[i][j] = 1
return board
except KeyError:
print("You can't move there!")
return board
def flip_h(board):
return [[*reversed(row)] for row in board]
def flip_v(board):
return [*reversed(board)]
def transpose(board):
return [[*t] for t in zip(*board)]
Demo:
In [3]: board = move_gamma((1, 1))
In [4]: print(*board, sep="\n")
[0, 0, 0, 0]
[0, 1, 1, 0]
[0, 1, 0, 0]
[0, 1, 0, 0]
In [5]: board = move_gamma((1, 2))
In [6]: print(*board, sep="\n")
[0, 0, 0, 0]
[0, 0, 1, 1]
[0, 0, 1, 0]
[0, 0, 1, 0]
In [7]: print(*transpose(board), sep="\n")
[0, 0, 0, 0]
[0, 0, 0, 0]
[0, 1, 1, 1]
[0, 1, 0, 0]
Just a heads up, you'd still need to implement the logic for mapping WASD to movement relative to the current corner indices.
If you need to stay within the confines of the standards library, you can use collection.deque, which has a rotate method that does exactly what you need and will have to implement in lists if you can't use deque.
This is what I can offer with deque.
NB. this wraps around the edges which might not be intended,
from collections import deque
space = deque([
deque([0, 1, 1, 0]),
deque([0, 0, 1, 0]),
deque([0, 0, 1, 0]),
deque([0, 0, 0, 0]),
])
def move(mvt):
if mvt == "s":
space.rotate(1)
elif mvt == "w":
space.rotate(-1)
elif mvt == "d":
[x.rotate(1) for x in space]
elif mvt == "a":
[x.rotate(-1) for x in space]
else:
raise NotImplementedError
move("d") # etc
Your answer is in your question - instead of keeping track of all space (array with mostly 0s), use an object (python dict) to instead specify the shape, and where to start drawing it e.g.:
{
'abs_pos': (0,1),
'rel_points': [(0,0), (0,1), (1,0), (2,0)]
}
Then moving it only amounts to updating the location of (e.g.) the upper-left corner abs_pos.
When it's time to print your map of all space, start from abs_pos and then add a dot at each point in rel_points.

How to solve partial Knight's Tour with special constraints

Knight's tour problem described in the image here, with diagram.
A knight was initially located in a square labeled 1. It then proceeded to make a
series of moves, never re-visiting a square, and labeled the visited squares in
order. When the knight was finished, the labeled squares in each region of connected
squares had the same sum.
A short while later, many of the labels were erased. The remaining labels can be seen
above.
Complete the grid by re-entering the missing labels. The answer to this puzzle is
the sum of the squares of the largest label in each row of the completed grid, as in
the example.
[1]: E.g. the 14 and 33 are in different regions.
The picture explains it a lot more clearly, but in summary a Knight has gone around a 10 x 10 grid. The picture shows a 10 x 10 board that shows some positions in has been in, and at what point of its journey. You do not know which position the Knight started in, or how many movements it made.
The coloured groups on the board need to all sum to the same amount.
I’ve built a python solver, but it runs for ages - uses recursion. I’ve noted that the maximum sum of a group is 197, based on there being 100 squares and the smallest group is 2 adjacent squares.
My code at this link: https://pastebin.com/UMQn1HZa
import sys, numpy as np
fixedLocationsArray = [[ 12, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 5, 0, 23, 0],
[ 0, 0, 0, 0, 0, 0, 8, 0, 0, 0],
[ 0, 0, 0, 14, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 2, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 20, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 33, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 28]]
groupsArray = [[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 1, 0, 0, 0, 0,10, 0],
[0, 0, 0, 1, 0, 0, 0, 0,10, 0],
[0, 0, 1, 1, 1, 1, 9,10,10,10],
[2, 0, 1, 0, 0,11, 9, 9, 9, 9],
[2, 0, 0, 0,11,11,11,15,15, 9],
[2, 4, 4,14,11,12,12,15,15, 8],
[2, 3, 4,14,14,13,13,13,15, 8],
[2, 3, 5,14,16,16,16, 7, 7, 8],
[3, 3, 5, 6, 6, 6, 6, 6, 7, 8]]
'''
Solver
- Noted that the maximum sum of a group is 197 since the group of only 2 can have the 100 filled and then 97 on return
'''
class KnightsTour:
def __init__(self, width, height, fixedLocations, groupsArray):
self.w = width
self.h = height
self.fixedLocationsArray = fixedLocations
self.groupsArray = groupsArray
self.npfixedLocationsArray = np.array(fixedLocations)
self.npgroupsArray = np.array(groupsArray)
self.board = [] # Contains the solution
self.generate_board()
def generate_board(self):
"""
Creates a nested list to represent the game board
"""
for i in range(self.h):
self.board.append([0]*self.w)
def print_board(self): # Prints out the final board solution
print(" ")
print("------")
for elem in self.board:
print(elem)
print("------")
print(" ")
def generate_legal_moves(self, cur_pos, n):
"""
Generates a list of legal moves for the knight to take next
"""
possible_pos = []
move_offsets = [(1, 2), (1, -2), (-1, 2), (-1, -2),
(2, 1), (2, -1), (-2, 1), (-2, -1)]
locationOfNumberInFixed = [(ix,iy) for ix, row in enumerate(self.fixedLocationsArray) for iy, i in enumerate(row) if i == n+1]
groupsizeIsNotExcessive = self.groupsNotExcessiveSize(self.board, self.groupsArray)
for move in move_offsets:
new_x = cur_pos[0] + move[0]
new_y = cur_pos[1] + move[1]
new_pos = (new_x, new_y)
if groupsizeIsNotExcessive:
if locationOfNumberInFixed:
print(f"This number {n+1} exists in the fixed grid at {locationOfNumberInFixed[0]}")
if locationOfNumberInFixed[0] == new_pos:
print(f"Next position is {new_pos} and matches location in fixed")
possible_pos.append((new_x, new_y))
else:
continue
elif not locationOfNumberInFixed: # if the current index of move is not in table, then evaluate if it is a legal move
if (new_x >= self.h): # if it is out of height of the board, continue, don't app onto the list of possible moves
continue
elif (new_x < 0):
continue
elif (new_y >= self.w):
continue
elif (new_y < 0):
continue
else:
possible_pos.append((new_x, new_y))
else:
continue
print(f"The legal moves for index {n} are {possible_pos}")
print(f"The current board looks like:")
self.print_board()
return possible_pos
def sort_lonely_neighbors(self, to_visit, n):
"""
It is more efficient to visit the lonely neighbors first,
since these are at the edges of the chessboard and cannot
be reached easily if done later in the traversal
"""
neighbor_list = self.generate_legal_moves(to_visit, n)
empty_neighbours = []
for neighbor in neighbor_list:
np_value = self.board[neighbor[0]][neighbor[1]]
if np_value == 0:
empty_neighbours.append(neighbor)
scores = []
for empty in empty_neighbours:
score = [empty, 0]
moves = self.generate_legal_moves(empty, n)
for m in moves:
if self.board[m[0]][m[1]] == 0:
score[1] += 1
scores.append(score)
scores_sort = sorted(scores, key = lambda s: s[1])
sorted_neighbours = [s[0] for s in scores_sort]
return sorted_neighbours
def groupby_perID_and_sum(self, board, groups):
# Convert into numpy arrays
npboard = np.array(board)
npgroups = np.array(groups)
# Get argsort indices, to be used to sort a and b in the next steps
board_flattened = npboard.ravel()
groups_flattened = npgroups.ravel()
sidx = groups_flattened.argsort(kind='mergesort')
board_sorted = board_flattened[sidx]
groups_sorted = groups_flattened[sidx]
# Get the group limit indices (start, stop of groups)
cut_idx = np.flatnonzero(np.r_[True,groups_sorted[1:] != groups_sorted[:-1],True])
# Create cut indices for all unique IDs in b
n = groups_sorted[-1]+2
cut_idxe = np.full(n, cut_idx[-1], dtype=int)
insert_idx = groups_sorted[cut_idx[:-1]]
cut_idxe[insert_idx] = cut_idx[:-1]
cut_idxe = np.minimum.accumulate(cut_idxe[::-1])[::-1]
# Split input array with those start, stop ones
arrayGroups = [board_sorted[i:j] for i,j in zip(cut_idxe[:-1],cut_idxe[1:])]
arraySum = [np.sum(a) for a in arrayGroups]
sumsInListSame = arraySum.count(arraySum[0]) == len(arraySum)
return sumsInListSame
def groupsNotExcessiveSize(self, board, groups):
# Convert into numpy arrays
npboard = np.array(board)
npgroups = np.array(groups)
# Get argsort indices, to be used to sort a and b in the next steps
board_flattened = npboard.ravel()
groups_flattened = npgroups.ravel()
sidx = groups_flattened.argsort(kind='mergesort')
board_sorted = board_flattened[sidx]
groups_sorted = groups_flattened[sidx]
# Get the group limit indices (start, stop of groups)
cut_idx = np.flatnonzero(np.r_[True,groups_sorted[1:] != groups_sorted[:-1],True])
# Create cut indices for all unique IDs in b
n = groups_sorted[-1]+2
cut_idxe = np.full(n, cut_idx[-1], dtype=int)
insert_idx = groups_sorted[cut_idx[:-1]]
cut_idxe[insert_idx] = cut_idx[:-1]
cut_idxe = np.minimum.accumulate(cut_idxe[::-1])[::-1]
# Split input array with those start, stop ones
arrayGroups = [board_sorted[i:j] for i,j in zip(cut_idxe[:-1],cut_idxe[1:])]
arraySum = [np.sum(a) for a in arrayGroups]
print(arraySum)
# Check if either groups aren't too large
groupSizeNotExcessive = all(sum <= 197 for sum in arraySum)
return groupSizeNotExcessive
def tour(self, n, path, to_visit):
"""
Recursive definition of knights tour. Inputs are as follows:
n = current depth of search tree
path = current path taken
to_visit = node to visit, i.e. the coordinate
"""
self.board[to_visit[0]][to_visit[1]] = n # This writes the number on the grid
path.append(to_visit) #append the newest vertex to the current point
print(f"Added {n}")
print(f"For {n+1} visiting: ", to_visit)
if self.groupby_perID_and_sum(self.board, self.npgroupsArray): #if all areas sum
self.print_board()
print(path)
print("Done! All areas sum equal")
sys.exit(1)
else:
sorted_neighbours = self.sort_lonely_neighbors(to_visit, n)
for neighbor in sorted_neighbours:
self.tour(n+1, path, neighbor)
#If we exit this loop, all neighbours failed so we reset
self.board[to_visit[0]][to_visit[1]] = 0
try:
path.pop()
print("Going back to: ", path[-1])
except IndexError:
print("No path found")
sys.exit(1)
if __name__ == '__main__':
#Define the size of grid. We are currently solving for an 8x8 grid
kt0 = KnightsTour(10, 10, fixedLocationsArray, groupsArray)
kt0.tour(1, [], (3, 0))
# kt0.tour(1, [], (7, 0))
# kt0.tour(1, [], (7,2))
# kt0.tour(1, [], (6,3))
# kt0.tour(1, [], (4,3))
# kt0.tour(1, [], (3,2))
# startingPositions = [(3, 0), (7, 0), (7,2), (6,3), (4,3), (3,2)]
kt0.print_board()
Here are some observations that you could include to be able to stop more early in your backtracking.
First of all remember that for n steps the total sum in all areas can be computed with the formula n(n+1)/2. This number has to be divisible evenly into the groups i.e. it has to be divisible by 17 which is the amount of groups.
Furthermore if we look at the 12 we can conclude that the 11 and 13 must have been in the same area so we get a lower bound on the number for each area as 2+5+8+11+12+13=51.
And lastly we have groups of size two so the largest two step numbers must make up the total sum for one group.
Using those conditions we can calculate the remaining possible amount of steps with
# the total sum is divisible by 17:
# n*(n+1)/2 % 17 == 0
# the sum for each group is at least the predictable sum for
# the biggest given group 2+5+8+11+12+13=51:
# n*(n+1)/(2*17) >= 51
# since there are groups of two elements the sum of the biggest
# two numbers must be as least as big as the sum for each group
# n+n-1 >= n*(n+1)/(2*17)
[n for n in range(101) if n*(n+1)/2 % 17 == 0 and n*(n+1)/(2*17) >= 51 and n+n-1 >= n*(n+1)/(2*17)]
giving us
[50,51].
So the knight must have taken either 50 or 51 steps and the sum for each area must be either 75 or 78.

Breadth First Search falling one index short of desired outcome

I'm trying to implement a bread-first search algorithm in Python to find the shortest path from the top left corner of a matrix (2d list) and the number 9, wherever it may fall in the matrix. For some reason when I run the code below with the included print statements, my x coordinate will outcome correctly (reached index 5 against desired 5). However, my y coordinate continues to be one index short (e.g. need 7 and it stops at 6). Additionally, when I print the queue, it's not empty but for some reason the function still ends. So, I don't understand why the code is exiting if neither criteria has been met (i.e. queue is empty or our desired coordinates have been found).
import numpy as np
from collections import deque
def shortest_path(maze):
num_rows = len(maze)
num_cols = len(maze[0])
row_moves = [1, -1, 0, 0]
col_moves = [0, 0, 1, -1]
# Convert maze to an array and use numpy to find coordinates of our goal, which is the 9.
maze_to_matrix = np.array(maze)
destination = np.where(maze_to_matrix == 9)
destination_x, destination_y = destination[1][0], destination[0][0]
def is_valid_move(matrix, visited, y, x):
return (y >= 0) and (y < num_rows) and (x >= 0) and (x < num_cols) \
and matrix[y][x] == 1 and not visited[y][x]
def bfs(matrix, dest_y, dest_x):
# Construct matrix to keep track of visited cells.
visited = [[False for x in range(num_cols)] for y in range(num_rows)]
# Mark our origin as visited.
# Our origin is always the top left node.
visited[0][0] = True
# Create an empty queue to keep track of our nodes to visit.
queue = deque()
# Append our starting coordinates and its minimum distance from the source to our queue.
# First number is y coordinate, or outer list index.
# Second number is x coordinate, or inner list index.
queue.append((0, 0, 0))
# Store the length of the longest path from source to destination
min_dist = float('inf')
# Pull most recently visited node off queue and determine if neighbouring
# nodes are accessible. Continue until no valid unvisited nodes remain.
while queue:
(y, x, dist) = queue.popleft()
print(f'y: {y} and dest_y: {dest_y}')
print(f'x: {x} and dest_x: {dest_x}')
# If our destination is found then break the loop and return value.
if y == dest_y and x == dest_x:
min_dist = dist
break
# Check for all possible movement directions from current (x, y)
# and add valid movements to our queue.
for i in range(4):
if is_valid_move(matrix, visited, y + row_moves[i], x + col_moves[i]):
visited[y + row_moves[i]][x + col_moves[i]] = True
queue.append((y + row_moves[i], x + col_moves[i], dist + 1))
print(queue)
if min_dist != float('inf'):
return min_dist
else:
return "Desired destination can't be reached from given origin points."
return bfs(maze, destination_y, destination_x)
maze = [
[1, 0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 0, 1, 0],
[0, 1, 0, 1, 0, 1, 0],
[1, 1, 0, 1, 1, 1, 0],
[1, 0, 0, 0, 0, 1, 0],
[1, 1, 1, 1, 0, 1, 0],
[0, 0, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 1, 9, 0]
]
print(shortest_path(maze))
# Outputting y: 6 and dest_y: 7 x: 5 and dest_x: 5 and the queue is full.
Nevermind, I'm an idiot... In my is_valid_move function I was only counting spaces within the matrix where value is 1, which means our goal of 9 won't ever be valid.
So the is_valid_function should look like this:
return (y >= 0) and (y < num_rows) and (x >= 0) and (x < num_cols) \
and (matrix[y][x] == 1 or matrix[y][x] == 9) and not visited[y][x]

Dijkstra's Algorithm Implementation in Python - How Does it Work?

I can use the following English algorithm to find shortest paths using Dijkstra's Algorithm on paper:
Step 1: Assign permanent label and order to starting node
Step 2: Assign temporary labels to all nodes directly reached by starting node
Step 3: Select the lowest temporary label and make it permanent
Step 4: Assign an order to the node
Step 5: Update and assign temporary labels for nodes directly reached from the new permanent node
Step 6: Repeat steps 3, 4 & 5 until the destination node is made permanent
I have searched for a Python implementation and many are quite complex or use data structures I'm not familiar with. Eventually I found the one below. I have spent quite some time tracing it's execution in a Python visualizer, and I can get a sense of how it works but it has not yet clicked for me.
Could someone please explain how the code relates to the English algorithm? For example, how does the notion of "predecessors" relate to the "permanent labels" in the English version?
from math import inf
graph = {'a':{'b':10,'c':3},'b':{'c':1,'d':2},'c':{'b':4,'d':8,'e':2},'d':{'e':7},'e':{'d':9}}
def dijkstra(graph,start,goal):
shortest_distance = {}
predecessor = {}
unseenNodes = graph
infinity = inf
path = []
for node in unseenNodes:
shortest_distance[node] = infinity
shortest_distance[start] = 0
# Determine which is minimum node. What does that mean?
while unseenNodes:
minNode = None
for node in unseenNodes:
if minNode is None:
minNode = node
elif shortest_distance[node] < shortest_distance[minNode]:
minNode = node
for edge, weight in graph[minNode].items():
if weight + shortest_distance[minNode] < shortest_distance[edge]:
shortest_distance[edge] = weight + shortest_distance[minNode]
predecessor[edge] = minNode
unseenNodes.pop(minNode)
currentNode = goal
while currentNode != start:
try:
path.insert(0,currentNode)
currentNode = predecessor[currentNode]
except KeyError:
print('Path not reachable')
break
path.insert(0,start)
if shortest_distance[goal] != infinity:
print('Shortest distance is ' + str(shortest_distance[goal]))
print('And the path is ' + str(path))
dijkstra(graph, 'a', 'b')
Dijkstra’s algorithm same to prim’s algorithm for minimum spanning tree. Like Prim’s MST, we generate a shortest path tree with given source as root. We maintain two sets, one set contains vertices included in shortest path tree, other set includes vertices not yet included in shortest path tree. At every step of the algorithm, we find a vertex which is in the other set (set of not yet included) and has a minimum distance from the source.
import sys
class Graph():
def __init__(self, vertices):
self.V = vertices
self.graph = [[0 for column in range(vertices)]
for row in range(vertices)]
def printSolution(self, dist):
print("Vertex tDistance from Source")
for node in range(self.V):
print(node, "t", dist[node])
def minDistance(self, dist, sptSet):
min = sys.maxint
for v in range(self.V):
if dist[v] < min and sptSet[v] == False:
min = dist[v]
min_index = v
return min_index
def dijkstra(self, src):
dist = [sys.maxint] * self.V
dist[src] = 0
sptSet = [False] * self.V
for cout in range(self.V):
u = self.minDistance(dist, sptSet)
sptSet[u] = True
for v in range(self.V):
if self.graph[u][v] > 0 and sptSet[v] == False and \
dist[v] > dist[u] + self.graph[u][v]:
dist[v] = dist[u] + self.graph[u][v]
self.printSolution(dist)
g = Graph(9)
g.graph = [[0, 4, 0, 0, 0, 0, 0, 8, 0],
[4, 0, 8, 0, 0, 0, 0, 11, 0],
[0, 8, 0, 7, 0, 4, 0, 0, 2],
[0, 0, 7, 0, 9, 14, 0, 0, 0],
[0, 0, 0, 9, 0, 10, 0, 0, 0],
[0, 0, 4, 14, 10, 0, 2, 0, 0],
[0, 0, 0, 0, 0, 2, 0, 1, 6],
[8, 11, 0, 0, 0, 0, 1, 0, 7],
[0, 0, 2, 0, 0, 0, 6, 7, 0]]
g.dijkstra(0)

Count possibilites for single crossovers

I have the following problem. In my code
I have arrays of size L. The entries are either 0 or 1 for all arrays. Lets set L=3 for an example. Three possible arrays are (1,1,1), (1,0,0) and (0,1,1). Now I want to know how many single crossovers are possible with (1,0,0) and (0,1,1) to form (1,1,1). The answer would be one. For (1,0,1) and (0,1,0) to form (1,1,1) the answer would be 0, because I would need two crossovers. I am looking for an algorithm which does this for general L. (L is usually not larger than 9). So far I don't have any idea and that is why I have post this problem her but I will make an edit if I got one. Hope you can help me :)
Edit: The outcome can be of course not only 0 or 1 but also greater than 1.
Example: (1,1,0,0) and (0,0,0,0) to form (0,0,0,0) the outcome would be 2. (I can only take the last entry of the first array or the last 2 entries of the first array)
Edit 2: By single crossover I mean, that I can take the left/right side of the first sequence and the right/left side of the second sequence to form the given third sequence. (1,1,0,0) and (0,0,0,0) to form (0,0,0,0) --> 0,0) + (0,0, or 0) + (0,0,0,
Another way of interpreting the problem is as calculating the Hamming Distance. Here is a snippet to create a dictionary of all pairs of each Hamming Distance/crossovers.
from itertools import combinations
tuples = [(0, 0, 1), (1, 0, 0), (1, 0, 1)]
crossovers = {k: [] for k in range(len(tuples[0]))}
for a, b in combinations(tuples, r=2):
num_crossovers = sum(el1 != el2 for el1, el2 in zip(a, b))
crossovers[num_crossovers].append((a, b))
After executing crossovers will be as follows
{0: [],
1: [((0, 0, 1), (1, 0, 1)), ((1, 0, 0), (1, 0, 1))],
2: [((0, 0, 1), (1, 0, 0))]}
EDIT:
I missed that you were using numpy arrays instead of tuples, you could do
arrays = np.array([[0, 0, 1], [1, 0, 0], [1, 0, 1]])
crossovers = {k: [] for k in range(arrays[0].size)}
for a, b in combinations(arrays, r=2):
num_crossovers = sum(np.abs(a - b))
crossovers[num_crossovers].append((a, b))

Categories