How does this recursive permutation generator work? [duplicate] - python

def permute2(seq):
if not seq: # Shuffle any sequence: generator
yield seq # Empty sequence
else:
for i in range(len(seq)):
rest = seq[:i] + seq[i+1:] # Delete current node
for x in permute2(rest): # Permute the others
# In some cases x = empty string
yield seq[i:i+1] + x # Add node at front
for x in permute2('abc'):
print('result =',x)
When yielding the first result ('abc') the value of i == 0 and seq == 'abc'. The control flow then takes it to the top of the outer for loop where i == 1, which makes sense; however, seq == 'bc', which completely baffles me.

I'll try to explain it from an inductive standpoint.
Let the length of the sequence be n.
Our base case is n = 0, when we simply return the empty sequence (it is the only permutation).
If n > 0, then:
To define a permutation, we must first begin by choosing the first element. To define all permutations, obviously we must consider all elements of the sequence as possible first elements.
Once we have chosen the first element, we now need to choose the rest of the elements. But this is just the same as finding all the permutations of sequence without the element we chose. This new sequence is of length n-1, and so by induction we can solve it.
Slightly altering the original code to mimic my explanation and hopefully make it clearer:
def permute2(seq):
length = len(seq)
if (len == 0):
# This is our base case, the empty sequence, with only one possible permutation.
yield seq
return
for i in range(length):
# We take the ith element as the first element of our permutation.
first_element_as_single_element_collection = seq[i:i+1]
# Note other_elements has len(other_elements) == len(seq) - 1
other_elements = seq[:i] + seq[i+1:]
for permutation_of_smaller_collection in permute2(other_elements):
yield first_element_as_single_element_collection + permutation_of_smaller_collection
for x in permute2('abc'):
print('result =',x)
Hopefully it is clear that the original code does exactly the same thing as the above code.

Related

Leetcode jumpgame recursive approach

Given the following problem:
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the array represents your maximum jump length at that position.
Return true if you can reach the last index, or false otherwise.
Example 1:
Input: nums = [2,3,1,1,4]
Output: True
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
Example 2:
Input: nums = [3,2,1,0,4]
Output: False
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible to reach the last index.
I am trying to come up with a recursive solution. This is what I have so far. I am not looking for the optimal solution. I am just trying to solve using recursion for now. If n[i] is 0 I want the loop to go back to the previous loop and continue recursing, but I can't figure out how to do it.
def jumpGame(self, n: []) -> bool:
if len(n) < 2:
return True
for i in range(len(n)):
for j in range(1, n[i]+1):
next = i + j
return self.jumpGame(n[next:])
return False
If you want to do recursively and you said no need to be optimal ( so not memoized ), you could go with the below method. You don't need nested loops.
Also no need to explore all paths, you could optimize by looking at the step that you are going by checking i + (jump) < n
def jumpGame(a, i):
if i > len(a) - 1:
return False
if i == len(a) - 1:
return True
reached = False
for j in range(1, a[i] + 1):
if i + j < len(a):
reached = jumpGame(a, i + j)
if reached:
return True
return reached
print(jumpGame([2, 3, 1, 1, 4], 0))
print(jumpGame([3,2,1,0,4], 0))
True
False
When considering recursive solutions, the first thing you should consider is the 'base case', followed by the 'recursive case'. The base case is just 'what is the smallest form of this problem for which I can determine an answer', and the recursive is 'can I get from some form n of this problem to some form n - 1'.
That's a bit pedantic, but lets apply it to your situation. What is the base case? That case is if you have a list of length 1. If you have a list of length 0, there is no last index and you can return false. That would simply be:
if len(ls) == 0:
return False
if len(ls) == 1:
return True
Since we don't care what is in the last index, only at arriving at the last index, we know these if statements handle our base case.
Now for the recursive step. Assuming you have a list of length n, we must consider how to reduce the size of the problem. This is by making a 'jump', and we know that we can make a jump equal to a length up to the value of the current index. Then we just need to test each of these lengths. If any of them return True, we succeed.
any(jump_game(n[jump:] for jump in range(1, n[0] + 1)))
There are two mechanisms we are using here to make this easy. any takes in a sequence and quits as soon as one value is True, returning True. If none of them are true, it will return False. The second is a list slice, n[jump:] which takes a slice of a list from the index jump to the end. This might result in an empty list.
Putting this together we get:
def jump_game(n: list) -> bool:
# Base cases
if len(n) == 0:
return False
if len(n) == 1:
return True
# Recursive case
return any(jump_game(n[jump:]) for jump in range(1, n[0] + 1))
The results:
>>> jump_game([2,3,1,1,4])
True
>>> jump_game([3,2,1,0,1])
False
>>> jump_game([])
False
>>> jump_game([1])
True
I'm trying to lay out the rigorous approach here, because I think it helps to clarify where recursion goes wrong. In your recursive case you do need to iterate through your options - but that is only one loop, not the two you have. In your solution, in each recursion, you're iterating (for i in range(len(n))) through the entire list. So, you're really hiding an iterative solution inside a recursive one. Further, your base case is wrong, because a list of length 0 is considered a valid solution - but in fact, only a list of length 1 should return a True result.
What you should focus on for recursion is, again, solving the smallest possible form(s) of the problem. Here, it is if the list is one or zero length long. Then, you need to step each other possible size of the problem (length of the list) to a base case. We know we can do that by examining the first element, and choosing to jump anywhere up to that value. This gives us our options. We try each in turn until a solution is found - or until the space is exhausted and we can confidently say there is no solution.

How to code this "strange" sort in Python

I would like to write a function which performs efficiently this "strange" sort (I am sorry for this pseudocode, it seems to me to be the clearest way to introduce the problem):
l=[[A,B,C,...]]
while some list in l is not sorted (increasingly) do
find a non-sorted list (say A) in l
find the first two non-sorted elements of A (i.e. A=[...,b,a,...] with b>a)
l=[[...,a,b,...],[...,b+a,...],B,C,...]
Two important things should be mentioned:
The sorting is dependent on the choice of the first two
non-sorted elements: if A=[...,b,a,r,...], r<a<b and we choose to
sort wrt to (a,r) then the final result won't be the same. This is
why we fix the two first non-sorted elements of A.
Sorting this way always comes to an end.
An example:
In: Sort([[4,5,3,10]])
Out: [[3,4,5,10],[5,7,10],[10,12],[22],[4,8,10]]
since
(a,b)=(5,3): [4,5,3,10]->[[4,3,5,10],[4,8,10]]
(a,b)=(4,3): [[4,3,5,10],[4,8,10]]->[[3,4,5,10],[7,5,10],[4,8,10]]
(a,b)=(7,5): [[3,4,5,10],[7,5,10],[4,8,10]]->[[3,4,5,10],[5,7,10],[12,10],[4,8,10]]
(a,b)=(12,10): [[3,4,5,10],[5,7,10],[12,10],[4,8,10]]->[[3,4,5,10],[5,7,10],[10,12],[22],[4,8,10]]
Thank you for your help!
EDIT
Why am I considering this problem:
I am trying to do some computations with the Universal Enveloping Algebra of a Lie algebra. This is a mathematical object generated by products of some generators x_1,...x_n. We have a nice description of a generating set (it amounts to the ordered lists in the question), but when exchanging two generators, we need to take into account the commutator of these two elements (this is the sum of the elements in the question). I haven't given a solution to this question because it would be close to the worst one you can think of. I would like to know how you would implement this in a good way, so that it is pythonic and fast. I am not asking for a complete solution, only some clues. I am willing to solve it by myself .
Here's a simple implementation that could use some improvement:
def strange_sort(lists_to_sort):
# reverse so pop and append can be used
lists_to_sort = lists_to_sort[::-1]
sorted_list_of_lists = []
while lists_to_sort:
l = lists_to_sort.pop()
i = 0
# l[:i] is sorted
while i < len(l) - 1:
if l[i] > l[i + 1]:
# add list with element sum to stack
lists_to_sort.append(l[:i] + [l[i] + l[i + 1]] + l[i + 2:])
# reverse elements
l[i], l[i + 1] = l[i + 1], l[i]
# go back if necessary
if i > 0 and l[i - 1] > l [i]:
i -= 1
continue
# move on to next index
i += 1
# done sorting list
sorted_list_of_lists.append(l)
return sorted_list_of_lists
print(strange_sort([[4,5,3,10]]))
This keeps track of which lists are left to sort by using a stack. The time complexity is pretty good, but I don't think it's ideal
Firstly you would have to implement a while loop which would check if all of the numbers inside of the lists are sorted. I will be using all which checks if all the objects inside a sequence are True.
def a_sorting_function_of_some_sort(list_to_sort):
while not all([all([number <= numbers_list[numbers_list.index(number) + 1] for number in numbers_list
if not number == numbers_list[-1]])
for numbers_list in list_to_sort]):
for numbers_list in list_to_sort:
# There's nothing to do if the list contains just one number
if len(numbers_list) > 1:
for number in numbers_list:
number_index = numbers_list.index(number)
try:
next_number_index = number_index + 1
next_number = numbers_list[next_number_index]
# If IndexError is raised here, it means we don't have any other numbers to check against,
# so we break this numbers iteration to go to the next list iteration
except IndexError:
break
if not number < next_number:
numbers_list_index = list_to_sort.index(numbers_list)
list_to_sort.insert(numbers_list_index + 1, [*numbers_list[:number_index], number + next_number,
*numbers_list[next_number_index + 1:]])
numbers_list[number_index] = next_number
numbers_list[next_number_index] = number
# We also need to break after parsing unsorted numbers
break
return list_to_sort

Find the Duplicate Number

Given an array nums containing n + 1 integers where each integer is between 1 and n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.
My solution:
def findDuplicate(nums):
slow = fast = finder = 0
while fast is not None:
slow = nums[slow]
fast = nums[nums[fast]]
if fast is slow:
return slow
return False
nums = [1,2,2,3,4]
print findDuplicate(nums)
My above solution works and gives me o/p 2 but it doesn't work for every input for example it doesn't work for [11,15,17,17,14] or [3,1,2,6,2,3] and gives me error IndexError: list index out of range. I am not able to find patterns and am not able to track down the exact problem. Also tried to change my while condition:
while fast is not None and nums[nums[fast]] is not None:
your help will be greatly appreciated! Thank you.
Since the numbers are between 1 and n and you have been told there is only one duplicate, you can use difference between the sum of the numbers in the array and the sum of numbers from 1 to n to get the duplicate.
def findDuplicate(l):
n = len(l) - 1 # Get n as length of list - 1
return sum(l) - (n * (n + 1) / 2) # n*(n+1)/2 is the sum of integers from 1 to n
So the duplicate is the sum of the list - n*(n+1)/2
Of course, this doesn't generalize to finding duplicates for any list. For that case, you need to use #Jalepeno112 's answer.
The fact that the first one works is a fluke. Let's look at what it does on the first pass.
nums = [1,2,2,3,4]
# slow starts as index 0. So now, you've reassigned slow to be nums[0] which is 1.
# so slow equals 1
slow = nums[slow]
# now you are saying that fast equals nums[nums[0]].
# nums[0] is 1. nums[1] is 2
# so fast = 2
fast = nums[nums[fast]]
On the next pass, slow will be nums[1] which is 2. fast will be nums[nums[2]] which is nums[2] which is 2. At this point slow and fast are equal.
In your second example, you are getting an IndexError because of fast = nums[nums[fast]] If the value at nums[fast] is not a valid index, then this code will fail. Specifically in the second example, nums[0] is 11. nums doesn't have an element at index 11, so you get an error.
What you really want to be doing is performing a nested for loop on the array:
# range(0,len(nums)-1) will give a list of numbers from [0, to the length of nums-1)
# range(1, len(nums)) does the same,
# except it will start at 1 more than i is currently at (the next element in the array).
# So it's range is recomputed on each outer loop to be [i+1, length of nums)
for i in range(0,len(nums)-1):
for j in range(i+1,len(nums)):
# if we find a matching element, return it
if nums[i] == nums[j]:
return nums[i]
# if we don't find anything return False
return False
There are likely other more Pythonic ways to achieve this, but that wasn't your original question.
first you must ensure all numbers in list satisfy your constrains.
to find duplicated numbers in a list Use Counter in collections it will return each number and number of occurrence example :
>>> from collections import Counter
>>> l=Counter([11,15,17,17,14])
>>> l
Counter({17: 2, 11: 1, 14: 1, 15: 1})
to get the most common one use :
>>> l.most_common(n=1)
[(17, 2)]
where n is the number most common numbers you want to get
def duplicates(num_list):
if type(num_list) is not list:
print('No list provided')
return
if len(num_list) is 0 or len(num_list) is 1:
print('No duplicates')
return
for index,numA in enumerate(num_list):
num_len = len(num_list)
for indexB in range(index+1, num_len):
if numA == num_list[indexB]:
print('Duplicate Number:'+str(numA))
return
duplicates([11,15,17,17,14])
duplicates([3,1,2,6,2,3])
duplicates([])
duplicates([5])
l=[]
n= int(input("the number of digit is :"))
l=[0 for k in range(n)]
for j in range(0,n):
l[j]=int(input("the component is"))
print(l)
b=0; c=0
for i in range(n):
if l[i]== l[n-1-i]:
b=1;c=i
if b==1:
print("duplicate found! it is",l[c])
elif b==0:
print("no duplicate")
The answer is unfinished. It tries to convert the array to a linked list. So far it found where the slow pointer and fast pointer met, but this is the halfway solution. To get the solution, we need to initialize another pointer from the beginning of the linked list and walk towards each other. When they meet, that point is the where cycle is detected, in our question it is where the single point is:
class Solution:
def findDuplicate(self, nums: List[int]) -> int:
slow,fast=0,0
while True:
slow=nums[slow]
fast=nums[nums[fast]]
if slow==fast:
break
slow2=0
while True:
slow2=nums[slow2]
slow=nums[slow]
if slow==slow2:
return slow2

In this short recursive function `list_sum(aList)`, the finish condition is `if not aList: return 0`. I see no logic in why this condition works

I am learning the recursive functions. I completed an exercise, but in a different way than proposed.
"Write a recursive function which takes a list argument and returns the sum of its integers."
L = [0, 1, 2, 3, 4] # The sum of elements will be 10
My solution is:
def list_sum(aList):
count = len(aList)
if count == 0:
return 0
count -= 1
return aList[0] + list_sum(aList[1:])
The proposed solution is:
def proposed_sum(aList):
if not aList:
return 0
return aList[0] + proposed_sum(aList[1:])
My solution is very clear in how it works.
The proposed solution is shorter, but it is not clear for me why does the function work. How does if not aList even happen? I mean, how would the rest of the code fulfill a not aList, if not aList means it checks for True/False, but how is it True/False here?
I understand that return 0 causes the recursion to stop.
As a side note, executing without if not aList throws IndexError: list index out of range.
Also, timeit-1million says my function is slower. It takes 3.32 seconds while the proposed takes 2.26. Which means I gotta understand the proposed solution.
On the call of the function, aList will have no elements. Or in other words, the only element it has is null. A list is like a string or array. When you create a variable you reserve some space in the memory for it. Lists and such have a null on the very last position which marks the end so nothing can be stored after that point. You keep cutting the first element in the list, so the only thing left is the null. When you reach it you know you're done.
If you don't use that condition the function will try to take a number that doesn't exist, so it throws that error.
You are counting the items in the list, and the proposed one check if it's empty with if not aList this is equals to len(aList) == 0, so both of you use the same logic.
But, you're doing count -= 1, this has no sense since when you use recursion, you pass the list quiting one element, so here you lose some time.
According to PEP 8, this is the proper way:
• For sequences, (strings, lists, tuples), use the fact that empty
sequences are false.
Yes: if not seq:
if seq:
No: if len(seq)
if not len(seq)
Here is my amateur thougts about why:
This implicit check will be faster than calling len, since len is a function to get the length of a collection, it works by calling an object's __len__ method. This will find up there is no item to check __len__.
So both will find up there is no item there, but one does it directly.
not aList
return True if there is no elements in aList. That if statement in the solution covers edge case and checks if input parameter is not empty list.
For understand this function, let's run it step by step :
step 0 :
L=[0,1,2,3,4]
proposed_sum([0,1,2,3,4])
L != []
return l[0] + proposed_sum([1,2,3,4])
step 1 calcul proposed_sum([1,2,3,4]):
proposed_sum([1,2,3,4])
L != []
return l[0] + sum([2,3,4])
step 2 calcul proposed_sum([2,3,4]):
proposed_sum([2,3,4])
L != []
return l[0] + sum([3,4])
step 3 calcul proposed_sum([3,4]):
proposed_sum([3,4])
L != []
return l[0] + sum([4])
step 4 calcul proposed_sum([4]):
proposed_sum([4])
L != []
return l[0] + sum([])
step 5 calcul proposed_sum([]):
proposed_sum([])
L == []
return 0
step 6 replace:
proposed_sum([0,1,2,3,4])
By
proposed_sum([]) + proposed_sum([4]) + proposed_sum([3,4]) + proposed_sum([2,3,4]) + proposed_sum([1,2,3,4])+ proposed_sum([0,1,2,3,4])
=
(0) + 4 + 3 + 2 + 1 + 0
Python considers as False multiple values:
False (of course)
0
None
empty collections (dictionaries, lists, tuples)
empty strings ('', "", '''''', """""", r'', u"", etc...)
any other object whose __nonzero__ method returns False
in your case, the list is evaluated as a boolean. If it is empty, it is considered as False, else it is considered as True. This is just a shorter way to write if len(aList) == 0:
in addition, concerning your new question in the comments, consider the last line of your function:
return aList[0] + proposed_sum(aList[1:])
This line call a new "instance" of the function but with a subset of the original list (the original list minus the first element). At each recursion, the list passed in argument looses an element and after a certain amount of recursions, the passed list is empty.

I can't understand the "yield" with recursive function in Python

I am trying to understand the "yield".
First, the code that I can't understand is below.
def permutations(seq):
if len(seq) <= 1: yield seq
else:
for perm in permutations(seq[1:]):
for i in range(len(perm)+1):
yield perm[i:] + seq[0:1] + perm[:i]
print list(permutations(['police', 'buffalo', 'fish']))
The result is as below:
[['fish', 'buffalo', 'police'], ['buffalo', 'police', 'fish'], ['police', 'fish', 'buffalo'], ['buffalo', 'fish', 'police'], ['fish', 'police', 'buffalo'], ['police', 'buffalo', 'fish']]
My undertanding level about "yield" is just it is used for generater. And I can understand the code below.
def reverse(data):
for index in range(len(data) -1, -1, -1):
yield data[index]
for char in reverse('golf'):
print(char)
f
l
o
g
My level is just understanding above.. but with recursion, I can't understand... please explain. Thanks
Yes, yield is for generators. That implies that when called, they return iterators. Generators can be recursive: they will call themselves, get an iterator and the iterate over it, on each Iteration they can yield up as many or few of their own elements as they like.
In your example permutations is a generator which always returns an iterator over lists.
if len(seq) <= 1: yield seq
Is simple enough: in the trivial case, just generate one list, seq itself.
for perm in permutations(seq[1:]):
...
Means "now iterate over different sequence-of-lists", in this case that is the sequence of all permutations of the elements after the first. In each iteration we have a nested loop that inserts the first element into each position of the permutation and yields the result.
I hope that helps. It's a little hard for me because I don't know exactly what you are confused about.
Update: the OP wants to know why the first result is in reverse order. Consider the line:
yield perm[i:] + seq[0:1] + perm[:i]
For the first result (i=0) this is equivalent to yield perm + seq[0:1] -- the first element is sent to the end of the yielded list. By induction, this result is the reverse of seq. If you want the first result to be ['police', 'buffalo', 'fish'] then you can do:
yield perm[:i] + seq[0:1] + perm[i:]
The code you've given works according to this algorithm:
If the sequence has one item or is empty, the only permutation is the sequence itself
Separate the sequence into its first element, and the list of all its remaining elements
Consider all the permutations of the remainder of the sequence, according to this same algorithm. For each permutation:
For each position in the permutation:
Split the permutation at that position
Insert the first element of the original sequence in that split. This is a new permutation of the original sequence.
Done.
The original code is slightly strange in how it achieves point 3.1.2. We can make it this a little bit clearer by using more variables to show the relationship to the algorithm more clearly - this assumes you're using Python 3:
def permutations(seq):
if len(seq) <= 1:
yield seq
else:
first, *rest = seq
for perm in permutations(rest):
for i in range(len(perm)+1):
before = perm[:i]
after = perm[i:]
yield after + [first] + before
As you can see, the final line switches the start and end of the permutation for no real reason. It could just as easily do before + [first] + after, but the author decided not to. This doesn't affect how the algorithm works - it will find all the orderings, including the mirrored ones - but it means the order it produces them in might be a little strange.
You can use a similar recursive generator to implement reverse as well. In this case, the algorithm is:
If the sequence only has one item or is empty, it is its own reverse
Split the sequence into its first element and the list of all remaining elements
Use this algorithm to reverse the remaining elements
Yield each item of the result of the last step
Yield the first element of the original sequence
In Python, it looks like this:
def reverse(seq):
if len(seq) <= 1:
return seq
first, *rest = seq
for item in reverse(rest):
yield item
# Or you could use:
# yield from reverse(rest)
# Instead of the above loop
yield first

Categories