Heap's Algorithm - Non Recursive Method in Python to generate permutations - python

I am a newbie to programming. I am working on Heap's Algorithm, specifically non-recursive method. There is not so much explanation available on internet, to how the algorithm works. I found this piece of work from Bernardo Sulzbach but he doesn't explain how his algorithm works. I am stuck on it from days, tried everything couldn't figure it out. I have added comments after each line to make myself understand -- what's happening here? but I still couldn't make it work. Am I doing something wrong? Please help.
# Heap's Algorithm (Non Recursive)
# function to swap values in python
def swap(elements, i, j):
elements[i], elements[j] = elements[j], elements[i]
# function to generate permutation
def generate_permutations(elements, n):
# Passing two parameters of elements and n to function "generate_permutations".
c = [0] * n
# c is a new list and its value set to an array literal with value 0, n times.
# Example - [a] * 3 ==> ['a', 'a', 'a', 'a']
yield elements
# "yield" statement is used to define generators, while "return" statement causes a function to exit.
# "yield" replacing the return of a function to provide a result to its caller without destroying
# local variables. Unlike a function, where on each call it starts with new sets of variables, a generator
# will resume the execution where it left off.
i = 0
# i is a new variable and its value is set to 0. It can also be used to count the number of loop runs.
while i < n:
# while loop ==> while i is less than n, do following:
if c[i] < i:
# if statement ==> while i is less than n and if any element from 'c' list is less than i,
# then do following:
if i % 2 == 0:
# if statement ==> while i is less than n, and if any element in 'c' list is less than i, and
# i is an even number, then do following:
swap(elements, 0, i)
# calling swap function and passing following arguments: elements, '0' and 'i'
else:
# else, if all three conditions above are not true then do following:
swap(elements, c[i], i)
# calling swap funtions and passing following arguments: elements, an item from 'c' list and 'i'
yield elements
# ??? yield elements
c[i] += 1
# after that, increment c[i] by 1.
i = 0
# set the value of i to 0
else:
# else, if c[i] < i is not true the do the following.
c[i] = 0
# set the value of c[i] to 0
i += 1
# and increment i by 1
def permutations(elements):
return generate_permutations(elements, len(elements))
# Driver Code
# c = ?
# n = ?
# i = ?
print(permutations(['abc']))

Just cleaning up your code (too many comments):
# Heap's Algorithm (Non Recursive)
# https://en.wikipedia.org/wiki/Heap%27s_algorithm
def swap(seq, i, j):
seq[i], seq[j] = seq[j], seq[i]
def generate_permutations(seq, seqLen, resLen):
c = [0] * seqLen
yield seq[:resLen]
i = 0
while i < seqLen:
if c[i] < i:
if i % 2 == 0:
swap(seq, 0, i)
else:
swap(seq, c[i], i)
yield seq[:resLen]
c[i] += 1
i = 0
else:
c[i] = 0
i += 1
def permutations(seq, resLen=None):
if not resLen: resLen = len(seq)
return generate_permutations(seq, len(seq), resLen)
for p in permutations([1,2,3]): print(p)
for p in permutations([1,2,3],2): print(p)

Related

Index value not in list

I need to write code that returns 2 index numbers of an array. The function takes 2 arguments, one is an array and the other is an integer.
I need to check if two of the values inside the array adds up to the integer and using the numbers inside the array only once.
Here is my code:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return list([i,a[cnt]])
else:
cnt += 1
print(func([3,7,2,10,20],27))
My output for func([3, 7, 2, 10, 20], 27) is [7, 20].
This code shows that the loop can find the numbers which add up to the integer.
But when I do this:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return a.index(i,a[cnt])
else:
cnt += 1
print(func([3,7,2,10,20],27))
I get the Value error: 7 not in list, which clearly is.
I've had this issue working with other exercises as well.
Am I doing something wrong or the index function isn't suppose to be used like that.
What would be an efficient way to return the index numbers without having to write another loop for it?
The second parameter to index that you're passing is actually the starting index on the list in which the search for the element will start (as you can see here). If you remove it, you'll see that it returns the first value you want (but not the second). It is relevant to note that the index method will only ever return the first occurrence of the value.
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return a.index(i)
else:
cnt += 1
print(func([3,7,2,10,20],27))
>>> 1
This happens because the value you're passing as the starting index (a[cnt]) is greater than the actual index of the number (1).
By removing it, you search through all the list, and find the correct value (remembering that Python uses zero-indexed iterators).
But since you want to return a list with both values, you need to explicitly state you want the index for each, such as:
def func(a,b):
for i in a:
cnt = 0
while cnt < len(a):
if i + a[cnt] == b and i != a[cnt]:
return [a.index(i), a.index(a[cnt])]
else:
cnt += 1
print(func([3,7,2,10,20],27))
>>> [1, 4]
You could achieve the same results using two for loops, however, in a cleaner way (also gave meaningful names to variables):
def find_indices_for_sum(array, sum_value):
for i in array:
for j in array:
if i + j == sum_value and i != j:
return [array.index(i), array.index(j)]
print(find_indices_for_sum([3,7,2,10,20],27))
>>> [1, 4]
If you want to be able to deal with equal numbers, you can change the comparison strategy altogether, using indices instead of values, since the former are unique in a list, but the latter are not. enumerate is a good option here, since it allows to iterate both through index and values at the same time in a clean way.
def find_indices_for_sum(array, sum_value):
for i, value_i in enumerate(array):
for j, value_j in enumerate(array):
if i != j and value_i + value_j == sum_value:
return [i, j]
print(find_indices_for_sum([3,3],6))
>>> [0, 1]

Number of inversions with merge sort: Why do I not need to initialize a global variable here?

I implemented merge sort in python and am trying to get the number of inversions of a given array. An inversion is defined as a pair of indices 0 ≤ 𝑖 < 𝑗 < 𝑛 such that 𝑎𝑖 > 𝑎𝑗. This problem is also described as 'how far away is an array from being sorted'.
Here is my code:
def merge(a,b):
c = [] # Output array
i, j = 0, 0 # Indices of the left and right split arrays
global num_inversions
# It looks like there is no need to initialize this? I don't understand this
while i < len(a) and j < len(b):
if a[i] <= b[j]:
c.append(a[i])
i += 1
else:
c.append(b[j])
j += 1
num_inversions = num_inversions + len(a) - i
if i == len(a):
c.extend(b[j:])
else:
c.extend(a[i:])
return c
def mergesort(a):
# Base case
if len(a) <= 1:
return a
mid = len(a) // 2
left_array = mergesort(a[:mid])
right_array = mergesort(a[mid:])
return merge(left_array, right_array)
if __name__ == '__main__':
#inp = input()
#n, *a = list(map(int, inp.split()))
a = [6, 5, 4, 3, 2, 1] # Sample input. Gives the correct answer 15
a_sorted = mergesort(a)
print(num_inversions)
I seem to be getting the correct result. What I don't understand is why the global variable num_inversions can be declared in the function, and why it doesn't need to be initialized.
Initializing it in the function is obviously wrong because the value will keep getting rewritten each time the function is called recursively.
Edit: Sorry, this question was incredibly stupid. I realized I did initialize it at some point while testing it, and forgot.

Count work for Merge_Sort Algorithm

All, I would like to count how close an array is being sorted by using Merge Sort Algorithm. I am able to use Merge Sort to arrange the array but I have trouble to keep counting how many inversion I need during the process.
For example, when input [9,4,8,3], I want to get the output [3,4,8,9] and 4 inversions. The definition of inversion is: if b in B , c in C and we have b>c then inversion is needed (the order of B,C matter). First, I will get two parts ([4,9],1) and ([3,8],1) which indicate one inversion individually. Then, when they merge again, there are another two inversions: choosing 3 instead of 4, choosing 8 instead of 9.
My main question might not relate to the algorithm itself. It is about how to keep one of my variable evolve within function loop of a function. (I have using Merge_Sort function within Merge_Sort function)
def Merge_Sort(a):
n = len(a)
if n==1:
if not 'total_rev' in vars():
total_rev = 0
else:
total_rev += rev_ind
return a , total_rev
else:
m = math.floor(n/2)
b , rev_ind_b = Merge_Sort(a[:m])
if not 'total_rev' in vars():
total_rev = 0
else:
total_rev += rev_ind_b
c , rev_ind_c = Merge_Sort(a[m:])
if not 'total_rev' in vars():
total_rev = 0
else:
total_rev += rev_ind_c
a_sort , rev_ind = Merge(b,c)
if not 'total_rev' in vars():
total_rev = 0
total_rev += rev_ind
else :
total_rev += rev_ind
return a_sort , total_rev
def Merge(b,c):
p = len(b)
q = len(c)
d = []
reverse_ind = 0
while len(b)!=0 or len(c)!=0 :
if (len(b)*len(c) != 0) :
b0 = b[0]
c0 = c[0]
if b0 <= c0 :
d.append(b0)
b.remove(b[0])
else :
reverse_ind += 1
d.append(c0)
c.remove(c[0])
else :
d.extend(b)
b=[]
d.extend(c)
c=[]
return d,reverse_ind
The Merge function can work well. The only question is I cannot keep the variable "total_inv" update as I wish. I try to define "total_inv" whenever it is not defined. Not sure if it is a good way because it made my code messy. I also try to use global variable but it cannot work well. Thank you!
It is simpler than that:
when at the deepest recursion level (n==1) just return 0 for the number of swaps. The logic is that you should return the number of swaps for the list as it is at that recursion level, without any consideration of what the larger list may be. So when n==1 your list has one value, which obviously does not need swapping.
In other cases, just add up the counts you get from the recursive calls. That way they will increase when bubbling back up the recursion tree.
Here is the adapted code for Merge_Sort:
def Merge_Sort(a):
n = len(a)
if n==1:
return a, 0 # at deepest recursion always return 0 for the number of swaps
else:
m = n//2 # use integer division; you don't need `math.floor`
b , rev_ind_b = Merge_Sort(a[:m])
c , rev_ind_c = Merge_Sort(a[m:])
a_sort , rev_ind = Merge(b,c)
return a_sort , rev_ind_b + rev_ind_c + rev_ind # add the numbers

Optimal Search Tree Using Python - Code Analysis

First of all, sorry about the naive question. But I couldn't find help elsewhere
I'm trying to create an Optimal Search Tree using Dynamic Programing in Python that receives two lists (a set of keys and a set of frequencies) and returns two answers:
1 - The smallest path cost.
2 - The generated tree for that smallest cost.
I basically need to create a tree organized by the most accessed items on top (most accessed item it's the root), and return the smallest path cost from that tree, by using the Dynamic Programming solution.
I've the following implemented code using Python:
def optimalSearchTree(keys, freq, n):
#Create an auxiliary 2D matrix to store results of subproblems
cost = [[0 for x in xrange(n)] for y in xrange(n)]
#For a single key, cost is equal to frequency of the key
#for i in xrange (0,n):
# cost[i][i] = freq[i]
# Now we need to consider chains of length 2, 3, ... .
# L is chain length.
for L in xrange (2,n):
for i in xrange(0,n-L+1):
j = i+L-1
cost[i][j] = sys.maxint
for r in xrange (i,j):
if (r > i):
c = cost[i][r-1] + sum(freq, i, j)
elif (r < j):
c = cost[r+1][j] + sum(freq, i, j)
elif (c < cost[i][j]):
cost[i][j] = c
return cost[0][n-1]
def sum(freq, i, j):
s = 0
k = i
for k in xrange (k,j):
s += freq[k]
return s
keys = [10,12,20]
freq = [34,8,50]
n=sys.getsizeof(keys)/sys.getsizeof(keys[0])
print(optimalSearchTree(keys, freq, n))
I'm trying to output the answer 1. The smallest cost for that tree should be 142 (the value stored on the Matrix Position [0][n-1], according to the Dynamic Programming solution). But unfortunately it's returning 0. I couldn't find any issues in that code. What's going wrong?
You have several very questionable statements in your code, definitely inspired by C/Java programming practices. For instance,
keys = [10,12,20]
freq = [34,8,50]
n=sys.getsizeof(keys)/sys.getsizeof(keys[0])
I think you think you calculate the number of items in the list. However, n is not 3:
sys.getsizeof(keys)/sys.getsizeof(keys[0])
3.142857142857143
What you need is this:
n = len(keys)
One more find: elif (r < j) is always True, because r is in the range between i (inclusive) and j (exclusive). The elif (c < cost[i][j]) condition is never checked. The matrix c is never updated in the loop - that's why you always end up with a 0.
Another suggestion: do not overwrite the built-in function sum(). Your namesake function calculates the sum of all items in a slice of a list:
sum(freq[i:j])
import sys
def optimalSearchTree(keys, freq):
#Create an auxiliary 2D matrix to store results of subproblems
n = len(keys)
cost = [[0 for x in range(n)] for y in range(n)]
storeRoot = [[0 for i in range(n)] for i in range(n)]
#For a single key, cost is equal to frequency of the key
for i in range (0,n):
cost[i][i] = freq[i]
# Now we need to consider chains of length 2, 3, ... .
# L is chain length.
for L in range (2,n+1):
for i in range(0,n-L+1):
j = i + L - 1
cost[i][j] = sys.maxsize
for r in range (i,j+1):
c = (cost[i][r-1] if r > i else 0)
c += (cost[r+1][j] if r < j else 0)
c += sum(freq[i:j+1])
if (c < cost[i][j]):
cost[i][j] = c
storeRoot[i][j] = r
return cost[0][n-1], storeRoot
if __name__ == "__main__" :
keys = [10,12,20]
freq = [34,8,50]
print(optimalSearchTree(keys, freq))

Python - Append Help - String & list

Another append question... This is my code:
def s(xs,n,m):
t = []
while n < m:
n += 2
t.append(xs[n])
return t
When I evaluate the following:
x = s('African', 0, 3)
Why does it return this?:
['r', 'c']
while n < m:
n += 2 # at this point n = 2 because you've passed 0
t.append(xs[n]) # you append r to t since xs[2] = r
but n < m still, so next iteration:
while n < m:
n += 2 # at this point n = 4
t.append(xs[n]) # you append c to t since xs[4] = c
now n > m, so the function returns ['r', 'c']. Everything is correct.
Ok, so line-by-line...
Your call looks like this:
x = s('African', 0, 3)
so what happens is:
Step 1. - initial assignement
def s(xs,n,m):
xs='African', n=0 and m=3 and then:
t = []
(so, empty list t is introduced).
Step 2. - loop
Then the following condition is evaluated:
while n < m:
to True, because 0 < 3.
And then n is increased:
n += 2
so it is now equal to 2.
Then the appropriate element is appended to the empty t list:
t.append(xs[n])
and this element is "r", because xs[2] == 'r'.
Then n < m condition is again evaluated to True (because 2 < 3), so the loop executes again:
n += 2
and n is now equal to 4.
Then appropriate char from xs string is appended to t list (which already has one element, r, as we mentioned above).
t.append(xs[n])
and this element is "c" (because xs[4] is exactly "c").
Then condition for while loop is again evaluated, but this time to False (because 4 < 3 is not true), so the loop stops executing...
(Step 3. - after the loop) ...and the program flow goes to the final statement of the function, which is:
return t
And t returns the list we filled with two elements - as a result, the function returns list ['r', 'c'].
Is it clear enough? Did it help?

Categories