Accumulating results of a recursive function in python - python

Consider the following function to permute numbers in a list:
def permute(numbers, N=0):
# base case
if N == len(numbers):
print numbers
return
for i in range(len(numbers)-N):
# swapping relevant elements
element=numbers.pop(N+i)
numbers.insert(N,element)
# recursive call
permute(numbers, N+1)
# swapping back relevant elements when backtracking
element=numbers.pop(N)
numbers.insert(N+i,element)
numbers=[1,2,3]
permute(numbers)
Why is it that when I run the above code, it produces the correct output:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
but when I'm trying to accumulate the result to a list:
def permute(numbers, permutations, N=0):
# base case
if N == len(numbers):
print numbers
permutations.append(numbers)
return
for i in range(len(numbers)-N):
# swapping relevant elements
element=numbers.pop(N+i)
numbers.insert(N,element)
# recursive call
permute(numbers, permutations, N+1)
# swapping back relevant elements when backtracking
element=numbers.pop(N)
numbers.insert(N+i,element)
numbers=[1,2,3]
permutations=[]
permute(numbers, permutations)
print "-----------"
for p in permutations:
print p
The output is this:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
-----------
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
[1, 2, 3]
Not quite what I expected...

All of the items you're appending to permutations are actually the same list. Changing one changes all of the others. The easiest fix for this is to make a copy of the list before appending it, so future changes to numbers has no effect on the already-appended results.
def permute(numbers, permutations, N=0):
# base case
if N == len(numbers):
print numbers
permutations.append(numbers[:])
return
Result:
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
---------
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]

Related

Permutation with recursion not printing all values in python?

I am writing python code to print all permutation of a number.
Below is my code:
a=[1,2,3,4]
for i in range(len(a)):
temp=a[:]
temp[0],temp[i]=temp[i],temp[0]
def p(temp,i):
k=i+1
if k ==len(a)-1:
print(temp)
return
temp[k],temp[k+1]=temp[k+1],temp[k]
p(temp,k)
temp[k],temp[k+1]=temp[k+1],temp[k]
p(temp,k)
p(temp,i=0)
The idea is to replace every integer at first place and permutate remaining.
That's what this for loop is doing:
for i in range(len(a)):
temp=a[:]
temp[0],temp[i]=temp[i],temp[0]
But,for every permutation starting with i,it only prints 4 permutations.
for ex:
Starting with 1,the permutations should be:
[1,2,3,4]
[1,2,4,3]
[1,3,2,4]
[1,3,4,2]
[1,4,3,2]
[1,4,2,3]
But,its only printing
[1,2,3,4]
[1,2,4,3]
[1,3,2,4]
[1,3,4,2]
4 at second place is not getting printed.
Missing:
[1,4,3,2]
[1,4,2,3]
Can anyone tell me what am I doing wrong?
Edited code:
def p(temp, k=0):
if k == len(temp):
print(temp)
return
for i in range(k, len(temp)):
temp[k], temp[i] = temp[i], temp[k]
p(temp, k+1)
temp[k], temp[i] = temp[i], temp[k]
p([1,2,3,4])
result:
[1, 2, 3, 4]
[1, 2, 4, 3]
[1, 3, 2, 4]
[1, 3, 4, 2]
[1, 4, 3, 2]
[1, 4, 2, 3]
[2, 1, 3, 4]
[2, 1, 4, 3]
[2, 3, 1, 4]
[2, 3, 4, 1]
[2, 4, 3, 1]
[2, 4, 1, 3]
[3, 2, 1, 4]
[3, 2, 4, 1]
[3, 1, 2, 4]
[3, 1, 4, 2]
[3, 4, 1, 2]
[3, 4, 2, 1]
[4, 2, 3, 1]
[4, 2, 1, 3]
[4, 3, 2, 1]
[4, 3, 1, 2]
[4, 1, 3, 2]
[4, 1, 2, 3]
Heres a correction for your process
a=[1,2,3,4]
def p(temp,i):
k=i+1
if k ==len(a)-1:
print(temp)
return
temp[k],temp[k+1]=temp[k+1],temp[k]
prev = temp.copy()
p(temp,k)
temp[k],temp[k+1]=temp[k+1],temp[k]
p(temp,k)
temp[k],temp[k+1]=temp[k+1],temp[k]
if temp != prev:
p(temp,k)
for i in range(len(a)):
temp=a[:]
temp[0],temp[i]=temp[i],temp[0]
p(temp,i=0)
The problem was that you were only rearranging the temp list 2 times in the actual call of the function p from the for loop. Calling it three times made it to contain '4' in the second place of the permutation. The if statement I added is to check if the previous rearranged temp is not the as the current rearrange temp, this removed duplicate results which occurred when calling the function p three times. Although I do recommend Hamid's answer.
The logic seems to be wrong. You are swapping the neighboring elements. Because of that, you are unable to generate all the permutations. Ideally, you want to recursively repeat the logic of fixing the first element.

How to weave two lists using recursion in Python

I want to weave two lists and output all the possible results.
For example,
input: two lists l1 = [1, 2], l2 = [3, 4]
output: [1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [3, 1, 2, 4], [3, 1, 4, 2], [3, 4, 1, 2]
Note: I need to keep the order in each list (e.g. 1 is always before 2, and 3 is always before 4)
The way I am solving this is by removing the head from one list, recursing, and then doing the same thing with the other list. The code is below:
all_possibles = []
def weaveLists(first, second, added):
if len(first) == 0 or len(second) == 0:
res = added[:]
res += first[:]
res += second[:]
all_possibles.append(res)
return
cur1 = first[0]
added.append(cur1)
first = first[1:]
weaveLists(first, second, added)
added = added[:-1]
first = [cur1] + first
cur2 = second[0]
added.append(cur2)
second = second[1:]
weaveLists(first, second, added)
added = added[:-1]
second = [cur2] + second
weaveLists([1, 2], [3, 4], [])
print(all_possibles)
The result I got is:
[[1, 2, 3, 4], [1, 3, 2, 4], [1, 3, 4, 2], [1, 3, 1, 2, 4], [1, 3, 1, 4, 2], [1, 3, 1, 4, 1, 2]]
I couldn't figure out why for the last three lists, the heading 1 from the first list is not removed.
Can anyone help? Thanks!
The reason you get those unexpected results is that you mutate added at this place:
added.append(cur1)
...this will affect the caller's added list (unintentionally). While the "undo" operation is not mutating the list:
added = added[:-1]
This creates a new list, and therefore this "undo" action does not roll back the change in the list of the caller.
The easy fix is to replace the call to append with:
added = added + [cur1]
And the same should happen in the second block.
It is easier if you pass the new values for the recursive call on-the-fly, and replace those two code blocks with just:
weaveLists(first[1:], second, added + [first[0]])
weaveLists(first, second[1:], added + [second[0]])
Here is another way to do it: we generate the possible indices of the items of the first list inside the weaved list, and fill the list accordingly.
We can generate the indices with itertools.combinations: it's the combinations of the indices of the weaved list, taking len(first_list) of them each time.
from itertools import combinations
​
def weave(l1, l2):
total_length = len(l1) + len(l2)
# indices at which to put items from l1 in the weaved output
for indices in combinations(range(total_length), r=len(l1)):
out = []
it1 = iter(l1)
it2 = iter(l2)
for i in range(total_length):
if i in indices:
out.append(next(it1))
else:
out.append(next(it2))
yield out
Sample run:
l1 = [1, 2]
l2 = [3, 4]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4]
[1, 3, 2, 4]
[1, 3, 4, 2]
[3, 1, 2, 4]
[3, 1, 4, 2]
[3, 4, 1, 2]
Another sample run with a longer list:
l1 = [1, 2]
l2 = [3, 4, 5]
​
for w in weave(l1, l2):
print(w)
​
[1, 2, 3, 4, 5]
[1, 3, 2, 4, 5]
[1, 3, 4, 2, 5]
[1, 3, 4, 5, 2]
[3, 1, 2, 4, 5]
[3, 1, 4, 2, 5]
[3, 1, 4, 5, 2]
[3, 4, 1, 2, 5]
[3, 4, 1, 5, 2]
[3, 4, 5, 1, 2]

Why does recursion overwrite values in a list that is passed along?

This recusion is adapted from http://www.geeksforgeeks.org/print-all-possible-combinations-of-r-elements-in-a-given-array-of-size-n/ and does indeed print out all possible unique combination of arr with length r.
What I want from it is to save all possible combination in a list to further use this algorithm in another program. Why is the values overritten in combArray in the recustion and how do I solve this?
def combRecursive(arr, data, start, end, index, r, combArray):
if index == r:
combArray.append(data)
return combArray
i = start
while True:
if i > end or end - i + 1 < r - index:
break
data[index] = arr[i]
combArray = combRecursive(arr, data, i + 1, end, index + 1, r, combArray)
i += 1
return combArray
def main():
arr = [1, 2, 3, 4, 5]
r = 3
n = len(arr)
data = [9999999, 9999999, 9999999]
combArray = []
combArray = combRecursive(arr, data, 0, n-1, 0, r, combArray)
print("All possible unique combination is: ")
for element in combArray:
print(element)
Result as of now:
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
[3, 4, 5]
What I want:
[1, 2, 3]
[1, 2, 4]
[1, 2, 5]
[1, 3, 4]
[1, 3, 5]
[1, 4, 5]
[2, 3, 4]
[2, 3, 5]
[2, 4, 5]
[3, 4, 5]
You initialize data, and from then on make changes to it & add it to combArray, which means you are always adding the same array to combArray, so all of its elements are the same. If you want the elements to be distinct arrays, you need to make a new array for each you want to add to combArrays (by, for example, making a copy of data).

All ways of partitioning a list into two non-empty lists

[0.0, 1.0, 2.0, 3.0, 4.0]
I have 5 numbers and two groups, left and right.
Each number has two choices - it can go left or right.
I need a list that contains all partitioning of the list [0,1,2,3,4] into two non empty parts. For example: [([0], [1,2,3,4]), ([0,1], [2,3,4]), ...,]
Note that there are a total of (2^5 -2)/2 partitioning - order doesn't matter and I don't want repeats. Meaning I don't want something like this (if my list was [1,2,3,4]):
[] [1, 2, 3, 4]
[1] [2, 3, 4]
[2] [1, 3, 4]
[1, 2] [3, 4]
[3] [1, 2, 4]
[1, 3] [2, 4]
[2, 3] [1, 4]
[1, 2, 3] [4]
[4] [1, 2, 3]
[1, 4] [2, 3]
[2, 4] [1, 3]
[1, 2, 4] [3]
[3, 4] [1, 2]
[1, 3, 4] [2]
[2, 3, 4] [1]
[1, 2, 3, 4] []
I've looked into all of the itertools functions and none seem to work.
Edit:
for list [i for i in range(16)], which has 16 elements, If I do the following, this is what I see:
n = len(l)//2 + 1
>>> xs = list(chain(*[combinations(l, i) for i in range(1, n)]))
>>> pairs = [(list(x), list(set(l) - set(x))) for x in xs]
>>> print len(pairs)
39202
>>> (2**16-2)/2
32767
In fact, it doesn't work for a list with 6 elements either. I don't see why...
The problem occurs for all even length lists. For example, when I try a length 2 list, I get:
[([0.0], [1.0]), ([1.0], [0.0])]
The stuff is there in itertools, maybe you just weren't looking in the right places.
Here is teh codez:
from collections import OrderedDict
from itertools import chain, combinations
def partition(L):
n = len(L)//2 + 1
xs = chain(*[combinations(L, i) for i in range(1, n)])
pairs = (tuple(sorted([x, tuple(set(L) - set(x))])) for x in xs)
return OrderedDict.fromkeys(pairs).keys()
Output:
>>> for pair in partition([1,2,3,4]):
... left, right = map(list, sorted(pair, key=len))
... print left, right
...
[1] [2, 3, 4]
[2] [1, 3, 4]
[3] [1, 2, 4]
[4] [1, 2, 3]
[1, 2] [3, 4]
[1, 3] [2, 4]
[1, 4] [2, 3]

.append() will not add a list to a list of lists

I wrote a script to find all permutations of a list of numbers in lexicographic order. The output is correct when immediately printed, but it gets changed if I append it to a list. In the output of my script, first you see the list with a single element, then the lists I append to that element, and finally you see the finished list after the sub-lists are added:
Find all permutations of 1 -> x. x = ? 3
[[1, 2, 3]]
[1, 2, 3]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
[[1, 3, 2], [2, 3, 1], [2, 3, 1], [3, 2, 1], [3, 2, 1], [3, 2, 1]]
As you can see, the initial [1, 2, 3] list is not even in the final output. Below is my code. Can anybody tell me what the heck is going on?
permutation_range = int(input('Find all permutations of 1 -> x. x = ? '))
def permutation_finder(x):
permutation = list(range(1, 1+x))
rev_permutation = permutation[::-1]
permutation_list = [permutation]
print(permutation_list, '\n\n') #bugcheck print
while permutation != rev_permutation:
permutation, index_k = step_1(permutation)
permutation, index_k, index_l = step_2(permutation, index_k)
permutation, index_k = step_3(permutation, index_k, index_l)
permutation = step_4(permutation, index_k)
permutation_list.append(permutation)
print(permutation) #bugcheck print
return permutation_list
def step_1(permutation):
for val in permutation[:-1][::-1]:
index_k = permutation.index(val)
if val < permutation[index_k+1]:
return permutation, index_k
def step_2(permutation, index_k):
for val in permutation[index_k+1:][::-1]:
if val > permutation[index_k]:
index_l = permutation.index(val)
return permutation, index_k, index_l
def step_3(permutation, index_k, index_l):
a_k, a_l = permutation[index_k], permutation[index_l]
permutation[index_k], permutation[index_l] = a_l, a_k
return permutation, index_k
def step_4(permutation, index_k):
front = permutation[:index_k+1]
back = permutation[index_k+1:]
back.reverse()
permutation = front + back
return permutation
print('\n\n', permutation_finder(permutation_range))
Because a list in list is actually a reference, not converting to value when you use append(). So when you edit the list afterwards, the previously added one also changes.
Adding [:] to copy the list before appending could fix it:
def permutation_finder(x):
permutation = list(range(1, 1+x))
rev_permutation = permutation[::-1]
permutation_list = [permutation[:]]
print(permutation_list, '\n\n') #bugcheck print
while permutation != rev_permutation:
permutation, index_k = step_1(permutation)
permutation, index_k, index_l = step_2(permutation, index_k)
permutation, index_k = step_3(permutation, index_k, index_l)
permutation = step_4(permutation, index_k)
permutation_list.append(permutation[:])
print(permutation) #bugcheck print
return permutation_list
(Note the [:] part)
And after the change I can get:
$ python permutation.py
Find all permutations of 1 -> x. x = ? 3
[[1, 2, 3]]
[1, 3, 2]
[2, 1, 3]
[2, 3, 1]
[3, 1, 2]
[3, 2, 1]
[[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]]
Should be what you want :)

Categories

Resources