How to find all the paths from each router to the rest? - python

map = {
routers[0]: [routers[3], routers[4]],
routers[1]: [routers[5], routers[6]],
routers[2]: [routers[7], routers[8]],
routers[3]: [routers[0], routers[4], routers[8]],
routers[4]: [routers[0], routers[3], routers[5], routers[9]],
routers[5]: [routers[1], routers[4], routers[6]],
routers[6]: [routers[1], routers[5], routers[7], routers[9]],
routers[7]: [routers[2], routers[6], routers[8]],
routers[8]: [routers[2], routers[3], routers[7], routers[9]],
routers[9]: [routers[4], routers[6], routers[8]]
}
The keys are the routers and their values are there neighbors.
For example, routers[0] is connected to both routers[3] and routers[4].
And I'm trying to create a table for each router like this:
routers[0] = {
router[1]: router[x],
router[2]: router[y],
# and so on.
}
Where the keys are the destinations and the values are the next routers.
I did it, but it's too messy. Is there a better way to do that?
Here is more info:

Not sure how you are making a dictionary where the keys are also dictionaries, but you can write a breadth-first-search function to find the shortest path and then just grab the first step at index 1.
routers = [f'router{i}' for i in range(10)]
router_map = {
routers[0]: [routers[3], routers[4]],
routers[1]: [routers[5], routers[6]],
routers[2]: [routers[7], routers[8]],
routers[3]: [routers[0], routers[4], routers[8]],
routers[4]: [routers[0], routers[3], routers[5], routers[9]],
routers[5]: [routers[1], routers[4], routers[6]],
routers[6]: [routers[1], routers[5], routers[7], routers[9]],
routers[7]: [routers[2], routers[6], routers[8]],
routers[8]: [routers[2], routers[3], routers[7], routers[9]],
routers[9]: [routers[4], routers[6], routers[8]]
}
def shortest_path(start, dest, router_map = router_map):
visited = [start]
paths = [[start]]
while dest not in visited:
paths = [path + [neighbor]
for path in paths
for neighbor in router_map[path[-1]]]
visited = set([node for path in paths for node in path])
return [path for path in paths if dest in path]
for i in range(1, 10):
print(shortest_path(routers[0], routers[i]))

It seems you are looking for all_shortest_paths. You can use a library like networkx to do it for you.
Assuming your list of routers is definded like :
routers = list(range(len(map))
you could do:
import networkx as nx
G = nx.Graph()
for k, v in map.items():
G.add_edges_from([(k, r) for r in v])
all_shortest_paths_dict = {
k: {r: list(nx.all_shortest_paths(G, k, r)) for r in range(len(map)) if r != k}
for k in map
}
print(all_shortest_paths_dict)
{0: {1: [[0, 4, 5, 1]],
2: [[0, 3, 8, 2]],
3: [[0, 3]],
4: [[0, 4]],
5: [[0, 4, 5]],
6: [[0, 4, 5, 6], [0, 4, 9, 6]],
7: [[0, 3, 8, 7]],
8: [[0, 3, 8]],
9: [[0, 4, 9]]},
1: {0: [[1, 5, 4, 0]],
2: [[1, 6, 7, 2]],
3: [[1, 5, 4, 3]],
4: [[1, 5, 4]],
5: [[1, 5]],
6: [[1, 6]],
7: [[1, 6, 7]],
8: [[1, 6, 7, 8], [1, 6, 9, 8]],
9: [[1, 6, 9]]},
......

Related

Find all the consecutive subsequences of alternating odd and even numbers

Given an integer array, find all the consecutive subsequences of alternating odd and even numbers.
Also print the total number of such subsequences.
All the subsequences should be unique.
The numbers in the list may or may not be unique.
Example:
array = [1,2,5]
output1: [[1], [1,2], [2], [2,5], [5], [1,2,5]]
output2: 6
My Code:
res = []
lst = [1,2,5]
for i in range(len(lst)-1):
res.append([lst[i]])
if abs(lst[i] - lst[i+1]) % 2 == 1:
res.append([lst[i], lst[i+1]])
print(res)
Output: [[1], [1, 2], [2], [2, 5]]
How can I get the remaining subsequences?
You care about duplicates:
def alternating_sublists(xs: list[int]) -> list[list[int]]:
results = []
for i in range(len(xs)):
if [xs[i]] not in results:
results.append([xs[i]])
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
if xs[i:j+1] not in results:
results.append(xs[i:j+1])
else:
break
return results
print(list(alternating_sublists([1, 2, 5])))
print(list(alternating_sublists([1, 2, 2, 2, 1])))
print(list(alternating_sublists([1, 2, 3, 2, 3, 2, 1])))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]
It's not extremely efficient (there's many lookups of lists already in the result). Depending on the application you may want a more complex data structure to save expensive 'list in large list' tests.
The basic logic is this:
each sequence has to start at some index, so try sequences starting at all possible indices for i in range(len(xs)):
the sequence with length 1 always meets your rule, so add it if it wasn't there yet
the other sequences start at index i and end at index i+1 or greater for j in range(i+1, len(xs)):
break from the loop whenever the modulo is 0 for the last two items in list you're about to add, since this sequence doesn't meet the rule, and longer ones wouldn't either.
Slightly faster and shorter, using tuples internally, but essentially the same:
def alternating_sublists2(xs: list[int]) -> list[list[int]]:
results = set()
for i in range(len(xs)):
results.add((xs[i],))
for j in range(i+1, len(xs)):
if (xs[j] - xs[j-1]) % 2 != 0:
results.add(tuple(xs[i:j+1]))
else:
break
return [list(t) for t in results]
shorter as the previous if statements are now internal to set.add()
faster because looking up tuples is faster than looking up strings, and testing membership of a set is faster than testing membership of a list
not quite as fast as you might like, since it then has to convert the result back to a list of lists, to get the result you required.
However, no guarantees on the order of the sublists in the result, so this is no good if you need the sublists in the order they are first found.
Here's a recursive solution to the problem. It iterates the elements of the list, adding the results from recursing the balance of the list when there is a change from odd to even between the current element and the next:
def odd_even(list, start=None):
result = []
for i, val in enumerate(list):
if start is None or i == 0:
if [val] not in result:
result.append([val])
if len(list) > i+1 and (list[i+1] - val) % 2 == 1:
for res in odd_even(list[i+1:], val):
if [val] + res not in result:
result = result + [[val] + res]
return result
print(odd_even([1, 2, 5]))
print(odd_even([1, 2, 2, 2, 1]))
print(odd_even([1, 2, 3, 2, 3, 2, 1]))
Output:
[[1], [1, 2], [1, 2, 5], [2], [2, 5], [5]]
[[1], [1, 2], [2], [2, 1]]
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 2], [1, 2, 3, 2, 3], [1, 2, 3, 2, 3, 2], [1, 2, 3, 2, 3, 2, 1], [2], [2, 3], [2, 3, 2], [2, 3, 2, 3], [2, 3, 2, 3, 2], [2, 3, 2, 3, 2, 1], [3], [3, 2], [3, 2, 3], [3, 2, 3, 2], [3, 2, 3, 2, 1], [2, 3, 2, 1], [3, 2, 1], [2, 1]]
Your accepted output is silly, because it's obvious that every subsequence of a "good" sequence is also "good" and there's no need to enumerate them all. Let's concentrate on finding longest alternating sequences:
def split(a):
buf = [a[0]]
for i in range(1, len(a)):
if a[i] % 2 != a[i - 1] % 2:
buf.append(a[i])
else:
yield buf
buf = [a[i]]
if buf:
yield buf
test = [1, 2, 5, 7, 3, 8, 9, 9, 10, 11]
result = list(split(test))
# [[1, 2, 5], [7], [3, 8, 9], [9, 10, 11]]
To get your expected answer, take each list from the result and generate all sublists of it. This is another, much simpler task.
This looks like a gray code sequence with additional twist:
https://en.wikipedia.org/wiki/Gray_code
Code:
import math
def powerOf2(k):
if k == 0:
return 1
else:
return 2*powerOf2(k-1)
def gray_encode(n):
return n ^ n >> 1
def count_required_sequence(lst):
n = len(lst)
sequence_nr = powerOf2(n)
results = []
results_sequence = -1
for i in range(sequence_nr):
gray = gray_encode(i)
gray_r = list("{:>010b}".format(gray))[::-1]
#print(gray_r)
count = sum(el == "1" for el in gray_r)
if count > 1:
results_sequence += 1
results.append(list())
for k in range(len(gray_r)):
if k < len(gray_r)-1:
if gray_r[k] == "1" and gray_r[k+1] == "1":
if abs(lst[k] - lst[k+1]) % 2 == 1:
results[results_sequence].append(lst[k])
results[results_sequence].append(lst[k+1])
is_there_count1 = results.count(list(set(results[results_sequence])))
results[results_sequence] = list(set(results[results_sequence]))
is_there_count = results.count(results[results_sequence])
if is_there_count > 1 or is_there_count1 > 1:
index = results.index(list(set(results[results_sequence])))
results.pop(results_sequence)
results_sequence -= 1
elif count == 1 :
results_sequence += 1
results.append(list())
pos = [index for index,value in enumerate(gray_r) if value == "1" ]
results[results_sequence].append(lst[pos[0]])
results = (list(filter(lambda a: a != [], results)))
print("results: {}".format(results))
# Driver code
if __name__ == "__main__" :
# lst = [ 1, 2, 5, 6, 7];
lst = [ 1, 2, 5 ];
count_required_sequence(lst);
Output:
results: [[1], [1, 2], [2], [2, 5], [1, 2, 5], [5]]
Change 010b to a bigger number is len(lst) is bigger then 10
gray_r = list("{:>010b}".format(gray))[::-1]

How to sort a path of a graph

I've been trying sort the path of a graph.
For example, I have the following list in python.
graph = [
[4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [1, 0], [0, 2], [4, 1]
]
The result needs to be,
graph = [
[0, 2], [2, 5], [5, 7], [7, 3], [3, 8], [8, 6], [6, 4], [4, 1], [1, 0]
]
0 -> 2 -> 5 -> 7 -> 3 -> 8 -> 6 -> 4 -> 1 -> 0
The premise is that the path begins with an edge whose initial value is zero (0) and ends with an edge whose last element is also zero.
Here is another example:
graph = [
[0, 4], [4, 6], [8, 3], [3, 7], [5, 2], [2, 1], [1, 0], [7, 6], [5, 8]
]
The result needs to be:
graph = [
[0, 1], [1, 2], [2, 5], [5, 8], [8, 3], [3, 7], [7, 6], [6, 4], [4, 0]
]
0 -> 1 -> 2 -> 5 -> 8 -> 3 -> 7 -> 6 -> 4 -> 0
The direction doesn't matter.
I started with this code.
def sort_graph(graph):
sorted_graph = []
for edge in graph:
if edge[0] == 0:
sorted_graph.append(edge)
for edge in graph:
if edge[0] != 0:
sorted_graph.append(edge)
return sorted_graph
but I'm not sure where to go from here.
For each node in the cycle, keep track of its two neighbors. You can then walk through these neighbors to produce an ordering of the nodes. Once you've reached a node where both neighbors have already been visited, you're done.
neighbors = {}
for fst, snd in graph:
neighbors.setdefault(fst, []).append(snd)
neighbors.setdefault(snd, []).append(fst)
seen_both_neighbors = False
current = 0
path = []
seen = set()
while not seen_both_neighbors:
path.append(current)
fst, snd = neighbors[current]
if fst not in seen:
seen.add(current)
current = fst
elif snd not in seen:
seen.add(current)
current = snd
else:
seen_both_neighbors = True
result = list(map(list, zip(path, path[1:] + [path[0]])))
print(result)
For both of your examples, this produces the correct answer down to ordering.
That was a fun problem to solve! Here's my idea for the approach:
Find all pairs (forward and backward)
Create a lookup table to easily navigate them
Start at 0, iterate through and remove nodes you've already visited
from itertools import chain
import random
graph = [
[4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [1, 0], [0, 2], [4, 1]
]
# find all pairs independent of their direction
all_pairs = [*graph, *([t, f] for f, t in graph)]
# find all nodes
nodes = set(chain(*all_pairs))
# create a lookup dictionary for each point to show where you could go to
lookup = {node: {to_ for (from_, to_) in all_pairs if from_ == node} for node in nodes}
# simple solution - take a random path
from_ = 0
to_ = None
sorted_graph = []
while to_ != 0:
# select a random next point
to_ = random.choice(list(lookup[from_]))
# make sure to delete it so it doesn't get used again
lookup[from_].remove(to_)
lookup[to_].remove(from_)
# add to output
sorted_graph.append((from_, to_))
# tick one step forward
from_ = to_
print(sorted_graph)
You could also implement cycle,as below:
def cycle(vec):
result = [vec[0]]
s = vec[1:]
index = 0
while s:
if result[-1][0] == 0:
start = index
for i,v in enumerate(s):
if result[-1][1] in v:
del s[i]
result.append(v if v[0] == result[-1][1] else v[::-1])
break
index += 1
return result[start:] + result[:start]
cycle(graph)
[[0, 1], [1, 4], [4, 6], [6, 8], [8, 3], [3, 7], [7, 5], [5, 2], [2, 0]]

how can I balance these brackets in this recursive function?

I'm trying to write a recursive Python function to get all of the successive 1 or 2 digit orderings from a given number. For example, given 123, the result would be: [[1, 2, 3], [12, 3], [1, 23]].
I've written a function that seems to be on the right track but I can't balance the brackets.
def gen_codes(num):
if len(str(num)) == 1:
return [[num]]
result = []
for i in range(1, 3):
rest_str = str(num)[i:]
path = [int(str(num)[0:i])]
if len(rest_str):
path = [path + x for x in gen_codes(int(rest_str))]
result.append(path)
return result
print(gen_codes(123))
This outputs:
[[[1, [2, 3]], [1, 23]], [[12, 3]]]
Expected:
[[1, 2, 3], [12, 3], [1, 23]]
Your problem is with this line:
path = [path + x for x in gen_codes(int(rest_str))]
as it is adding an extra level of nesting of the lists. You should replace this code:
if len(rest_str):
path = [path + x for x in gen_codes(int(rest_str))]
result.append(path)
with
if len(rest_str):
for x in gen_codes(int(rest_str)):
result.append(path + x)
else:
result.append(path)
and then your code will produce the desired results. For example:
print(gen_codes(123))
print(gen_codes(1234))
print(gen_codes(12345))
Output:
[[1, 2, 3], [1, 23], [12, 3]]
[[1, 2, 3, 4], [1, 2, 34], [1, 23, 4], [12, 3, 4], [12, 34]]
[[1, 2, 3, 4, 5], [1, 2, 3, 45], [1, 2, 34, 5], [1, 23, 4, 5], [1, 23, 45], [12, 3, 4, 5], [12, 3, 45], [12, 34, 5]]
There's probably far more performant ways to write this, but here is the first one I thought of:
def f(s: str) -> List[List[int]]:
if len(s) == 1:
return [[int(s)]]
if len(s) == 2:
return [[int(s[0]), int(s[1])], [int(s)]]
return (
[[int(s[0]), *xs] for xs in f(s[1:])] +
[[int(s[0:2]), *xs] for xs in f(s[2:])]
)
Two base cases:
if s == "a", return [a]
if s == "ab", return [a, b] and [ab]
For all other strings,
if s == "ab[...]", return [a, *xs] for xs in f(b[...]) and [ab, *xs] for xs in f([...])
One key idea is to consistently operate within the correct (monadic) context. And that is always List[List[int]]. If you ever return anything else, you're doing something wrong.

Collapsing dictionary by merging matching keys and key,value pairs

So I am trying to find a way to "merge" a dependency list which is in the form of a dictionary in python, and I haven't been able to come up with a solution. So imagine a graph along the lines of this: (all of the lines are downward pointing arrows in this directed graph)
1 2 4
\ / / \
3 5 8
\ / \ \
6 7 9
this graph would produce a dependency dictionary that looks like this:
{3:[1,2], 5:[4], 6:[3,5], 7:[5], 8:[4], 9:[8], 1:[], 2:[], 4:[]}
such that keys are nodes in the graph, and their values are the nodes they are dependent on.
I am trying to convert this into a total ancestry list in terms of a tree, so that each node is a key, and its value is a list of ALL nodes that lead to it, not just it's immediate parents. The resulting dictionary would be:
{3:[1,2], 5:[4], 6:[3, 5, 1, 2, 4], 7:[5, 4], 8:[4], 9:[8, 4], 1:[], 2:[], 3:[]}
Any suggestions on how to solve this? I have been banging my head into it for a while, tried a recursive solution that I haven't been able to get working.
You can use a chained dict comprehension with list comprehension for up to two nodes.
>>> {k: v + [item for i in v for item in d.get(i, [])] for k,v in d.items()}
{3: [1, 2],
5: [4],
6: [3, 5, 1, 2, 4],
7: [5, 4],
8: [4],
9: [8, 4],
1: [],
2: [],
4: []}
For unlimited depth, you can use a recursive approach
def get_ant(node, d):
if node:
return d.get(node,[]) + [item for x in d.get(node, []) for item in get_ant(x, d) ]
return []
Then,
>>> get_ant(6, d)
[3, 5, 1, 2, 10, 4]
To get all cases:
>>> {k: get_ant(k, d) for k in d.keys()}
{3: [1, 2, 10],
5: [4],
6: [3, 5, 1, 2, 10, 4],
7: [5, 4],
8: [4],
9: [8, 4],
1: [10],
2: [],
4: []}
Here's a really simple way to do it.
In [22]: a
Out[22]: {1: [], 2: [], 3: [1, 2], 4: [], 5: [4], 6: [3, 5], 7: [5], 8: [4], 9: [8]}
In [23]: final = {}
In [24]: for key in a:
...: nodes = set()
...:
...: for val in a[key]:
...: nodes.add(val)
...: if val in a:
...: nodes.update(set(a[val]))
...:
...: final[key] = list(nodes)
In [25]: final
Out[25]:
{1: [],
2: [],
3: [1, 2],
4: [],
5: [4],
6: [3, 1, 2, 5, 4],
7: [5, 4],
8: [4],
9: [8, 4]}

How to merge multiple arrays in pairs

I have a problem with "pairing" arrays into one (by index). Here is an example:
INPUT:
inputArray = [[0, 1, 2, 3, 4], [2, 3, 5, 7, 8], [9, 6, 1]]
EXPECTED OUTPUT:
outputArray =
[[0,2,9],
[1,3,6],
[2,5,1],
[3,7,chooseRandom()],
[4,8,chooseRandom()]]
Questions:
How to avoid "out of range" "index error" problem
How to write chooseRandom() to choose N neighbour
Answers:
[SOLVED] Solutions provided by #jonrsharpe & #Christian & #Decency works as
expected
Clarification:
By N neighbour I mean:
I'm using python but feel free to share your thoughts in any language.
I think the following will do what you want:
from itertools import izip_longest # 'zip_longest' in Python 3.x
from random import choice
# Step 1
outputArray = list(map(list, izip_longest(*inputArray)))
# Step 2
for index, arr in enumerate(outputArray):
if any(item is None for item in arr):
valid = [item for item in arr if item is not None]
outputArray[index] = [choice(valid) if item is None else item
for item in arr]
This has two steps:
Combine all sub-lists of inputArray to the length of the longest sub-array, filling with None: [[0, 2, 9], [1, 3, 6], [2, 5, 1], [3, 7, None], [4, 8, None]]; and
Work through the outputArray, finding any sub-lists that contain None and replacing the None with a random choice from the other items in the sub-list that aren't None.
Example output:
[[0, 2, 9], [1, 3, 6], [2, 5, 1], [3, 7, 3], [4, 8, 8]]
Here's my approach to the problem, in Python 3.4. I don't really know what you mean by "choose N neighbour" but it should be pretty easy to write that however you'd like in the context below.
inputArray = [[0, 1, 2, 3, 4], [2, 3, 5, 7, 8], [9, 6, 1]]
import itertools
zipped = itertools.zip_longest(*inputArray, fillvalue=None)
outputArray = [list(item) for item in zipped]
# [[0, 2, 9], [1, 3, 6], [2, 5, 1], [3, 7, None], [4, 8, None]]
# Now replace the sentinel None in our sublists
for sublist in outputArray:
for i, element in enumerate(sublist):
if element is None:
sublist[i] = chooseRandom()
print(outputArray)
Not the most pythonic way, but you could try using this code snipped, read the comments in the code below:
import itertools, random
inputArray = [ [0, 1, 2, 3, 4], [2, 3, 5, 7, 8], [9, 6, 1] ]
outputArray = []
max_length = max(len(e) for e in inputArray) # maximum length of the sublists in <inputArray>
i = 0 # to keep the index of sublists of <outputArray>
for j in range(max_length):
outputArray.append([]) # add new sublist
for e in inputArray: # iterate through each element of <inputArray>
try:
outputArray[i].append(e[j]) # try to append the number, if an exception is raised
# then the code in the <except> clause will be executed
except IndexError as e:
outputArray[i].append(random.randint(0, 10)) # add the random number
i += 1 # increase the sublists index on each iteration
print outputArray
# [[0, 2, 9], [1, 3, 6], [2, 5, 1], [3, 7, 3], [4, 8, 7]]
Note:
You may want to change the part
random.randint(0, 10)
to get the "N neighbour".
Let me know whether you like this code:
import random
array = [[0, 1, 2, 3, 4], [2, 3, 5, 7, 8], [9, 6, 1]]
max_len = max([len(l) for l in array])
dictionary = {}
for l in array:
for i in range(0,len(l)):
if dictionary.has_key(i):
dictionary[i].append(l[i])
else:
dictionary[i] = [l[i]]
for i in range(len(l),max_len):
if dictionary.has_key(i):
dictionary[i].append(random.choice(l))
else:
dictionary[i] = [random.choice(l)]
print dictionary.values()

Categories

Resources