Efficiently generate permutations with ordering restrictions(possibly without backtracking)? - algorithm

I need to generate permutations of with ordering restrictions on ordering
for example, in the list [A,B,C,D]
A must always come before B, and C must always come before D. There also may or may not be E,F,G... that has no restrictions.
The input would look like this: [[A,B],[C,D],[E],[F]]
Is there a way to do this without computing unnecessary permutations or backtracking?

Normally, a permutations algorithm might look somewhat like this (Python):
def permutations(elements):
if elements:
for i, current in enumerate(elements):
front, back = elements[:i], elements[i+1:]
for perm in permutations(front + back):
yield [current] + perm
else:
yield []
You iterate the list, taking each of the elements as the first element, and combining them with all the permutations of the remaining elements. You can easily modify this so that the elements are actually lists of elements, and instead of just using the current element, you pop the first element off that list and insert the rest back into the recursive call:
def ordered_permutations(elements):
if elements:
for i, current in enumerate(elements):
front, back = elements[:i], elements[i+1:]
first, rest = current[0], current[1:]
for perm in ordered_permutations(front + ([rest] if rest else []) + back):
yield [first] + perm
else:
yield []
Results for ordered_permutations([['A', 'B'], ['C', 'D'], ['E'], ['F']]):
['A', 'B', 'C', 'D', 'E', 'F']
['A', 'B', 'C', 'D', 'F', 'E']
['A', 'B', 'C', 'E', 'D', 'F']
[ ... some 173 more ... ]
['F', 'E', 'A', 'C', 'D', 'B']
['F', 'E', 'C', 'A', 'B', 'D']
['F', 'E', 'C', 'A', 'D', 'B']
['F', 'E', 'C', 'D', 'A', 'B']
Note, though, that this will create a lot of intermediate lists in each recursive call. Instead, you could use stacks, popping the first element off the stack and putting it back on after the recursive calls.
def ordered_permutations_stack(elements):
if any(elements):
for current in elements:
if current:
first = current.pop()
for perm in ordered_permutations_stack(elements):
yield [first] + perm
current.append(first)
else:
yield []
The code might be a bit easier to grasp, too. In this case, you have to reserve the sublists, i.e. call it as ordered_permutations_stack([['B', 'A'], ['D', 'C'], ['E'], ['F']])

Related

Permutation of arrays

There are multiple sets of elements like:
[['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
I need an algorithm to get all possible combinations of each element from each set.
E.g.
['a', 'e', 'g']
['a', 'f', 'g']
['a', 'f', 'h']
['a', 'f', 'i']
['b', 'e', 'g']
...etc
You can use backtracking to solve this:
def get_perms(arr):
def backtrack(idx, partial_res, res):
if idx == len(arr):
res.append(partial_res[:])
return
for i in range(0, len(arr[idx])):
partial_res.append(arr[idx][i])
backtrack(idx+1, partial_res, res)
partial_res.pop()
res = []
backtrack(0, [], res)
return res
A quick test:
arr = [['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
get_perms(arr)
[['a', 'e', 'g'],
['a', 'e', 'h'],
['a', 'e', 'i'],
['a', 'f', 'g'],
['a', 'f', 'h'],
['a', 'f', 'i'],
['b', 'e', 'g'],
['b', 'e', 'h'],
['b', 'e', 'i'],
['b', 'f', 'g'],
['b', 'f', 'h'],
['b', 'f', 'i'],
['c', 'e', 'g'],
['c', 'e', 'h'],
['c', 'e', 'i'],
['c', 'f', 'g'],
['c', 'f', 'h'],
['c', 'f', 'i']]
The algorithm just goes over each inner list and adds each element to partial_res and calls itself recursively while incrementing the index to go to the next inner list.
What you want is a Cartesion product. Use itertools:
from itertools import product
matrix = [['a', 'b', 'c'], ['e', 'f'], ['g', 'h', 'i']]
for res in product(*matrix):
print(list(res))

Use dynamic programming to merge two arrays such that the number of repetitions of the same element is minimised

Let's say we have two arrays m and n containing the characters from the set a, b, c , d, e. Assume each character in the set has a cost associated with it, consider the costs to be a=1, b=3, c=4, d=5, e=7.
for example
m = ['a', 'b', 'c', 'd', 'd', 'e', 'a']
n = ['b', 'b', 'b', 'a', 'c', 'e', 'd']
Suppose we would like to merge m and n to form a larger array s.
An example of s array could be
s = ['a', 'b', 'c', 'd', 'd', 'e', 'a', 'b', 'b', 'b', 'a', 'c', 'e', 'd']
or
s = ['b', 'a', 'd', 'd', 'd', 'b', 'e', 'c', 'b', 'a', 'b', 'a', 'c', 'e']
If there are two or more identical characters adjacent to eachother a penalty is applied which is equal to: number of adjacent characters of the same type * the cost for that character. Consider the second example for s above which contains a sub-array ['d', 'd', 'd']. In this case a penalty of 3*5 will be applied because the cost associated with d is 5 and the number of repetitions of d is 3.
Design a dynamic programming algorithm which minimises the cost associated with s.
Does anyone have any resources, papers, or algorithms they could share to help point me in the right direction?

Python: List all possible paths in graph represented by dictionary

I have a dictionary with keys representing nodes and values representing possible nodes that the key can traverse to.
Example:
dependecyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H']}
I want to create a new dicitonary, ChainsDict, that will contain all 'values' that each 'key' can traverse to by means of dependecyDict.
For example, the output of the program with this example will be:
ChainsDict = {'A': ['D', 'C', 'B','E'], 'B':['A','D','C','E'], 'C':['B','A','D','E'], 'D':['C','B','A','E'], 'G': ['H']}
I think using a recursive algorithm is the best way to go about making a solution and I tried modifying a shortest path traversing algorithm as follows:
def helper(dependencyDict, ChainsDict):path = []
for key in dependencyDict:
path = path + [(recursiveRowGen(dependencyDict,key))]
for paths in path:
ChainsDict[paths[0]] = paths[1:]
print(finalLineDict)
def recursiveRowGen(dependencyDict,key,path = []):
path = path + [key]
if not key in dependencyDict:
print("no key: ",key)
return path
print(dependencyDict[key])
for blocking in dependencyDict[key]:
if blocking not in path:
newpath = recursiveRowGen(dependencyDict,blocking,path)
if newpath:
return newpath
return path
This code however is having problems capturing the correct output when a key in dependecyDict has more than one value.
I found a hacky solution but it doesn't feel very elegant. Any help is appreciated, thanks!
Here is a recursive solution:
Code
def get_chain_d(argDict):
def each_path(i,caller_chain):
a=[]
caller_chain.append(i)
b = argDict.get(i,[])
for j in b:
if j not in caller_chain:
a.append(j)
a.extend(each_path(j,caller_chain))
return a
return {i:each_path(i,[]) for i in argDict}
dependecyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H']}
print(get_chain_d(dependecyDict))
Output:
{'B': ['A', 'D', 'C', 'E'], 'A': ['D', 'C', 'B', 'E'], 'D': ['C', 'B', 'A', 'E'], 'C': ['B', 'A', 'D', 'E'], 'G': ['H']}
This is basically a graph traversal problem. You can represent each of your key as a node in a graph and its values are the nodes it is connected to.
You can do either Depth-first-search or breadth-first-search for graph. Of course, there's also an iterative and a recursive solution for each of these method. Here's an iterative implementation (I added a few conditionals to eliminate loops):
dependencyDict = { 'A': ['D'], 'B': ['A', 'E'], 'C': ['B'], 'D': ['C'], 'G':['H'] }
chainsDict = {}
for key in dependencyDict:
currKey = key
frontier = [key]
visited = []
while frontier:
currKey = frontier[0]
frontier.remove(currKey)
if dependencyDict.get(currKey,0) and (currKey not in visited) and (currKey not in frontier):
nodes = dependencyDict[currKey]
frontier.extend(nodes)
visited.append(currKey)
elif currKey in visited:
visited.remove(currKey)
elif dependencyDict.get(currKey,0) == 0:
visited.append(currKey)
for i in visited:
if i == key:
visited.remove(i)
chainsDict[key] = visited
print chainsDict
The result looks like:
{'A': ['D', 'C', 'B', 'E'], 'C': ['B', 'A', 'E', 'D'], 'B': ['A', 'E', 'D', 'C'], 'D': ['C', 'B', 'A', 'E'], 'G': ['H']}

How to calculate total weight of paths of directed weighted graph in DFS in one iteration?

G = (V,E) - a directed weighted graph.
D -> G (w:4)
D -> C (w:2)
D -> E (w:2)
C -> F (w:5)
C -> A (w:4)
B -> D (w:3)
B -> E (w:10)
G -> F (w:1)
E -> G (w:6)
A -> D (w:1)
A -> B (w:2)
picture
I use DFS to find all simple path between START=A node to END=F node:
def find_all_paths(self, start, end, path=[]):
path = path + [start]
if start == end:
return [path]
if start not in self.edges:
return []
paths = []
for node in self.edges[start]:
if node not in path:
paths.extend(self.find_all_paths(node, end, path))
return paths
Result:
['A', 'D', 'G', 'F']
['A', 'D', 'C', 'F']
['A', 'D', 'E', 'G', 'F']
['A', 'B', 'D', 'G', 'F']
['A', 'B', 'D', 'C', 'F']
['A', 'B', 'D', 'E', 'G', 'F']
['A', 'B', 'E', 'G', 'F']
I need to get result like this:
['A', 'D', 'G', 'F'], TOTAL_WEIGHT_OF_PATH = 6
['A', 'D', 'C', 'F'], TOTAL_WEIGHT_OF_PATH = 8
['A', 'D', 'E', 'G', 'F'], TOTAL_WEIGHT_OF_PATH = 10
....
....
Where TOTAL_WEIGHT_OF_PATH is sum of weights for each edge in path.
Of course I could just count the TOTAL_WEIGHT_OF_PATH value after getting result of DFS, but I need to calculate it into DFS steps for cutoff searching in condition based on TOTAL_WEIGHT_OF_PATH (e.g. TOTAL_WEIGHT_OF_PATH should be < MAX_WEIGHT_OF_PATH)
Well, notice that the TOTAL_WEIGT_OF_PATH (TWOP) to any node V (other then the root) is TWOP to the preceding node U plus the weight of the edge (U,V). TWOP to root is 0.
TWOP<sub>V</sub> = TWOP<sub>U</sub> + weight(U,V)
Any time you are expanding a new node on a path, you just need to store the TWOP to this node into it, so you don't need to calculate it every time.
Note, that if you visit a node again, using different path, you need to "calculate" a new weight.

How to get specific value within an array of arrays

I have an array (outside array) that contains three arrays (inside arrays), each of which have three elements.
array = [[a, b, c], [d, e, f], [g, h, i]]
I want to select the specific inside array using an index of the outside array and then select the value within the selected inside array based off its index. Here is what I tried:
array.each_index{|i| puts "letter: #{array[i[3]]} " }
I was hoping that would give me the following output
letter: c letter: f letter: i
but instead, I get
letter: [[a, b, c], [d, e, f], [g, h, i]]
I also tried
array.each_index{|i| puts "letter: #{array[i][3]} " }
but I get the same result. Please any suggestions are very appreciated. I need a simple explanation.
each_index is an Enumerable which goes through all indices and performs an action on each one. When it's done it will return your original collection as it's not its job to change it. If you want to output stuff on the screen via puts / print then each_index is fine.
If you want to create a new collection as a result of going through all the elements of an original collection, you should use map.
e.g.
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
new_array = array.map {|el| el[2]}
=> ["c", "f", "i"]
array.map iterates through array's elements so in every step |el| is an element, not an index, as in: ['a', 'b', 'c'] in the first iteration, ['d', 'e', 'f'] in the second one and so on...
Just pointing this out since I don't know what's the goal of what you're trying to do.
do it like this:
array.each_index{|i| puts "letter: #{array[i][2]} " }
Since you want letter at index 2, not 3.
Also array should be defined like this:
array = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
You could use map like so:
a = [['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
a.map(&:last)
# => ["c", "f", "i"]
Or if you really want the puts and not the collected values:
a.each {|v| puts "letter: #{v.last}"}
You could also use Ruby's Matrix class if there's more Matrix-y stuff you want to do:
require 'matrix'
m = Matrix[['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']]
last_column = m.column_vectors.last
# => Vector["c", "f", "i"]
You can now use any of the Vector methods on last_column, including to_a, which will put you back in familiar territory with the last column's values.
Arrays in ruby are indexed from 0, not from 1. So:
array.each_index{|i| puts "letter: #{array[i][2]} " }
should give you what you want.
You could try the below also:
p RUBY_VERSION
arr = [[1,2,3],[4,5,6],[11,12,13]]
arr.each{|x| p x; x.each_index{|i| p "Digit :: #{x[i]}" if i == 2} }
output:
"2.0.0"
[1, 2, 3]
"Digit :: 3"
[4, 5, 6]
"Digit :: 6"
[11, 12, 13]
"Digit :: 13

Resources