finding paths in graphs with integer stack or array - python

Given a graph (ordered DAG) represented using an adjacency matrix
g = [
[0,1,1,0,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,0,1,1,0],
[0,0,0,0,0,0,1],
[0,0,0,0,0,0,1],
[0,0,0,0,0,0,0]
]
I wrote the following to figure out all the longest paths in the graph starting from the first node, node 0.
from collections import deque
stack = deque()
stack.append([0])
current_longest = 1
paths = []
N = len(g[0])
while stack:
cur_path = stack.pop()
print(cur_path)
last_node = cur_path[-1]
any_n = False
for new_node in range(last_node + 1, N):
if g[last_node][new_node] == 1:
any_n = True
stack.append(cur_path + [new_node])
if any_n == False:
if len(cur_path) > current_longest:
nmb_paths = 1
paths = []
paths.append(cur_path)
current_longest = len(cur_path)
elif len(cur_path) == current_longest:
nmb_paths += 1
paths.append(cur_path)
print(f"paths = {paths}")
The output of this:
[0]
[0, 2]
[0, 2, 3]
[0, 2, 3, 5]
[0, 2, 3, 5, 6]
[0, 2, 3, 4]
[0, 2, 3, 4, 6]
[0, 1]
[0, 1, 3]
[0, 1, 3, 5]
[0, 1, 3, 5, 6]
[0, 1, 3, 4]
[0, 1, 3, 4, 6]
paths = [[0, 2, 3, 5, 6], [0, 2, 3, 4, 6], [0, 1, 3, 5, 6], [0, 1, 3, 4, 6]]
but I am now trying to figure out if I can do this without the luxury of an array stack. So if I can't store arrays as elements of the stack, I can only use an integer stack or a normal array. I am stuck on this, I think it's a matter of keeping track of the length of the list, as it grows, storing each path, and if it doesn't grow, storing the path, but a little stumped atm, any hints?

not much interest, but thought I'd put up my solution anyway, in case it helps anyone (I was implementing this in C so it was a pain having to write an ArrayStack) this was my prototype:
g = [
[0,1,1,0,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,1,0,0,0],
[0,0,0,0,1,1,0],
[0,0,0,0,0,0,1],
[0,0,0,0,0,0,1],
[0,0,0,0,0,0,0]
]
N = len(g[0])
nmb_paths = 1
current_longest = 1
paths = []
array = [0]
current_length = 1
visited = [0] * N
visited[0] = 1
pred = [-1] * N
while current_length > 0:
last_node = array[current_length-1]
any_n = False
new_node = last_node + 1
print('paths = ', paths)
while not any_n and new_node < N:
if g[last_node][new_node] == 1 and visited[new_node] != 1:
if pred[new_node] != last_node:
pred[new_node] = last_node
any_n = True
array.append(new_node)
visited[new_node] = 1
current_length += 1
last_node = new_node
if current_length > current_longest:
paths = []
app = array.copy()
paths.append(app)
current_longest = current_length
nmb_paths = 1
elif current_length == current_longest:
app = array.copy()
paths.append(app)
nmb_paths += 1
new_node+=1
if any_n == False:
final = array[current_length - 1]
del array[current_length-1]
visited[final] = 0
for j in range(final+1, N):
pred[j] = -1
current_length -= 1
the progression of paths:
paths = []
paths = [[0, 1]]
paths = [[0, 1, 3]]
paths = [[0, 1, 3, 4]]
paths = [[0, 1, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6], [0, 2, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6], [0, 2, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6], [0, 2, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6], [0, 2, 3, 5, 6]]
paths = [[0, 1, 3, 4, 6], [0, 1, 3, 5, 6], [0, 2, 3, 4, 6], [0, 2, 3, 5, 6]]

Related

Single vector multiple times in 2d array

This is my code:
b = [6 * [1, 3, 4, 2],
4 * [2, 1, 4, 3],
3 * [3, 4, 2, 1],
4 * [4, 2, 1, 3],
4 * [4, 3, 2, 1],
]
Which returns an array which has 6X4=24 elements in the first line 4X4=16 in the second etc...
What i want to achieve is adding the exact same line multiple times like:
1, 3, 4, 2
1, 3, 4, 2
1, 3, 4, 2
1, 3, 4, 2
1, 3, 4, 2
1, 3, 4, 2 #6 tines the first line
2, 1, 4, 3
2, 1, 4, 3
2, 1, 4, 3
2, 1, 4, 3 # 4 times the second
..........
but of course by not copying the same line again and again
Try:
b = [
*[[1, 3, 4, 2] for _ in range(6)],
*[[2, 1, 4, 3] for _ in range(4)],
*[[3, 4, 2, 1] for _ in range(3)],
*[[4, 2, 1, 3] for _ in range(4)],
*[[4, 3, 2, 1] for _ in range(4)],
]
print(b)
Prints:
[
[1, 3, 4, 2],
[1, 3, 4, 2],
[1, 3, 4, 2],
[1, 3, 4, 2],
[1, 3, 4, 2],
[1, 3, 4, 2],
[2, 1, 4, 3],
[2, 1, 4, 3],
[2, 1, 4, 3],
[2, 1, 4, 3],
[3, 4, 2, 1],
[3, 4, 2, 1],
[3, 4, 2, 1],
[4, 2, 1, 3],
[4, 2, 1, 3],
[4, 2, 1, 3],
[4, 2, 1, 3],
[4, 3, 2, 1],
[4, 3, 2, 1],
[4, 3, 2, 1],
[4, 3, 2, 1],
]
You can also put it in one line with
b = 6*[[1, 3, 4, 2]] + 4*[[2, 1, 4, 3]] + 3*[[3, 4, 2, 1]] + 4*[[4, 2, 1, 3]] + 4* [[4, 3, 2, 1]])
print(b)
Only two steps if you don't want to change how b was created.
import numpy as np
B = np.concatenate(b).ravel()
b = np.reshape(B,(21,4))
You can use a list comprehension of a list comprehension to repeat y times your x lists (from bb and aa, respectively)
The notation is a bit strange, the "center" one is the outer element, then it expands on the right.
Advantage: numpy not required, and it will work for any size entries, unlike other answers "harcoding" the rows
aa=[[1, 3, 4, 2],
[2, 1, 4, 3],
[3, 4, 2, 1],
[4, 2, 1, 3],
[4, 3, 2, 1]]
bb=[6,4,3,4,4]
xx = [x for x,y in zip(aa,bb) for _ in range(y) ]
returns:
[[1, 3, 4, 2], [1, 3, 4, 2], [1, 3, 4, 2], [1, 3, 4, 2], [1, 3, 4, 2], [1, 3, 4, 2], [2, 1, 4, 3], [2, 1, 4, 3], [2, 1, 4, 3], [2, 1, 4, 3], [3, 4, 2, 1], [3, 4, 2, 1], [3, 4, 2, 1], [4, 2, 1, 3], [4, 2, 1, 3], [4, 2, 1, 3], [4, 2, 1, 3], [4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1], [4, 3, 2, 1]]
It can be done systematically by pairing the factors and the sublists and multiplying them together. Then flat the list.
Here a hacky-way
b = [[1, 3, 4, 2],
[2, 1, 4, 3],
[3, 4, 2, 1],
[4, 2, 1, 3],
[4, 3, 2, 1],
]
factors = [6, 4, 3, 4, 4]
print(sum(f*[l] for f, l in zip(factors, b), []))
The flattening part (chaining of lists) is done with sum( , []) but is not performant (and designed for that)... but still useful, in my opinion, to test some small stuffs on the fly. The list chaining have to be done with i.e. itertools.chain. Here an example
import itertools as it
...
list(it.chain.from_iterable(f*[l] for f, l in zip(factors, b)))
# or (for many iterators)
list(it.chain(*(f*[l] for f, l in zip(factors, b))))
# or with repeat
list(it.chain.from_iterable(it.starmap(it.repeat, zip(b, factors))))
# or (another) with repeat
list(it.chain.from_iterable(map(it.repeat, b, factors))
# or ...
list(it.chain.from_iterable(map(list.__mul__, ([i] for i in b) , factors)))
# or with reduce
import functools as fc
list(fc.reduce(lambda i, j: i + j, zip(b, factors)))

delete elements from lists in a nested list [duplicate]

This question already has answers here:
How do I clone a list so that it doesn't change unexpectedly after assignment?
(24 answers)
Closed 1 year ago.
I am using python, I have a nested list like this
sequences_list = [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]
I want to get a sort of sequences like this
sequences_list = [[0], [0, 1], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]]
this is my code
k = 1
j = 0
while (k <= len(sequences_list)):
del sequences_list[j][k:]
k = k+1
j = j+1
print(sequences_list)
the result i get is
[[0], [0], [0], [0], [0], [0]]
Try this one
sequences_list = [[0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5]]
print([element[0:po+1] for po,element in enumerate(sequences_list)])

Python get all paths from graph

I'm trying to find the paths that a user could take through a website. I have represented my graph using this format:
graph = { 0 : [1, 2],
1 : [3, 6, 0],
2 : [4, 5, 0],
3 : [1],
4 : [6, 2],
5 : [6, 2],
6 : [1, 4, 5]}
I have implemented a depth first algorithm, but it needs a change for it to be useful. It needs to return the path and not just the nodes in the order it goes to them.
visitedList = [[]]
def depthFirst(graph, currentVertex, visited):
visited.append(currentVertex)
for vertex in graph[currentVertex]:
if vertex not in visited:
depthFirst(graph, vertex, visited)
return visited
traversal = depthFirst(graph, 0, visitedList)
print('Nodes visited in this order:')
print(visitedList)
That function returns this:
[[], 0, 1, 3, 6, 4, 2, 5]
Whereas I want something like this:
[[0, 1, 3], [0, 1, 6], [0, 2, 4, 6], [0, 2, 5, 6]]
When passing a list in python it does not deep copy. Using list.copy() can really help here. I'm not sure this is what you wanted but here is the code:
visitedList = [[]]
def depthFirst(graph, currentVertex, visited):
visited.append(currentVertex)
for vertex in graph[currentVertex]:
if vertex not in visited:
depthFirst(graph, vertex, visited.copy())
visitedList.append(visited)
depthFirst(graph, 0, [])
print(visitedList)
It returns all the paths.
[[], [0, 1, 3], [0, 1, 6, 4, 2, 5], [0, 1, 6, 4, 2], [0, 1, 6, 4], [0, 1, 6, 5, 2, 4], [0, 1, 6, 5, 2], [0, 1, 6, 5], [0, 1, 6], [0, 1], [0, 2, 4, 6, 1, 3], [0, 2, 4, 6, 1], [0, 2, 4, 6, 5], [0, 2, 4, 6], [0, 2, 4], [0, 2, 5, 6, 1, 3], [0, 2, 5, 6, 1], [0, 2, 5, 6, 4], [0, 2, 5, 6], [0, 2, 5], [0, 2], [0]]
List copy worked for me in python3.

Python: Sorting a List

I'm trying to sort a list by moving through it and checking if a given element of the list is greater than the next element of the list, and if so, moving it accordingly so that the smaller number is to the left, larger to the right.
This is the code I have so far:
L = [3, 4, 1, 5, 2, 0]
for i in range(0, (len(L)-1)):
if L[i] > L[i+1]:
L[i], L[i+1] = L[i+1], L[i]
print(L)
The output of this for the three iterations are as follows:
[3, 1, 4, 5, 2, 0]
[3, 1, 4, 2, 5, 0]
[3, 1, 4, 2, 0, 5]
My goal is to get it to read [0, 1, 2, 3, 4, 5]. I realize I can just use the sorted() function but I'd prefer not to. I was thinking that I could try and iterate the for loop over the list multiple times, but I am unsure as to how to do so.
Currently you only compare each element and it's successor. However you really want to compare it to all following elements (starting with i+1 and ending with len(L)), so you need a double loop:
L = [3, 4, 1, 5, 2, 0]
for i in range(0, (len(L)-1)):
for j in range(i+1, len(L)):
if L[i] > L[j]:
L[i], L[j] = L[j], L[i]
print(L)
Which prints the following steps:
[1, 4, 3, 5, 2, 0]
[0, 4, 3, 5, 2, 1]
[0, 3, 4, 5, 2, 1]
[0, 2, 4, 5, 3, 1]
[0, 1, 4, 5, 3, 2]
[0, 1, 3, 5, 4, 2]
[0, 1, 2, 5, 4, 3]
[0, 1, 2, 4, 5, 3]
[0, 1, 2, 3, 5, 4]
[0, 1, 2, 3, 4, 5]
You could just put it inside a while loop:
L = [3, 4, 1, 5, 2, 0]
flag = True
while flag:
flag = False
for i in range(0, (len(L)-1)):
if L[i] > L[i+1]:
L[i], L[i+1] = L[i+1], L[i]
flag = True
print(L)
Output:
[3, 1, 4, 5, 2, 0]
[3, 1, 4, 2, 5, 0]
[3, 1, 4, 2, 0, 5]
[1, 3, 4, 2, 0, 5]
[1, 3, 2, 4, 0, 5]
[1, 3, 2, 0, 4, 5]
[1, 2, 3, 0, 4, 5]
[1, 2, 0, 3, 4, 5]
[1, 0, 2, 3, 4, 5]
[0, 1, 2, 3, 4, 5]

Python list of list of lists

I don't have a real reason for doing this, other than to gain understanding, but I'm trying to create a list of lists of lists using list comprehension.
I can create a list of lists just fine:
In[1]: [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
Out[1]: [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
And I can create a list of lists of lists from either the results of that, for example:
In [2]: [range(0,i) for i in [3,4]]
Out[2]: [[0, 1, 2], [0, 1, 2, 3]]
In [3]: [range(0,i) for i in j]
Out[3]:
[[0, 1, 2],
[0, 1, 2, 3],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6, 7]]
But when I try to combine it into a single statement it goes awry:
In [4]: [range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
---------------------------------------------------------------------------
TypeError: range() integer end argument expected, got list.
Am I missing some brackets somewhere?
Try the following:
[[range(0, j) for j in range(3, i)] for i in range(5, 10)]
This results in the following list of lists of lists:
>>> pprint.pprint([[range(0, j) for j in range(3, i)] for i in range(5, 10)])
[[[0, 1, 2], [0, 1, 2, 3]],
[[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]],
[[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]],
[[0, 1, 2],
[0, 1, 2, 3],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5, 6]],
[[0, 1, 2],
[0, 1, 2, 3],
[0, 1, 2, 3, 4],
[0, 1, 2, 3, 4, 5],
[0, 1, 2, 3, 4, 5, 6],
[0, 1, 2, 3, 4, 5, 6, 7]]]
The best way to understand what is happening in a list comprehension is to try to roll it out into normal for loops, lets try that with yours and then mine to see what the difference is:
x = [range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
# equivalent to
a, b, c, x = [], [], [], []
for k in range(5, 10):
a.append(k)
for k in a:
b.append(range(3, k))
for j in b:
c.append(j)
for i in c:
x.append(range(0, i))
At the end of this x would be equivalent to your list comprehension, however of course this code will not work because b (and c) will be lists of lists, so i will be a list and range(0, i) will cause an error. Now obviously this is not what you intended to do, since what you would really like to see is those for loops nested instead of one after the other.
Lets look at how mine works:
x = [[range(0, j) for j in range(3, i)] for i in range(5, 10)]
# equivalent to
x = []
for i in range(5, 10):
a = []
for j in range(3, i):
a.append(range(0, j)):
x.append(a)
Hope this helped to clarify!
From your question:
In[1]: [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
Out[1]: [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
range takes integer parameters
So when you do
[range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
it is the equivalent of saying
L = []
for j in [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]:
L.append(range(0,i))
Of course, this will fail because each i is a list and range doesn't take list parameters.
Other answers here show you how to fix your error. This response was to explain what went wrong with your initial approach
Hope this helps
To troubleshoot this, I ran each step of the list comprehension at a time.
>>> [k for k in range(5,10)]
[5, 6, 7, 8, 9]
>>> [range(3,k) for k in [k for k in range(5,10)]]
[[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
Your problem is here, because it feeds lists to the next range() instead of ints:
>>> [j for j in [range(3,k) for k in [k for k in range(5,10)]]]
[[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
You can flatten this list using lambda and reduce:
# reduce(lambda x,y: x+y,l)
>>> reduce(lambda x,y: x+y, [range(3,k) for k in [k for k in range(5,10)]])
[3, 4, 3, 4, 5, 3, 4, 5, 6, 3, 4, 5, 6, 7, 3, 4, 5, 6, 7, 8]
But you will still only nest two lists deep:
>>> [range(0,i) for i in reduce(lambda x,y: x+y, [range(3,k) for k in [k for k in range(5,10)]])]
[[0, 1, 2], [0, 1, 2, 3], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6, 7]]
If you want to go three lists deep, you need to reconsider your program flow. List comprehensions are best suited for working with the outermost objects in an iterator. If you used list comprehensions on the left side of the for statement as well as the right, you could nest more deeply:
>>> [[[range(j, 5) for j in range(5)] for i in range(5)] for k in range(5)]
That's because:
[range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
^here i == j == range(3,k) - it's a range, not integer
You probably wanted to do:
[range(0,j) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]] for j in i]
^ i is still a list, iterate it with j here
You do of course know that your first statement can be reduced to :
[range(3,k) for k in range(5,10)]
??
[range(0,i) for i in [j for j in [range(3,k) for k in [k for k in range(5,10)]]]]
Because i is a list, you need list comprehend on that too:
[[range(0,h) for h in i] for i in [...]]
In your code:
print [
[range(0,h) for h in i] for i in [ # get a list for i here. Need to iterate again on i!
j for j in [ # [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
range(3,k) for k in [
k for k in range(5,10) # [5,6,7,8,9]
]
]
]
]
# output:
[[[0, 1, 2], [0, 1, 2, 3]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6]], [[0, 1, 2], [0, 1, 2, 3], [0, 1, 2, 3, 4], [0, 1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5, 6], [0, 1, 2, 3, 4, 5, 6, 7]]]
Also, you have one unnecessary comprehension in there. This will produce the same result:
print [
[range(0,i) for i in j] for j in [ # get a list for j here. Need to iterate again on j!
range(3,k) for k in [ # [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
k for k in range(5,10) # [5,6,7,8,9]
]
]
]
As an aside, this is about the point where a list comprehension becomes less desirable than a recursive function or simple nested for loops, if only for the sake of code readability.
Apparently, in the first range (range(0,i)), i is [[3, 4], [3, 4, 5], [3, 4, 5, 6], [3, 4, 5, 6, 7], [3, 4, 5, 6, 7, 8]]
You probably need to flatten that list before you call the range function with it.

Categories