Count work for Merge_Sort Algorithm - python

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

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]

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

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)

While loop running once?

New to coding and am trying to solve this coding problem to learn.
Prompt:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23. Find the sum of all the multiples of 3 or 5 below 1000.
three = []
five = []
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
return three
def fiveList():
n = 1
while (n*5<1000):
result = n*5
five.append(result)
n += 1
return five
threeList()
fiveList()
print(three,five)
This results in printing [3] [5] to the console.
Your return is part of the loop which means that at the end of iteration, instead of doing another iteration you just return from the function. Move it out of a loop, i.e.:
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
return three
Also this return makes little sense, because you are returning global variables. No point to return something that is already available (I suggest you read about variable scope), so it's safe to get rid of these returns completely:
def threeList():
n = 1
while (n*3<1000):
result = n*3
three.append(result)
n += 1
In fact, as both your functions differ very little, you should refactor your code and have just one function accepting the multiplier (as this is the only difference) and return populated list. This time we go with local variable to create the result list, so this time you need to return it otherwise result list will not be available outside the function:
def my_func(multiplier):
result = []
n = 1
while (n*multiplier < 1000):
result.append(n*multiplier)
n += 1
return result
and then replace
threeList()
fiveList()
with
three = my_func(3)
five = my_func(5)
In fact, you could merge this with print() as there's no other use for three and five, so your final code would then look like this:
def my_func(multiplier):
result = []
n = 1
while (n*multiplier < 1000):
result.append(n*multiplier)
n += 1
return result
print(my_func(3), my_func(5))
In addition to Marcin's fantastic answer, note that you can also do the math for which elements to use ahead of time and avoid the while loop entirely. range is your friend here.
multiples_of_five = range(5, 1001, step=5)
multiples_of_three = range(3, 1001, 3)
Since range's stop is exclusive, but we want all the multiples of three and five up to 1000 inclusive, we have to stop at 1001 instead. This simplifies the my_func that Marcin lays out above.
def list_multiples(n):
result = []
for i in range(n, 1001, n):
result.append(i)
return result
Though if we examine this more closely, you'll see we're basically just casting to list and returning. Let's do that directly.
def list_multiples(n):
return list(range(n, 1001, n))
From there we can find the multiples of five and multiples of three
fives = list_multiples(5)
threes = list_multiples(3)
Cast to set to remove duplicates (15 is a multiple of both 5 and 3, but shouldn't be summed twice)
all_nums = set(fives + threes)
And sum the result
result = sum(all_nums)
To solve your problem in Pythonic way, use sum() with a generator expression like:
Code:
sum(i for i in range(1000) if i % 5 == 0 or i % 3 == 0)
Test Code:
max_num = 1000
print(sum(i for i in range(max_num) if i % 5 == 0 or i % 3 == 0))
Results:
233168

Trying to write a code that will find the sum of the even numbers of the Fibonacci sequence?

I'm new to programming and I'm trying to write a program in Python that will find the sum of the even numbers of the numbers below 4,000,000 in the Fibonacci sequence. I'm not sure what I'm doing wrong but nothing will print. Thanks for any help.
def fib():
listx = []
for x in range(4000000):
if x == 0:
return 1
elif x == 1:
return 1
else:
listx.append(fib(x - 1) + fib(x - 2))
return listx
def evens(fib):
y = 0
for x in fib():
if x % 2 == 0:
y += x
else:
continue
print (y)
Here's an approach that uses a generator to keep memory usage to a minimum:
def fib_gen(up_to):
n, m = 0, 1
while n <= up_to:
yield n
n, m = m, n + m
total = 0
for f in fib_gen(4000000):
if f % 2 == 0:
total += f
Another option:
def fib_gen(up_to, filter):
n, m = 0, 1
while n <= up_to:
if filter(n):
yield n
n, m = m, n + m
sum(fib_gen(4000000, lambda f: f % 2 == 0)) # sum of evens
sum(fib_gen(4000000, lambda f: f % 2)) # sum of odds
First things first, there appears to be some contention between your requirements and the code you've delivered :-) The text of your question (presumably taken from an assignment, or Euler #2) requests the ...
sum of the even numbers of the numbers below 4,000,000 in the Fibonacci sequence.
Your code is summing the even numbers from the first four million Fibonacci numbers which is vastly different. The four millionth Fibonacci number has, according to Binet's formula, north of 800,000 digits in it (as opposed to the seven digits in the highest one below four million).
So, assuming the text to be more correct than the code, you don't actually need to construct a list and then evaluate every item in it, that's rather wasteful on memory.
The Fibonacci numbers can be generated on the fly and then simply accumulated if they're even. It's also far more useful to be able to use an arbitrary method to accumulate the numbers, something like the following:
def sumFibWithCond(limit, callback):
# Set up initial conditions.
grandparent, parent, child = 0, 0, 1
accum = 0
# Loop until number is at or beyond limit.
while child < limit:
# Add any suitable number to the accumulator.
accum = accum + callback(child)
# Set up next Fibonacci cycle.
grandparent, parent = parent, child
child = grandparent + child
# Return accumulator when done.
return accum
def accumulateEvens(num):
# Return even numbers as-is, zero for odd numbers.
if num % 2 == 0:
return num
return 0
sumEvensBelowFourMillion = sumFibWithCond(4000000, accumulateEvens)
Of special note is the initial conditions. The numbers are initialised to 0, 0, 1 since we want to ensure we check every Fibonacci number (in child) for the accumulating condition. This means the initial value of child should be one assuming, as per the question, that's the first number you want.
This doesn't make any difference in the current scenario since one is not even but, were you to change the accumulating condition to "odd numbers" (or any other condition that allowed for one), it would make a difference.
And, if you'd prefer to subscribe to the Fibonacci sequence starting with zero, the starting values should be 0, 1, 0 instead.
Maybe this will help you.
def sumOfEvenFibs():
# a,b,c in the Fibonacci sequence
a = 1
b = 1
result = 0
while b < 4000000:
if b % 2 == 0:
result += b
c = a + b
a = b
b = c
return result

a function that returns number of sums of a certain number.py

I need to write a function that returns the number of ways of reaching a certain number by adding numbers of a list. For example:
print(p([3,5,8,9,11,12,20], 20))
should return:5
The code I wrote is:
def pow(lis):
power = [[]]
for lst in lis:
for po in power:
power = power + [list(po)+[lst]]
return power
def p(lst, n):
counter1 = 0
counter2 = 0
power_list = pow(lst)
print(power_list)
for p in power_list:
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
counter1 == 0
else:
counter1 == 0
return counter2
pow() is a function that returns all of the subsets of the list and p should return the number of ways to reach the number n. I keep getting an output of zero and I don't understand why. I would love to hear your input for this.
Thanks in advance.
There are two typos in your code: counter1 == 0 is a boolean, it does not reset anything.
This version should work:
def p(lst, n):
counter2 = 0
power_list = pow(lst)
for p in power_list:
counter1 = 0 #reset the counter for every new subset
for j in p:
counter1 += j
if counter1 == n:
counter2 += 1
return counter2
As tobias_k and Faibbus mentioned, you have a typo: counter1 == 0 instead of counter1 = 0, in two places. The counter1 == 0 produces a boolean object of True or False, but since you don't assign the result of that expression the result gets thrown away. It doesn't raise a SyntaxError, since an expression that isn't assigned is legal Python.
As John Coleman and B. M. mention it's not efficient to create the full powerset and then test each subset to see if it has the correct sum. This approach is ok if the input sequence is small, but it's very slow for even moderately sized sequences, and if you actually create a list containing the subsets rather than using a generator and testing the subsets as they're yielded you'll soon run out of RAM.
B. M.'s first solution is quite efficient since it doesn't produce subsets that are larger than the target sum. (I'm not sure what B. M. is doing with that dict-based solution...).
But we can enhance that approach by sorting the list of sums. That way we can break out of the inner for loop as soon as we detect a sum that's too high. True, we need to sort the sums list on each iteration of the outer for loop, but fortunately Python's TimSort is very efficient, and it's optimized to handle sorting a list that contains sorted sub-sequences, so it's ideal for this application.
def subset_sums(seq, goal):
sums = [0]
for x in seq:
subgoal = goal - x
temp = []
for y in sums:
if y > subgoal:
break
temp.append(y + x)
sums.extend(temp)
sums.sort()
return sum(1 for y in sums if y == goal)
# test
lst = [3, 5, 8, 9, 11, 12, 20]
total = 20
print(subset_sums(lst, total))
lst = range(1, 41)
total = 70
print(subset_sums(lst, total))
output
5
28188
With lst = range(1, 41) and total = 70, this code is around 3 times faster than the B.M. lists version.
A one pass solution with one counter, which minimize additions.
def one_pass_sum(L,target):
sums = [0]
cnt = 0
for x in L:
for y in sums[:]:
z = x+y
if z <= target :
sums.append(z)
if z == target : cnt += 1
return cnt
This way if n=len(L), you make less than 2^n additions against n/2 * 2^n by calculating all the sums.
EDIT :
A more efficient solution, that just counts ways. The idea is to see that if there is k ways to make z-x, there is k more way to do z when x arise.
def enhanced_sum_with_lists(L,target):
cnt=[1]+[0]*target # 1 way to make 0
for x in L:
for z in range(target,x-1,-1): # [target, ..., x+1, x]
cnt[z] += cnt[z-x]
return cnt[target]
But order is important : z must be considered descendant here, to have the good counts (Thanks to PM 2Ring).
This can be very fast (n*target additions) for big lists.
For example :
>>> enhanced_sum_with_lists(range(1,100),2500)
875274644371694133420180815
is obtained in 61 ms. It will take the age of the universe to compute it by the first method.
from itertools import chain, combinations
def powerset_generator(i):
for subset in chain.from_iterable(combinations(i, r) for r in range(len(i)+1)):
yield set(subset)
def count_sum(s, cnt):
return sum(1 for i in powerset_generator(s) if sum(k for k in i) == cnt)
print(count_sum(set([3,5,8,9,11,12,20]), 20))

Categories