0

I am trying to complete a list of all possible routes between sets of values. My current data set is a set of lists with each list containing a pair (In and Out) of values

List = [[0.5,1],[0.75,1],[0.5,1.5],[0.75,1.5],[1,1.5],[0.5,2],[0.75,2],[1,2],[1.5,2],[0.75,3],[1,3],[1.5,3],[2,3],[1,4],[1.5,4],[2,4],[3,4],[1.5,6],[2,6],[3,6],[4,6],[3,8],[2,8],[4,8],[6,8],[3,10],[4,10],[6,10],[8,10],[4,12],[6,12],[8,12],[10,12],[6,14],[8,14],[10,14],[12,14],[6,16],[8,16],[10,16],[12,16],[14,16],[10,18],[12,18],[14,18],[16,18],[10,20],[12,20],[14,20],[16,20],[18,20],[14,24],[16,24],[18,24],[20,24],[22,24],[18,26],[20,26],[22,26],[24,26],[18,28],[20,28],[24,28],[26,28],[20,30],[24,30],[26,30],[28,30],[24,32],[26,32],[28,32],[30,32],[24,34],[26,34],[30,34],[32,34],[24,36],[26,36],[30,36],[32,36],[34,36],[24,42],[26,42],[30,42],[32,42],[34,42],[36,42],[36,48],[40,48],[42,48],[44,48],[46,48]]

I need to find a way to combine these to make a large list of all the routes between all values e.g for all routes between 0.5-2 it would list something similar to this:

[0.5,2];[0.5,1][1,2];[0.5,1][1,1.5][1.5,2];[0.5,1.5][1.5,2]

I believe that it would be possible using for loops in Python but I am a novice and my only solution would be via nesting as many loops as I have pairs which is extremely cumbersome and easy to make mistake whilst doing, I am unsure as to any alternative or more streamlined methods. Any help will be greatly appreciated

  • 1
    This is a graph. Consider using a graph library to help out https://stackoverflow.com/questions/40478752/generate-all-paths-in-an-efficient-way-using-networkx-in-python Hmm, they don't actually have an answer on that question. Maybe I'll go answer it. – Kenny Ostrom Mar 12 '20 at 17:12
  • Thank you, I have now begun looking into representing the problem as a network and it seems like it will be doable with networkx once I get my head round it – AHopefulFool Mar 12 '20 at 17:35

1 Answers1

1

You understand that this will produce a very large number of routes and that it could take a long time to compute if your list gets much larger.

What you can do is progressively build a list of routes that you extend with new routes formed by connecting pairs to the beginning or end of the routes you have so far:

List = [[0.5,1],[0.75,1],[0.5,1.5],[0.75,1.5],[1,1.5],[0.5,2],[0.75,2],[1,2],[1.5,2],[0.75,3],[1,3],[1.5,3],[2,3],[1,4],[1.5,4],[2,4],[3,4],[1.5,6],[2,6],[3,6],[4,6],[3,8],[2,8],[4,8],[6,8],[3,10],[4,10],[6,10],[8,10],[4,12],[6,12],[8,12],[10,12],[6,14],[8,14],[10,14],[12,14],[6,16],[8,16],[10,16],[12,16],[14,16],[10,18],[12,18],[14,18],[16,18],[10,20],[12,20],[14,20],[16,20],[18,20],[14,24],[16,24],[18,24],[20,24],[22,24],[18,26],[20,26],[22,26],[24,26],[18,28],[20,28],[24,28],[26,28],[20,30],[24,30],[26,30],[28,30],[24,32],[26,32],[28,32],[30,32],[24,34],[26,34],[30,34],[32,34],[24,36],[26,36],[30,36],[32,36],[34,36],[24,42],[26,42],[30,42],[32,42],[34,42],[36,42],[36,48],[40,48],[42,48],[44,48],[46,48]]

routes = []
for pair in List:
    inValue,outValue = pair
    newRoutes = [[pair]]
    for path in routes:
        if path[0][0] == outValue:
            newRoutes.append([pair] + path)
        if path[-1][-1] == inValue:
            newRoutes.append(path + [pair])
    routes.extend(newRoutes)

output:

print("number of routes:",len(routes))
for route in routes[:10]:
    print(route)
print("...")
for route in routes[-10:]:
    print(route)

number of routes: 5646987
[[0.5, 1]]
[[0.75, 1]]
[[0.5, 1.5]]
[[0.75, 1.5]]
[[1, 1.5]]
[[0.5, 1], [1, 1.5]]
[[0.75, 1], [1, 1.5]]
[[0.5, 2]]
[[0.75, 2]]
[[1, 2]]
...
[[0.75, 1], [1, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[0.5, 1.5], [1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[0.75, 1.5], [1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[1, 1.5], [1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[0.5, 1], [1, 1.5], [1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[0.75, 1], [1, 1.5], [1.5, 2], [2, 3], [3, 4], [4, 6], [6, 8], [8, 10], [10, 12], [12, 14], [14, 16], [16, 18], [18, 20], [20, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[22, 24], [24, 26], [26, 28], [28, 30], [30, 32], [32, 34], [34, 36], [36, 42], [42, 48]]
[[44, 48]]
[[46, 48]]

Note that this can be optimized by indexing the existing routes on their first/last value. Optimizing like that will make it twice as fast but it won't reduce the number of routes in the output:

routesFirst = dict()
routesLast  = dict()
routes      = []
for pair in List:
    inValue,outValue = pair
    newRoutes = [[pair]]
    for path in routesFirst.get(outValue,[]):
        newRoutes.append([pair]+path)
    for path in routesLast.get(inValue,[]):
        newRoutes.append(path+[pair])
    routes.extend(newRoutes)
    routesFirst.setdefault(inValue,[]).extend(newRoutes)
    routesLast.setdefault(outValue,[]).extend(newRoutes)

[EDIT] recursive approach (added for good measure):

If you are not going to go through all the paths, you can create a recursive generator function that will return them one by one. You can use it in a for loop (for example) that you intend to break when a certain condition is met.

Here is a very simple example (not optimized with meoization or anything):

def findRoutes(pairs,previous=None):
    for pair in pairs:
        inValue,outValue = pair
        if inValue != previous and previous is not None:
            continue
        yield [pair]
        for subRoute in findRoutes(pairs,outValue):
            yield [pair] + subRoute

output:

for route in findRoutes(List):
    if len(route) == 5:
       print("first 5 step route:",route)
       break

# first 5 step route: [[0.5, 1], [1, 1.5], [1.5, 2], [2, 3], [3, 4]]

[EDIT2] Finding a path from one number to another

To help with understanding the generator, I implemented a simple variant that will only return paths that connect two specific numbers. It still needs to use recursion but instead of going through all possible connections, it only looks at the ones that start from the given origin and stops when the destination is reached. For multi-step paths, the same function is called for each possible outValue that connect from the origin.

def findPath(pairs,origin,destination):
    for pair in pairs:
        inValue,outValue = pair
        if inValue != origin:
            continue
        if outValue == destination :
            yield [pair]
            continue
        for subPath in findPath(pairs,outValue,destination):
            yield [pair] + subPath

output:

List = [[0.5,1],[0.75,1],[0.5,1.5],[0.75,1.5],[1,1.5],[0.5,2],[0.75,2],[1,2],[1.5,2],[0.75,3],[1,3],[1.5,3],[2,3],[1,4],[1.5,4],[2,4],[3,4],[1.5,6],[2,6],[3,6],[4,6],[3,8],[2,8],[4,8],[6,8],[3,10],[4,10],[6,10],[8,10],[4,12],[6,12],[8,12],[10,12],[6,14],[8,14],[10,14],[12,14],[6,16],[8,16],[10,16],[12,16],[14,16],[10,18],[12,18],[14,18],[16,18],[10,20],[12,20],[14,20],[16,20],[18,20],[14,24],[16,24],[18,24],[20,24],[22,24],[18,26],[20,26],[22,26],[24,26],[18,28],[20,28],[24,28],[26,28],[20,30],[24,30],[26,30],[28,30],[24,32],[26,32],[28,32],[30,32],[24,34],[26,34],[30,34],[32,34],[24,36],[26,36],[30,36],[32,36],[34,36],[24,42],[26,42],[30,42],[32,42],[34,42],[36,42],[36,48],[40,48],[42,48],[44,48],[46,48]]

for path in findPath(List,1,3):
    print(path)


[[1, 1.5], [1.5, 2], [2, 3]]
[[1, 1.5], [1.5, 3]]
[[1, 2], [2, 3]]
[[1, 3]]
Alain T.
  • 40,517
  • 4
  • 31
  • 51
  • You may want to consider a generator. – Kenny Ostrom Mar 12 '20 at 18:27
  • That would require recursivity which I chose not to use because of the OP's indication that he/she is a novice – Alain T. Mar 12 '20 at 18:29
  • Good point, +1, but I want it noted that this should be a generator, even if they haven't learned about it yet. :) – Kenny Ostrom Mar 12 '20 at 18:33
  • Thank you for the reply, as a novice I feel that some of this is a little above me but I am going to try to learn more on your extended answer. – AHopefulFool Mar 13 '20 at 09:25
  • For the purpose of this exercise I was only aiming to find all the routes but now that I have seen the total number (5646987!!) I realise that I should probably use an approach of only generating the ones I need rather than attempting to store them as a list and search for the ones I require. I will look into using your generator approach to find all between a set of points – AHopefulFool Mar 13 '20 at 09:33