Indexing a list of permutations in Python - python

This code generates the list of all permutations:
def permute(xs, low=0):
if low + 1 >= len(xs):
yield xs
else:
for p in permute(xs, low + 1):
yield p
for i in range(low + 1, len(xs)):
xs[low], xs[i] = xs[i], xs[low]
for p in permute(xs, low + 1):
yield p
xs[low], xs[i] = xs[i], xs[low]
for p in permute(['A', 'B', 'C', 'D']):
print p
What I would like to do create an index for the list of permutations so that if I call a number I can access that particular permutation.
For example:
if index.value == 0:
print index.value # ['A','B','C','D']
elif index.value == 1:
print index.value # ['A','B','D','C']
#...
I am new to Python, thank you in advance for any guidance provided.

You can also create a new function getperm to get the permutation index from your generator:
def getperm(index,generator):
aux=0
for j in generator:
if aux == index:
return j
else:
aux = aux +1
In: getperm(15,permute(['A', 'B', 'C', 'D']))
Out: ['C', 'A', 'D', 'B']

Iterators does not support "random access". You'll need to convert your result to list:
perms = list(permute([....]))
perms[index]

Like levi said, it sounds like you want to use a dictionary.
The dictionary will look something like this:
#permDict = {0:['A', 'B', 'C', 'D'], 1:['A', 'B', 'D', 'C'], ...}
permDict = {}
index = 0
for p in permute(['A', 'B', 'C', 'D']):
permDict[index] = p
index += 1
Then just get a value according to the key you have assigned.
if index == 0:
print permDict[0] # ['A','B','C','D']
elif index == 1:
print permDict[1] # ['A','B','D','C']
#...
Or just store each permutation in a list and call those indices.
permList = [p for p in permute(['A', 'B', 'C', 'D'])]
#permList[0] = ['A', 'B', 'C', 'D']
#permlist[1] = ['A', 'B','D', 'C']

You can generate the desired permutation directly (without going through all previous permutations):
from math import factorial
def permutation(xs, n):
"""
Return the n'th permutation of xs (counting from 0)
"""
xs = list(xs)
len_ = len(xs)
base = factorial(len_)
assert n < base, "n is too high ({} >= {})".format(n, base)
for i in range(len_ - 1):
base //= len_ - i
offset = n // base
if offset:
# rotate selected value into position
xs[i+1:i+offset+1], xs[i] = xs[i:i+offset], xs[i+offset]
n %= base
return xs
then
>>> permutation(['A', 'B', 'C', 'D'], 15)
['C', 'B', 'D', 'A']

Related

Python permutation with recursion

I'm having trouble writing a function to display all of the permutations of a list. Here is my code. The main function is given and I cannot change it. When I ran the code, nothing showed up. What's wrong with my code?
def perm(lst):
if len(lst) == 0:
return []
if len(lst) == 1:
return [lst]
l = []
for i in range(len(lst)):
m = lst[i]
rem = lst[:i] + lst[i+1:]
for p in perm(rem):
l.append([m] + p)
return l
def main():
list = input().split(',')
start = []
perm(list)
if __name__ == "__main__":
main()
The input is: A,B,C
The expected output is:
['A', 'B', 'C']
['A', 'C', 'B']
['B', 'A', 'C']
['B', 'C', 'A']
['C', 'A', 'B']
['C', 'B', 'A']
Nothing's wrong with your code. Just need to print the results.
def main():
list = input().split(',')
print perm(list)
A bit of advice: it's a bad idea to use list as variable name, since it will alias the built-in type list.
Your function is fine as far as the permutations go. However, it only returns the list of permutations. It appears, the task as given expects it to print them as well. Just insert right before (or instead of) the return statement:
def perm(lst, lvl=0): # add recursion level as parameter
if len(lst) == 0:
return []
if len(lst) == 1:
return [lst]
l = []
for i in range(len(lst)):
m = [lst[i]]
for p in perm(lst[:i] + lst[i+1:], lvl+1):
l.append(m + p)
if lvl == 0: # only print if it is the top call
for p in l:
print(p)
return l
There is no print in your code, which is why you don't have any output. Because your function is recursive, and at deeper recursion levels the list is shorter, you'll only want to print at the outermost level.
It is probably the easiest if you wrap your function in yet another function, and only print in that wrapping function. Rename the original function to something else, and call the outer one perm:
def perm(lst):
# The original function, but renamed:
def recurse(lst):
print('recurse', lst)
if len(lst) == 0:
return []
if len(lst) == 1:
return [lst]
l = []
for i in range(len(lst)):
m = lst[i]
rem = lst[:i] + lst[i+1:]
for p in recurse(rem): # call the renamed function
l.append([m] + p)
return l
# only print in the wrapping function
l = recurse(lst)
print (l)
return l
With some use of list comprehension and moving the len(lst) == 0 test out of the recursive function, you can rewrite the above to:
def perm(lst):
def recurse(lst):
if len(lst) == 1:
return [lst]
return [[m] + p for i, m in enumerate(lst) for p in recurse(lst[:i] + lst[i+1:])]
l = recurse(lst) if len(lst) > 0 else []
print(l)
return l
You can also use itertools.permutations for this:
>>> from itertools import permutations
>>> list(permutations(['A','B','C']))
[('A', 'B', 'C'), ('A', 'C', 'B'), ('B', 'A', 'C'), ('B', 'C', 'A'), ('C', 'A', 'B'), ('C', 'B', 'A')]

How can I reverse a section of a list using a loop in Python?

I'm in need of a little help reversing a section of a list in Python using a loop.
I have a list: mylist = ['a', 'b', 'c', 'd', 'e', 'f']
Also have an index number, this number will tell where to start the reversing. For example, if the reverse-index number is 3, it needs to be something like this: ['d', 'c', 'b', 'a', 'e', 'f']
What I currently have:
def list_section_reverse(list1, reverse_index):
print("In function reverse()")
n_list = []
for item in range( len(list1) ):
n_list.append( (list1[(reverse_index - 1) - item]) )
item += 1
return n_list
mylist = ['a', 'b', 'c', 'd', 'e', 'f']
print( list_section_reverse(mylist, 3) )
Which returns ['c', 'b', 'a', 'f', 'e', 'd']
How can I alter my code, so that it prints out ['d', 'c', 'b', 'a', 'e', 'f']?
You can simply use:
def list_section_reverse(list1, reverse_index):
return list(reversed(list1[:reverse_index+1])) + list1[reverse_index+1:]
Edit: The problem with your existing solution is that you keep reversing after reverse_index. If you have to use a loop, try this:
def list_section_reverse(list1, reverse_index):
print("In function reverse()")
n_list = list1[:]
for i in range(reverse_index + 1):
n_list[i] = list1[-i-reverse_index]
return n_list
mylist = ['a', 'b', 'c', 'd', 'e', 'f']
print(list_section_reverse(mylist, 3))
The pythonic solution:
list1[reverse_index::-1] + list1[reverse_index+1:]
Now, that's not using loops like you asked. Well, not explicitly... Instead we can break down the above into its constituent for loops.
def list_section_reverse(list1, reverse_index):
if reverse_index < 0 or reversed_index >= len(list1):
raise ValueError("reverse index out of range")
reversed_part = []
for i in range(reverse_index, -1, -1): # like for i in [n, n-1, ..., 1, 0]
reversed_part.append(list1[i]
normal_part = []
for i in range(reverse_index + 1, len(list1)):
normal_part.append(list1[i])
return reversed_part + normal_part
Is it allowed to make a copy of the list?
def list_section_reverse(list1, reverse_index):
print("In function reverse()")
n_list = [ element for element in list1 ]
for item in range( reverse_index + 1 ):
n_list[ item ] = list1[ reverse_index - item ]
item += 1
return n_list
mylist = ['a', 'b', 'c', 'd', 'e', 'f']
print(list_section_reverse(mylist, 3))
Outs:
In function reverse()
['d', 'c', 'b', 'a', 'e', 'f']
You can modify the list inplace using a slice.
mylist[:4] = mylist[:4][::-1]
You can try this. This makes use of no slicing, and can use either a while loop or for loop.
def reversed_list(my_list, index):
result = []
list_copy = my_list.copy()
i = 0
while i < index+1:
result.append(my_list[i])
list_copy.remove(my_list[i])
i+=1
result.reverse()
return result + list_copy
Or with a for loop
def reversed_list(my_list, index):
result = []
list_copy = my_list.copy()
for i in range(len(my_list)):
if i < index + 1:
result.append(my_list[i])
list_copy.remove(my_list[i])
result.reverse()
return result + list_copy

Python yield statement issue in recursive function?

Now, I learn to how to use yield in python program. so I want to implement word permutation in python.
def permuteArr(arr, index=0):
'''(list, int) -> list
Get all the permutation of the elements in list.
>>> arr = ['a', 'b', 'c']
>>> for val in permuteArr(arr):
print val
'''
if index == len(arr):
yield arr
else:
for idx in range(index, len(arr)):
arr[idx], arr[index] = arr[index], arr[idx]
for val in permuteArr(arr, idx+1):
yield val
arr[idx], arr[index] = arr[index], arr[idx]
if '__name__' == '__main__':
arr = ['a', 'b', 'c']
for val in permuteArr(arr, 0):
print val
However, I run it in python shell under window, the results are not correct. There are only four results.
>>> for v in permuteArr(['a', 'b', 'c']):
print v
['a', 'b', 'c']
['a', 'c', 'b']
['b', 'a', 'c']
['c', 'b', 'a']
Is anything wrong when I use yield or in my program?
In loop for val in permuteArr(arr, idx+1): replace idx + 1 on index + 1

function for nCk chose k elements from list of n in python

I'm trying to write a function that makes nCk from the list in python
for example from the list for pairs:
['a', 'b', 'c']
output should be:
[['a','b'],['a','c'],['b','c']]
however I'm getting no output
here's my attempt:
def chose(elements, k):
output = []
for i in range(len(elements)):
if k == 1:
output.append(elements[i])
for c in chose(elements[i+1:], k-1):
output.append(elements[i])
output.append(c)
return output
print chose(['a', 'b', 'c'],2)
can you kindly tell what is wrong with function
Use itertools.combinations if you want to find all combinations:
from itertools import combinations
a = ['a', 'b', 'c']
result = [list(i) for i in combinations(a,2)]
The documentation and implementation of the combinations() function can be found on here ...
Update
This function should do what you want:
def chose(elements, k):
output = []
if k == 1:
return [[i] for i in elements]
else:
for i in range(len(elements)):
head = elements[i]
tails = chose(elements[i+1:], k-1)
output += [[head] + tail for tail in tails]
return output
print chose(['a','b','c'], 2)
You can use a powerset without using any imports:
def power_set(items,k):
n = len(items)
for i in xrange(2**n):
combo = []
for j in xrange(n):
if (i >> j) % 2 == 1:
combo.append(items[j])
if len(combo) == k:
yield combo
print(list(power_set(['a', 'b', 'c'],2)))
[['a', 'b'], ['a', 'c'], ['b', 'c']]

Finding neighbors in a list

I have a list:
l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']
I need to extract all the neighbors of '>>' and split them into groups where they have elements in between that are neither '>>' or neigbors of '>>'.
For the example list the expected outcome would be:
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
I have tried quite a few things, but all the simple ones have failed one way or another. At the moment the only code that seems to work is this:
def func(L,N):
outer=[]
inner=[]
for i,e in enumerate(L):
if e!=N:
try:
if L[i-1]==N or L[i+1]==N:
inner.append(e)
elif len(inner)>0:
outer.append(inner)
inner=[]
except IndexError:
pass
if len(inner):
outer.append(inner)
return outer
func(l,'>>')
Out[196]:
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
Although it seems to work, i am wondering if there is a better,cleaner way to do it?
I would argue that the most pythonic and easy to read solution would be something like this:
import itertools
def neighbours(items, fill=None):
"""Yeild the elements with their neighbours as (before, element, after).
neighbours([1, 2, 3]) --> (None, 1, 2), (1, 2, 3), (2, 3, None)
"""
before = itertools.chain([fill], items)
after = itertools.chain(items, [fill]) #You could use itertools.zip_longest() later instead.
next(after)
return zip(before, items, after)
def split_not_neighbour(seq, mark):
"""Split the sequence on each item where the item is not the mark, or next
to the mark.
split_not_neighbour([1, 0, 2, 3, 4, 5, 0], 0) --> (1, 2), (5)
"""
output = []
for items in neighbours(seq):
if mark in items:
_, item, _ = items
if item != mark:
output.append(item)
else:
if output:
yield output
output = []
if output:
yield output
Which we can use like so:
>>> l = ['a', '>>', 'b', '>>', 'd', 'e', 'f', 'g', '>>', 'i', '>>', '>>',
... 'j', 'k', 'l', '>>', '>>']
>>> print(list(split_not_neighbour(l, ">>")))
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
Note the neat avoidance of any direct indexing.
Edit: A more elegant version.
def split_not_neighbour(seq, mark):
"""Split the sequence on each item where the item is not the mark, or next
to the mark.
split_not_neighbour([1, 0, 2, 3, 4, 5, 0], 0) --> (1, 2), (5)
"""
neighboured = neighbours(seq)
for _, items in itertools.groupby(neighboured, key=lambda x: mark not in x):
yield [item for _, item, _ in items if item != mark]
Here is one alternative:
import itertools
def func(L, N):
def key(i_e):
i, e = i_e
return e == N or (i > 0 and L[i-1] == N) or (i < len(L) and L[i+1] == N)
outer = []
for k, g in itertools.groupby(enumerate(L), key):
if k:
outer.append([e for i, e in g if e != N])
return outer
Or an equivalent version with a nested list comprehension:
def func(L, N):
def key(i_e):
i, e = i_e
return e == N or (i > 0 and L[i-1] == N) or (i < len(L) and L[i+1] == N)
return [[e for i, e in g if e != N]
for k, g in itertools.groupby(enumerate(L), key) if k]
You can simplify it like this
l = ['']+l+['']
stack = []
connected = last_connected = False
for i, item in enumerate(l):
if item in ['','>>']: continue
connected = l[i-1] == '>>' or l[i+1] == '>>'
if connected:
if not last_connected:
stack.append([])
stack[-1].append(item)
last_connected = connected
my naive attempt
things = (''.join(l)).split('>>')
output = []
inner = []
for i in things:
if not i:
continue
i_len = len(i)
if i_len == 1:
inner.append(i)
elif i_len > 1:
inner.append(i[0])
output.append(inner)
inner = [i[-1]]
output.append(inner)
print output # [['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
Something like this:
l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']
l= filter(None,"".join(l).split(">>"))
lis=[]
for i,x in enumerate(l):
if len(x)==1:
if len(lis)!=0:
lis[-1].append(x[0])
else:
lis.append([])
lis[-1].append(x[0])
else:
if len(lis)!=0:
lis[-1].append(x[0])
lis.append([])
lis[-1].append(x[-1])
else:
lis.append([])
lis[-1].append(x[0])
lis.append([])
lis[-1].append(x[-1])
print lis
output:
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
or:
l=['a','>>','b','>>','d','e','f','g','>>','i','>>','>>','j','k','l','>>','>>']
l= filter(None,"".join(l).split(">>"))
lis=[[] for _ in range(len([1 for x in l if len(x)>1])+1)]
for i,x in enumerate(l):
if len(x)==1:
for y in reversed(lis):
if len(y)!=0:
y.append(x)
break
else:
lis[0].append(x)
else:
if not all(len(x)==0 for x in lis):
for y in reversed(lis):
if len(y)!=0:
y.append(x[0])
break
for y in lis:
if len(y)==0:
y.append(x[-1])
break
else:
lis[0].append(x[0])
lis[1].append(x[-1])
print lis
output:
[['a', 'b', 'd'], ['g', 'i', 'j'], ['l']]
Another medthod using superimposition of original list
import copy
lis_dup = copy.deepcopy(lis)
lis_dup.insert(0,'')
prev_in = 0
tmp=[]
res = []
for (x,y) in zip(lis,lis_dup):
if '>>' in (x,y):
if y!='>>' :
if y not in tmp:
tmp.append(y)
elif x!='>>':
if x not in tmp:
print 'x is ' ,x
tmp.append(x)
else:
if prev_in ==1:
res.append(tmp)
prev_in =0
tmp = []
prev_in = 1
else:
if prev_in == 1:
res.append(tmp)
prev_in =0
tmp = []
res.append(tmp)
print res

Categories