简体   繁体   中英

All Valid Possible Combinations

I have this road map with 5 bus stops:

a --_       _--b
     \--c--/
d ---/     \---e

I need to get all possible valid paths. A valid path is a path between two bus stops without skipping any bus station(s) in the between.

For example, If we represent the bus stops with a binary list as

            a b c d e
Stations = [1,1,1,1,1]

The following paths are valid:

P1 = [1,0,1,0,1] # a <-> c <-> e
P2 = [0,0,1,1,0] # c <-> b 
P3 = [0,0,0,0,1] # e

And these are invalid:

P4 = [1,1,0,0,0]
P5 = [0,1,0,1,0]

I created a matrix of valid connections:

  a b c d e
a 1   1
b   1 1
c 1 1 1 1 1
d     1 1
e     1   1

I created all possible combinations

c = list(itertools.product([0, 1], repeat=len(stations))

and multiplied each path to the above matrix but I didn't get anything meaningful. Does anyone know of a way to get all valid paths? we may have any number of bus stops on our map.

This answer contains a simple DFS solution (not necessarily very performant for large graphs).

Set up your graph:

graph = {
    'a': {'c'},
    'b': {'c'},
    'c': {'a', 'b', 'd', 'e'},
    'd': {'c'},
    'e': {'c'},
}

Given a node and a list of visited nodes, enumerate all possible sub-paths:

def list_paths(node, excluded_nodes):
    excluded_nodes = set(excluded_nodes)
    excluded_nodes.add(node)
    yield [node]
    yield from [[node] + p
        for x in graph[node] - excluded_nodes
        for p in list_paths(x, excluded_nodes)]

Do this for all starting nodes:

def list_all_paths():
    for k in graph:
        yield from list_paths(k, set())

Finally, running this we obtain:

>>> from pprint import pprint
>>> pprint(list(list_all_paths()))
[['a'],
 ['a', 'c'],
 ['a', 'c', 'b'],
 ['a', 'c', 'd'],
 ['a', 'c', 'e'],
 ['b'],
 ['b', 'c'],
 ['b', 'c', 'a'],
 ['b', 'c', 'd'],
 ['b', 'c', 'e'],
 ['c'],
 ['c', 'a'],
 ['c', 'd'],
 ['c', 'b'],
 ['c', 'e'],
 ['d'],
 ['d', 'c'],
 ['d', 'c', 'a'],
 ['d', 'c', 'b'],
 ['d', 'c', 'e'],
 ['e'],
 ['e', 'c'],
 ['e', 'c', 'a'],
 ['e', 'c', 'd'],
 ['e', 'c', 'b']]

Naturally, you can embed these into your own vectors quite trivially:

def list_all_vectors():
    embedding = {'a': 0, 'b': 1, 'c': 2, 'd': 3, 'e': 4}
    zero = [0 for x in range(len(embedding))]
    for path in list_all_paths():
        v = list(zero)
        for x in path:
            v[embedding[x]] = 1
        yield v

pprint(list(list_all_vectors()))

You'll notice that this is prone to recomputing the same sub-paths over and over for larger graphs. A quick fix for this might be some sort of memoization, though generally, there's probably better algorithms. (Try googling "enumerate all Hamiltonian paths".)

This looks like a tree algorithm to me.

From any starting point, make a list of all bus stops which can be reached directly.

Starting from A, that gives AD and AC

From each of these, repeat, except don't go to a stop we've already been to. (Don't add a letter if it is already in the list of stops we've been to)

AD - C (from AD, then add C)

AC-D, AC-B, AC-E

Again, repeat

ADC-B, ADC-E

ACD - (end of branch)

ACB-E (end of branch)

Repeat process for remaining starting points, Total possible routes, is the union of all paths discovered.

Instead of thinking of it as an array problem, think of it as building a set of strings. If you create data structures that represent the problem well, the algorithm is often much easier to understand and implement.

You need to start by entering the route map in a way that gives you all the neighbor stops

A: C,D

B: C,E

C: A,B,D,E

D: A,C

E: B,C

Hope this helps. That's all the time I have right now.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM