I have a list of lists and am looking for something similar to combinations or permutations, but there are conditions that may result in good "Path" or "Dead_End". If "Dead_End", it should index to the next choice in the list.
Example:
AList = [[1, 2, 3],[2, 0, 3],[3, 1, 0],[1, 0, 2]]N = 0
Index = 0
#AList[N][Index]=1, AList[2][2]=0
I would like to start with the value AList[N][Index], which at the start is [0][0] and therefore equals 1 and assign that to N. After N is assigned 1, we can never go back to that List and the Path = [1].
Then I would have AList[N][Index], which is AList[1][0] and that equals 2. Assign that to N. Path.append(N) and I would have Path = [1, 2].
AList[N][Index] would next equal 3, N = 3, and Path would become [1, 2, 3]
Now AList[N][Index] would equal 1. This would be a Dead_Path and not a good solution because 1 is already in Path and we can't go back to it. So I want to index over to the next choice in the list. Index = Index + 1. That would result in AList[N][Index] = 0. Path = [1, 2, 3, 0]
That is a good Path and there are many good Paths and many Dead_Ends. For example, another good Path would be Path = [1, 0, 2, 3].
Is this a multi-nested IF, For, While loop scenario or is there an itertools function to test out all of the Paths and Dead_Ends? And the List of list could be more complicated and not uniform to create even more Dead_Ends, like:
BList = [[1, 4, 3],[2, 0, 3, 4],[3, 4, 0],[1, 2],[3, 2, 1, 0]]
Edited:
I couldn't deal with all of the Dead_Ends. All of the loops and indexing got me confused, so I think the best solution is that I use itertools and list all path combinations and allow revisiting back to previous node. Then I would use set() function to eliminate the lists that revisited a node. In the end, I'm left with Perfect_Paths hitting each node once. I don't know if this is the fastest / cleanest, but it worked. Code below:
Perfect_Path = list()
i = 0 All_Paths = list(itertools.product(*Alist))for i in range(len(All_Paths)):if len(set(All_Paths[i])) == len(Alist): #filter out paths that revisited a node, which means it would have same number repeated in listPerfect_Path.append(All_Paths[i])print ("The perfect paths are ", Perfect_Path)
Edit: Well my code above works for a small Alist, but to itertools all combinations for a slightly larger list, say 10 data nodes all of a sudden becomes millions of paths. And then I have to set() them to eliminate choices. Back to the drawing board for me.
Reading through the text of the question again and again I detected two variants of rules for finding the right answer and will provide here both of them without asking which one is the one OP had in mind asking the question.
Let's start with the easy one. The path through the elements of the matrix can jump from one element to any other element and the only restriction for the path is that there are no same values in the path allowed. This makes it very easy to find all paths through the values of the matrix and it goes as follows:
Step1: make out of all of the matrix values a flat set with unique values
Step2: create all possible permutations of this set
Let's demonstrate with code that this variant works even on a quite large matrix:
theMatrix = [[1, 1, 3, 2, 1, 3, 3, 3, 2, 1, 1, 3],[1, 2, 1],[2, 3, 3, 1, 3, 3, 2, 1, 1, 1, 3, 3, 1, 3, 3],[3, 1, 3, 1, 2],[2, 3, 3, 1, 3, 3, 2, 1, 1, 1, 3],[1, 2],[1, 2, 1],[2, 3, 3, 1, 3, 3, 1, 1, 3, 3, 1, 3, 3],[3, 1, 3, 1, 2],[2, 3, 3, 1, 3, 3, 2, 1, 1, 1, 3],[1, 2],[2, 3, 3, 1, 3, 3, 1, 1, 3, 3, 1, 3, 3],[3, 1, 3, 1, 2],[2, 3, 3, 1, 3, 3, 2, 1, 1, 1, 3],[3, 2, 1, 3, 1]]
setOnlyDifferentMatrixValues = set()
for row in range(len(theMatrix)):for column in range(len(theMatrix[row])):setOnlyDifferentMatrixValues.add(theMatrix[row][column])
from itertools import permutations
allPossiblePaths = permutations(setOnlyDifferentMatrixValues)
print(list(allPossiblePaths))
The code above outputs:
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]
Now the more complex variant with more lines of code. The code is necessary, because there are more restrictions there and the path can go only through matrix elements which are neighbors (horizontal, vertical and diagonal). In this case not all paths have the same length and the same elements in them:
def getAllValidPathsFrom(myMatrix):
# def getDictRepresentationOf(myMatrix):dctOfMatrix = {}for row in range(len(myMatrix)):for column in range(len(myMatrix[row])):currPoint = (column, row)dctOfMatrix[currPoint] = myMatrix[row][column]lstIndicesOfAllMatrixPoints = list(dctOfMatrix.keys())setAllPossiblePaths = set()from itertools import permutationspermutationsIndicesOfAllMatrixPoints = permutations(lstIndicesOfAllMatrixPoints)cutBranchAt = ()sizeOfCutBranch = 0for pathCandidate in permutationsIndicesOfAllMatrixPoints: # print(pathCandidate, type(pathCandidate))if sizeOfCutBranch > 0:# print( "sizeOfCutBranch > 0", cutBranchAt )cutBranchAt = tuple(cutBranchAt)while cutBranchAt == pathCandidate[0:sizeOfCutBranch]:try:pathCandidate = next(permutationsIndicesOfAllMatrixPoints)except:breaklstPossiblePath = []prevIndexTuple = pathCandidate[0]lstPossiblePath.append(prevIndexTuple)for currIndexTuple in pathCandidate[1:]:if (abs(currIndexTuple[0]-prevIndexTuple[0]) > 1) or (abs(currIndexTuple[1]-prevIndexTuple[1]) > 1) :sizeOfCutBranch = len(lstPossiblePath)+1cutBranchAt = tuple(lstPossiblePath+[currIndexTuple])break # current path indices not allowed in path (no jumps)else:if dctOfMatrix[currIndexTuple] in [ dctOfMatrix[index] for index in lstPossiblePath ] : sizeOfCutBranch = len(lstPossiblePath)+1cutBranchAt = tuple(lstPossiblePath+[currIndexTuple])break # only values different from all previous are allowed else:sizeOfCutBranch = 0cutBranchAt = ()lstPossiblePath.append(currIndexTuple)prevIndexTuple = currIndexTupleif len(lstPossiblePath) > 1 and tuple(lstPossiblePath) not in setAllPossiblePaths: setAllPossiblePaths.add(tuple(lstPossiblePath))return setAllPossiblePaths, dctOfMatrix
#:def getAllValidSkiingPathsFromtheMatrix = [[3, 1, 3],[1, 1],[1, 2, 1, 3]]lstAllPossiblePaths, dctOfMatrix = getAllValidPathsFrom(theMatrix)
setDifferentPossiblePaths = set(lstAllPossiblePaths)
setTpl = set()
for item in setDifferentPossiblePaths:lstTpl = []for element in item:lstTpl.append(dctOfMatrix[element])setTpl.add(tuple(lstTpl))
print(setTpl)
The code above runs over a small matrix as the large one would kill it computationally. Because of special arrangement of values in the small matrix it is possible to demonstrate here that not all permutations are generated because of the more restrictive rules for building the path. Let's compare the outputs for the variant one and the variant two of the answer:
[(1, 2, 3), (1, 3, 2), (2, 1, 3), (2, 3, 1), (3, 1, 2), (3, 2, 1)]{(1, 2), (1, 3), (2, 1, 3), (3, 1), (2, 1), (3, 1, 2)}
Because in the second variant the path has to go through adjacent matrix elements not all permutations of the longest path are there and there are also shorter paths included.