Prim's Algorithm input parameter (values) in Python - python

I have looked at the following prim's algorithm (in order to create a minimum spanning tree) and I am unsure as to what the input value s in the following code is, I think the G of course would be the graph sent (adjacency matrix or list graphs) and I think the value s is where the start should be? Also if it is the start then in what way would you send a starting value to the following algorithm?:
from heapq import heappop, heappush
def prim(self, G, s):
P, Q = {}, [(0, None, s)]
while Q:
_, p, u = heappop(Q)
if u in P: continue
P[u] = p
for v, w in G[u].items():
heappush(Q, (w, u, v))
return P
Any help will be much appreciated, thank you!

Here you are:
#A = adjacency matrix, u = vertex u, v = vertex v
def weight(A, u, v):
return A[u][v]
#A = adjacency matrix, u = vertex u
def adjacent(A, u):
L = []
for x in range(len(A)):
if A[u][x] > 0 and x <> u:
L.insert(0,x)
return L
#Q = min queue
def extractMin(Q):
q = Q[0]
Q.remove(Q[0])
return q
#Q = min queue, V = vertex list
def decreaseKey(Q, K):
for i in range(len(Q)):
for j in range(len(Q)):
if K[Q[i]] < K[Q[j]]:
s = Q[i]
Q[i] = Q[j]
Q[j] = s
#V = vertex list, A = adjacency list, r = root
def prim(V, A, r):
u = 0
v = 0
# initialize and set each value of the array P (pi) to none
# pi holds the parent of u, so P(v)=u means u is the parent of v
P=[None]*len(V)
# initialize and set each value of the array K (key) to some large number (simulate infinity)
K = [999999]*len(V)
# initialize the min queue and fill it with all vertices in V
Q=[0]*len(V)
for u in range(len(Q)):
Q[u] = V[u]
# set the key of the root to 0
K[r] = 0
decreaseKey(Q, K) # maintain the min queue
# loop while the min queue is not empty
while len(Q) > 0:
u = extractMin(Q) # pop the first vertex off the min queue
# loop through the vertices adjacent to u
Adj = adjacent(A, u)
for v in Adj:
w = weight(A, u, v) # get the weight of the edge uv
# proceed if v is in Q and the weight of uv is less than v's key
if Q.count(v)>0 and w < K[v]:
# set v's parent to u
P[v] = u
# v's key to the weight of uv
K[v] = w
decreaseKey(Q, K) # maintain the min queue
return P
A = [ [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]]
V = [ 0, 1, 2, 3, 4, 5, 6, 7, 8 ]
P = prim(V, A, 0)
print P
[None, 0, 5, 2, 3, 6, 7, 0, 2]

G is the graph or the adjacency matrix and s is any random starting node which u can give , it does not matter which of the node you choose

Related

My recursive BFS implemention is not providing the correct answer

def riverSizes(matrix):
rows, cols = len(matrix), len(matrix[0])
visited = set()
res = []
def bfs(row, col, width):
max_width = width
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
for dr, dc in directions:
r, c = row + dr, col + dc
if (r,c) not in visited and r < rows and c < cols and r >= 0 and c >=0 and matrix[r][c] == 1:
visited.add((r,c))
max_width = max(bfs(r, c, width + 1), max_width)
print(max_width)
return max_width
for r in range(rows):
for c in range(cols):
if matrix[r][c] == 1 and (r, c) not in visited:
visited.add((r, c))
val = bfs(r, c, 1)
res.append(val)
return res
Input:
[[1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0],
[1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0],
[0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1],
[1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0],
[1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1]]
My output: [2, 1, 15, 5, 2, 1]
Expected output: [2, 1, 21, 5, 2, 1]
I am concerned that in the case where my recusion branches out in multiple directions, it isn't adding all the additional widths together.
I was helped by friend who corrected me that my approach is actually Depth First Search. I was mistakenly using the max function when instead all I needed to do was increment the width and return the width.
def riverSizes(matrix):
rows, cols = len(matrix), len(matrix[0])
visited = set()
res = []
def dfs(row, col, width):
directions = [(0, 1), (1, 0), (-1, 0), (0, -1)]
for dr, dc in directions:
r, c = row + dr, col + dc
if (r,c) not in visited and r < rows and c < cols and r >= 0 and c >=0 and matrix[r][c] == 1:
visited.add((r,c))
width = dfs(r, c, width + 1)
return width
for r in range(rows):
for c in range(cols):
if matrix[r][c] == 1 and (r, c) not in visited:
visited.add((r, c))
val = dfs(r, c, 1)
res.append(val)
return res

Python networkx edge and node labeling problem

I'm new to Python and I wanted to draw a incidence graph, but with changed labels. I want them to start labeling nodes from '1', not from '0'. Also I want to label edges like this: if It's between node '1' and '2', label It as '12' or '1:2'. Thanks in advance :)
im = np.array([[1, 1, 0, 0, 0, 0], [1, 0, 1, 1, 0, 0], [0, 1, 1, 0, 1, 0], [0, 0, 0, 1, 1, 1], [0, 0, 0, 0, 0, 1]])
am = (np.dot(im, im.T) > 0).astype(int)
np.fill_diagonal(am, 0)
K = nx.from_numpy_matrix(am)
pos = nx.spring_layout(K)
print(pos)
x=1
nx.relabel_nodes(K, lambda x: x + + 1)
nx.draw(K, pos, with_labels=True)
edge_labels = dict(((u, v), d) for u, v, d in K.edges(data=True))
nx.draw_networkx_edge_labels(K, pos, edge_labels = edge_labels, label_pos=0.5)
plt.show()
Then build the label you want, just as you've done so far. I'll break this down to basic opeartions; you can build it up again as you wish:
edge_labels = dict(((u, v),
str(u+1) +":"+ str(v+1) +" "+ str(d))
for u, v, d in K.edges(data=True))

Find Distance to Nearest Zero in NumPy Array

Let's say I have a NumPy array:
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
At each index, I want to find the distance to nearest zero value. If the position is a zero itself then return zero as a distance. Afterward, we are only interested in distances to the nearest zero that is to the right of the current position. The super naive approach would be something like:
out = np.full(x.shape[0], x.shape[0]-1)
for i in range(x.shape[0]):
j = 0
while i + j < x.shape[0]:
if x[i+j] == 0:
break
j += 1
out[i] = j
And the output would be:
array([0, 2, 1, 0, 4, 3, 2, 1, 0, 0])
I'm noticing a countdown/decrement pattern in the output in between the zeros. So, I might be able to do use the locations of the zeros (i.e., zero_indices = np.argwhere(x == 0).flatten())
What is the fastest way to get the desired output in linear time?
Approach #1 : Searchsorted to the rescue for linear-time in a vectorized manner (before numba guys come in)!
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
idx_nz = np.flatnonzero(~mask_z)
# Cover for the case when there's no 0 left to the right
# (for same results as with posted loop-based solution)
if x[-1]!=0:
idx_z = np.r_[idx_z,len(x)]
out = np.zeros(len(x), dtype=int)
idx = np.searchsorted(idx_z, idx_nz)
out[~mask_z] = idx_z[idx] - idx_nz
Approach #2 : Another with some cumsum -
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
# Cover for the case when there's no 0 left to the right
if x[-1]!=0:
idx_z = np.r_[idx_z,len(x)]
out = idx_z[np.r_[False,mask_z[:-1]].cumsum()] - np.arange(len(x))
Alternatively, last step of cumsum could be replaced by repeat functionality -
r = np.r_[idx_z[0]+1,np.diff(idx_z)]
out = np.repeat(idx_z,r)[:len(x)] - np.arange(len(x))
Approach #3 : Another with mostly just cumsum -
mask_z = x==0
idx_z = np.flatnonzero(mask_z)
pp = np.full(len(x), -1)
pp[idx_z[:-1]] = np.diff(idx_z) - 1
if idx_z[0]==0:
pp[0] = idx_z[1]
else:
pp[0] = idx_z[0]
out = pp.cumsum()
# Handle boundary case and assigns 0s at original 0s places
out[idx_z[-1]:] = np.arange(len(x)-idx_z[-1],0,-1)
out[mask_z] = 0
You could work from the other side. Keep a counter on how many non zero digits have passed and assign it to the element in the array. If you see 0, reset the counter to 0
Edit: if there is no zero on the right, then you need another check
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
out = x
count = 0
hasZero = False
for i in range(x.shape[0]-1,-1,-1):
if out[i] != 0:
if not hasZero:
out[i] = x.shape[0]-1
else:
count += 1
out[i] = count
else:
hasZero = True
count = 0
print(out)
You can use the difference between the indices of each position and the cumulative max of zero positions to determine the distance to the preceding zero. This can be done forward and backward. The minimum between forward and backward distance to the preceding (or next) zero will be the nearest:
import numpy as np
indices = np.arange(x.size)
zeroes = x==0
forward = indices - np.maximum.accumulate(indices*zeroes) # forward distance
forward[np.cumsum(zeroes)==0] = x.size-1 # handle absence of zero from edge
forward = forward * (x!=0) # set zero positions to zero
zeroes = zeroes[::-1]
backward = indices - np.maximum.accumulate(indices*zeroes) # backward distance
backward[np.cumsum(zeroes)==0] = x.size-1 # handle absence of zero from edge
backward = backward[::-1] * (x!=0) # set zero positions to zero
distZero = np.minimum(forward,backward) # closest distance (minimum)
results:
distZero
# [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
forward
# [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
backward
# [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
Special case where no zeroes are present on outer edges:
x = np.array([3, 1, 2, 0, 4, 5, 6, 0,8,8])
forward: [9 9 9 0 1 2 3 0 1 2]
backward: [3 2 1 0 3 2 1 0 9 9]
distZero: [3 2 1 0 1 2 1 0 1 2]
also works with no zeroes at all
[EDIT] non-numpy solutions ...
if you're looking for an O(N) solution that doesn't require numpy, you can apply this strategy using the accumulate function from itertools:
x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
from itertools import accumulate
maxDist = len(x) - 1
zeroes = [maxDist*(v!=0) for v in x]
forward = [*accumulate(zeroes,lambda d,v:min(maxDist,(d+1)*(v!=0)))]
backward = accumulate(zeroes[::-1],lambda d,v:min(maxDist,(d+1)*(v!=0)))
backward = [*backward][::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]
print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)
output:
x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
If you don't want to use any library, you can accumulate the distances manually in a loop:
x = [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
forward,backward = [],[]
fDist = bDist = maxDist = len(x)-1
for f,b in zip(x,reversed(x)):
fDist = min(maxDist,(fDist+1)*(f!=0))
forward.append(fDist)
bDist = min(maxDist,(bDist+1)*(b!=0))
backward.append(bDist)
backward = backward[::-1]
distZero = [min(f,b) for f,b in zip(forward,backward)]
print("x",x)
print("f",forward)
print("b",backward)
print("d",distZero)
output:
x [0, 1, 2, 0, 4, 5, 6, 7, 0, 0]
f [0, 1, 2, 0, 1, 2, 3, 4, 0, 0]
b [0, 2, 1, 0, 4, 3, 2, 1, 0, 0]
d [0, 1, 1, 0, 1, 2, 2, 1, 0, 0]
My first intuition would be to use slicing. If x can be a normal list instead of a numpy array, then you could use
out = [x[i:].index(0) for i,_ in enumerate(x)]
if numpy is necessary then you can use
out = [np.where(x[i:]==0)[0][0] for i,_ in enumerate(x)]
but this is less efficient because you are finding all zero locations to the right of the value and then pulling out just the first. Almost definitely a better way to do this in numpy.
Edit: I am sorry, I misunderstood. This will give you the distance to the nearest zeros - may it be at left or right. But you can use d_right as intermediate result. This does not cover the edge case of not having any zero to the right though.
import numpy as np
x = np.array([0, 1, 2, 0, 4, 5, 6, 7, 0, 0])
# Get the distance to the closest zero from the left:
zeros = x == 0
zero_locations = np.argwhere(x == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))
temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_left = np.cumsum(temp) - 1
# Get the distance to the closest zero from the right:
zeros = x[::-1] == 0
zero_locations = np.argwhere(x[::-1] == 0).flatten()
zero_distances = np.diff(np.insert(zero_locations, 0, 0))
temp = x.copy()
temp[~zeros] = 1
temp[zeros] = -(zero_distances-1)
d_right = np.cumsum(temp) - 1
d_right = d_right[::-1]
# Get the smallest distance from both sides:
smallest_distances = np.min(np.stack([d_left, d_right]), axis=0)
# np.array([0, 1, 1, 0, 1, 2, 2, 1, 0, 0])

Prim's algorithm alternate solution

I am trying to find an alternate spanning tree for the graph given. However, I am not able to understand that how I can have multiple solutions together in the output.
For the given graph, in the output I get 0-2 with a weight of 8. Also, 1-3 has a weight of 8. So I want a separate spanning tree too, which has 1-3 as a part of solution, giving me 2 separate solutions.
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 printTree(self, parent):
print ("Edge \t Weight")
for i in range(1, self.V):
print (parent[i], "-", i, "\t", self.graph[i][ parent[i] ] )
def minKey(self, key, Set):
min = sys.maxsize
for v in range(self.V):
if key[v] < min and Set[v] == False:
min = key[v]
min_index = v
return min_index
# Prim's Algorithm
def prim(self):
key = [sys.maxsize] * self.V # Key values used to pick minimum weight edge
parent = [None] * self.V # Array to store constructed MST
key[0] = 0 # Make key 0 so that this vertex is picked as first vertex
Set = [False] * self.V # Initially there are no vertices in MST set
parent[0] = -1 # First node is always the root
for i in range(self.V):
u = self.minKey(key, Set)
Set[u] = True
for v in range(self.V):
if self.graph[u][v] > 0 and Set[v] == False and key[v] > self.graph[u][v]:
key[v] = self.graph[u][v]
parent[v] = u
self.printTree(parent)
Prim = Graph(7)
Prim.graph = [[0, 4, 8, 0, 0, 0, 0],
[4, 0, 9, 8, 10,0, 0],
[8, 9, 0, 2, 0, 1, 0],
[0, 8, 2, 0, 7, 9, 0],
[0, 10, 0, 7, 0, 5, 6],
[0, 0, 1, 9, 5, 0, 2],
[0, 0, 0, 0, 6, 2, 0]]
Prim.prim()

Generate unique binary permutations in python

Please, how can I get all these binary permutations, but without repetition in Python?
a = list(itertools.permutations([1, 1, 0, 0]))
for i in range(len(a)):
print a[i]
(1, 1, 0, 0)
(1, 1, 0, 0)
(1, 0, 1, 0)
...
It would be great if it would be roughly efficient since I'll have to do that with a list of even 30 elements like this.
As #Antti said in a comment, this is equivalent to looking for combinations of positions of the input list which determine which bits in the output are 1.
from itertools import combinations
def binary_permutations(lst):
for comb in combinations(range(len(lst)), lst.count(1)):
result = [0] * len(lst)
for i in comb:
result[i] = 1
yield result
for perm in binary_permutations([1, 1, 0, 0]):
print(perm)
Output:
[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]
Here's the algorithm from the accepted answer to the generic algorithm question, adapted into Python 3 (should work in Python 2.7+). The function generate(start, n_bits) will generate all n-bit integers starting from start lexicographically.
def generate(start, n_bits):
# no ones to permute...
if start == 0:
yield 0
return
# fastest count of 1s in the input value!!
n_ones = bin(start).count('1')
# the minimum value to wrap to when maxv is reached;
# all ones in LSB positions
minv = 2 ** n_ones - 1
# this one is just min value shifted left by number of zeroes
maxv = minv << (n_bits - n_ones)
# initialize the iteration value
v = start
while True:
yield v
# the bit permutation doesn't wrap after maxv by itself, so,
if v == maxv:
v = minv
else:
t = ((v | ((v - 1))) + 1)
v = t | (((((t & -t)) // ((v & -v))) >> 1) - 1)
# full circle yet?
if v == start:
break
for i in generate(12, 4):
print('{:04b}'.format(i))
Prints
1100
0011
0101
0110
1001
1010
If list output is generated, this can then be decorated:
def generate_list(start):
n_bits = len(start)
start_int = int(''.join(map(str, start)), 2)
# old and new-style formatting in one
binarifier = ('{:0%db}' % n_bits).format
for i in generate(start_int, n_bits):
yield [int(j) for j in binarifier(i)]
for i in generate_list([1, 1, 0, 0]):
print(i)
prints
[1, 1, 0, 0]
[0, 0, 1, 1]
[0, 1, 0, 1]
[0, 1, 1, 0]
[1, 0, 0, 1]
[1, 0, 1, 0]
What is nice about this algorithm is that you can resume it at any point. If you find a way to calculate good starting points, it is possible to parallelize too. And the numbers should be more compact than lists, so you could use them if possible.
What you are trying to do is choose two positions at which the element will be 1.
Code
from itertools import combinations
def bit_patterns(size, ones):
for pos in map(set, combinations(range(size), ones)):
yield [int(i in pos) for i in range(size)]
Output
>>> print(*bit_patterns(4, 2), sep='\n')
[1, 1, 0, 0]
[1, 0, 1, 0]
[1, 0, 0, 1]
[0, 1, 1, 0]
[0, 1, 0, 1]
[0, 0, 1, 1]
Alternative
A fun alternative is to see the desired output as the binary representations which have only two ones. We can use this definition to get the output you want.
from itertools import combinations
def bit_patterns(size, ones):
for t in combinations([1 << i for i in range(size)], ones):
yield [int(n) for n in f'{sum(t):0{size}b}']
Here is a recursive solution:
def bin_combs_iter(ones, zeros):
if not zeros:
yield [1] * ones
elif not ones:
yield [0] * zeros
else:
for x in bin_combs_iter(ones - 1, zeros):
x.append(1)
yield x
for x in bin_combs_iter(ones, zeros - 1):
x.append(0)
yield x
def bin_combs(ones, zeros):
return list(bin_combs_iter(ones, zeros))

Categories