recursive solution for coin change problem - python-3.x

i need to write an algorithm that receives a number and a list of numbers and returns the number of possible combinations of numbers from the list that can create the sum number. for example: def coin(5,[1,2,5,6] should return the number 4 because there are 4 possible combinations from the list that can create together the number 5. below there is the code that i tried but it does not work. the output is 6 instead of 4 and i would love some help in understanding why.
def coin(n,lst):
if n<=1:
return 1
else:
total=0
for i in range(len(lst)):
change=lst[i]
if change>n:
continue
else:
result=coin(n-change,lst[i:])
if result>0:
total+=result
return total
print(coin(5,[1,2,5,6]))

The mistake is in the base case:
if n<=1:
return 1
This is only valid if 1 is one of the allowed coins. But in your recursive case, you slice the list at lst[i:], so not all coins will be allowed every time. Sometimes, 1 isn't one of the allowed coins, in which case there are zero ways to make a total of 1, not one way. To fix it, write something like:
if n <= 1:
if n == 0 or n in lst:
return 1
else:
return 0
This is correct because you can make change for n in the base case either if n is 0 (we can always make 0), or if n is 1 and 1 is one of the allowed coins.
That said, it would be simpler to let the recursive case handle 1, so the base case only needs to handle 0; this is also correct:
if n == 0:
return 1

int coinCount( int C[], int m, int n )
{
// if n is 0 return 1
if (n == 0)
return 1;
// If n is less than 0 then no solution exists
if (n < 0)
return 0;
// If there are no coins and n is greater than 0, then no solution exists
if (m <=0 && n > 0)
return 0;
return coinCount( C, m - 1, n ) + coinCount( C, m, n-C[m-1] );
}

Related

Different results from for-loop and while-loop for prime factorization in Python 3

I have written a function that returns the prime factors of a given integer. For the cases I tested, it seems to work alright. Here is the original function.
def prime_factors_2(num: int) -> set:
factors = []
while num % 2 == 0:
factors.append(2)
num //= 2
i = 3
while i <= int(sqrt(num)) + 1:
if num % i == 0:
factors.append(i)
num //= i
else:
i += 2
if num != 1:
factors.append(num)
return set(factors)
# num = 867844
# Output = {601, 2, 19}
While messing around with the code, I tried to implement the same but with a for loop instead of a while loop (as I prefer to use a for loop when counting is involved). This is the code for the second function.
def prime_factors_1(num: int) -> set:
factors = []
while num % 2 == 0:
factors.append(2)
num //= 2
for i in range(3, int(sqrt(num)) + 1, 2):
if num % i == 0:
factors.append(i)
num //= i
print(num)
if num != 1:
factors.append(num)
return set(factors)
# num = 867844
# Output = {2, 19, 11419}
For some reason, it no longer factors the 11419 into 601 and 19. Are both the loops not equivalent? Or am I making some mistake while translating the while loop to a for loop? I know that there is no practical difference between the two loops in this case, but I want to know this out of pure curiosity.
Problem is that in your "while loop", you are incrementing the value of "i" by 2 only when the "if" condition is not satisfied, but in the case of "for loop", i is getting incremented by 2 in each iteration.
So if you'll do something like:
while i <= int(sqrt(num)) + 1:
if num % i == 0:
factors.append(i)
num //= i
i += 2
Then the prime_factors_2 function would also result in the same answer as the prime_factors_1 function
There is a slight difference between the two functions.
If you look closely at prime_factors_2, in the while loop, the counter doesn't get incremented when the if condition is satisfied.
On the other hand, in prime_factors_1, in the for loop, the counter gets incremented in every iteration regardless of the if condition.
There is no way to control the counter from within a for loop, so the while loop implementation is the only correct approach.

find the first occurrence of a number greater than k in a sorted array

For the given sorted list,the program should return the index of the number in the list which is greater than the number which is given as input.
Now when i run code and check if it is working i am getting 2 outputs. One is the value and other output is None.
If say i gave a input of 3 for the below code.The expected output is index of 20 i.e., 1 instead i am getting 1 followed by None.
If i give any value that is greater than the one present in the list i am getting correct output i.e., "The entered number is greater than the numbers in the list"
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a,num_to_find):
j = i = 0
while j==0:
if a[len(a)-1] > num_to_find:
if num_to_find < a[i]:
j=1
print(i)
break
else:
i = i + 1
else:
ret_state = "The entered number is greater than the numbers in the list"
return ret_state
print(occur1(a,num_to_find))
This code is difficult to reason about due to extra variables, poor variable names (j is typically used as an index, not a bool flag), usage of break, nested conditionals and side effect. It's also inefficient because it needs to visit each element in the list in the worst case scenario and fails to take advantage of the sorted nature of the list to the fullest. However, it appears working.
Your first misunderstanding is likely that print(i) is printing the index of the next largest element rather than the element itself. In your example call of occur1([2, 20, 30], 3)), 1 is where 20 lives in the array.
Secondly, once the found element is printed, the function returns None after it breaks from the loop, and print dutifully prints None. Hopefully this explains your output--you can use return a[i] in place of break to fix your immediate problem and meet your expectations.
Having said that, Python has a builtin module for this: bisect. Here's an example:
from bisect import bisect_right
a = [1, 2, 5, 6, 8, 9, 15]
index_of_next_largest = bisect_right(a, 6)
print(a[index_of_next_largest]) # => 8
If the next number greater than k is out of bounds, you can try/except that or use a conditional to report the failure as you see fit. This function takes advantage of the fact that the list is sorted using a binary search algorithm, which cuts the search space in half on every step. The time complexity is O(log(n)), which is very fast.
If you do wish to stick with a linear algorithm similar to your solution, you can simplify your logic to:
def occur1(a, num_to_find):
for n in a:
if n > num_to_find:
return n
# test it...
a = [2, 5, 10]
for i in range(11):
print(i, " -> ", occur1(a, i))
Output:
0 -> 2
1 -> 2
2 -> 5
3 -> 5
4 -> 5
5 -> 10
6 -> 10
7 -> 10
8 -> 10
9 -> 10
10 -> None
Or, if you want the index of the next largest number:
def occur1(a, num_to_find):
for i, n in enumerate(a):
if n > num_to_find:
return i
But I want to stress that the binary search is, by every measure, far superior to the linear search. For a list of a billion elements, the binary search will make about 20 comparisons in the worst case where the linear version will make a billion comparisons. The only reason not to use it is if the list can't be guaranteed to be pre-sorted, which isn't the case here.
To make this more concrete, you can play with this program (but use the builtin module in practice):
import random
def bisect_right(a, target, lo=0, hi=None, cmps=0):
if hi is None:
hi = len(a)
mid = (hi - lo) // 2 + lo
cmps += 1
if lo <= hi and mid < len(a):
if a[mid] < target:
return bisect_right(a, target, mid + 1, hi, cmps)
elif a[mid] > target:
return bisect_right(a, target, lo, mid - 1, cmps)
else:
return cmps, mid + 1
return cmps, mid + 1
def linear_search(a, target, cmps=0):
for i, n in enumerate(a):
cmps += 1
if n > target:
return cmps, i
return cmps, i
if __name__ == "__main__":
random.seed(42)
trials = 10**3
list_size = 10**4
binary_search_cmps = 0
linear_search_cmps = 0
for n in range(trials):
test_list = sorted([random.randint(0, list_size) for _ in range(list_size)])
test_target = random.randint(0, list_size)
res = bisect_right(test_list, test_target)[0]
binary_search_cmps += res
linear_search_cmps += linear_search(test_list, test_target)[0]
binary_search_avg = binary_search_cmps / trials
linear_search_avg = linear_search_cmps / trials
s = "%s search made %d comparisons across \n%d searches on random lists of %d elements\n(found the element in an average of %d comparisons\nper search)\n"
print(s % ("binary", binary_search_cmps, trials, list_size, binary_search_avg))
print(s % ("linear", linear_search_cmps, trials, list_size, linear_search_avg))
Output:
binary search made 12820 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 12 comparisons
per search)
linear search made 5013525 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 5013 comparisons
per search)
The more elements you add, the worse the situation looks for the linear search.
I would do something along the lines of:
num_to_find = int(input("Enter the number to be found"))
a=[2,20,30]
def occur1(a, num_to_find):
for i in a:
if not i <= num_to_find:
return a.index(i)
return "The entered number is greater than the numbers in the list"
print(occur1(a, num_to_find))
Which gives the output of 1 (when inputting 3).
The reason yours gives you 2 outputs, is because you have 2 print statements inside your code.

Number of Combinations to make change with coins

We have unlimited coins of different values - Calculate the unique combinations of how these coins can make up a specific amount. For example:
n = 4 (say, 4 cents)
coins_list = [1,2] - we have 1-cent coins, and 2-cent coins
The different combinations would be 112, 1111, and 22. (121 and 211 should be excluded since it's not unique - using one 2-cent coin and two 1-cent coin)
I have watched this video: https://www.youtube.com/watch?v=k4y5Pr0YVhg
countless number of times, and edited my codes countless number of times, but I cannot manage to get rid of the same combination of different orders.
def make_change(n, coinlist_index=None):
coin_list = [1, 2]
if coinlist_index == None:
coinlist_index = 0
#coin index position in coin_list; starts at index 0 and cycles through all the coins
if n == 0:
return 1
if n < 0:
return 0
ways = 0
# if I use for i in range(len(coin_list)), it returns an error message saying that index is out of range
for coinlist_index in range(len(coin_list)):
ways += make_change((n - coin_list[coinlist_index]), coinlist_index)
coinlist_index += 1
return ways
make_change(4)
Output: 5
My output was 5 (different ways to make change for 4 cents with 1 and 2-cent coins), instead of 3 (which is what I want).
I'm sure it has to do with the for loop toward the end, but when i change "for coinlist_index in range..." to a different iterator, i, I get an error that says index is out of range.
What is going on, and more importantly, how can I fix it?
EDIT: P.S. This is just a simple example that I'm working through to solve the actual assignment - Which is with 6 types of coins, in cents, (1, 5, 10, 25, 50, 100), and calculate how many ways to make change for 200 dollars. I have seen and tried the dynamic programming method out there, which worked, but we have to use recursion for assignment purposes.
Looks like I got it working. In each recursive pass you want to make sure that you aren't double counting possible ways to make the change. My thought to do this was to make sure that you never go backwards in the coin_list. So for the coin_list [1,2] if we ever use the 2 cent coin we never want the option to use the 1 cent coin afterwards. I made sure it follows this order by changing your for loop a bit:
for i in range(len(coin_list)-coinlist_index):
ways += make_change((n - coin_list[i+coinlist_index-1]), coinlist_index)
In the for loop I subtracted coinlist_index from the upper bound so we don't cycle over all coins once the index reaches 1, then added the index to where you pull from the coin_list, making sure once coinlist_index is 1 or more, we NEVER usecoin_list[0]. This got me to 3 in your sample case, hopefully it works for all cases. Full code:
def make_change(n, coinlist_index=None):
coin_list = [1, 2]
if coinlist_index == None:
coinlist_index = 0
#coin index position in coin_list; starts at index 0 and cycles through all the coins
if n == 0:
return 1
if n < 0:
return 0
ways = 0
# if I use for i in range(len(coin_list)), it returns an error message saying that index is out of range
for i in range(len(coin_list)-coinlist_index):
ways += make_change((n - coin_list[i+coinlist_index-1]), coinlist_index)
coinlist_index += 1
return ways
print(make_change(4))
I feel 5 is actually the correct answer.
1 1 1 1
1 1 2
1 2 1
2 1 1
2 2
Or if you want distinct result, you may store results in the list and remove the duplicate result.
def make_change(n, coinlist_index=0):
coin_list = [1, 2]
if n == 0:
return [[]]
if n < 0:
return []
ways = []
for coinlist_index in range(len(coin_list)):
res = make_change((n - coin_list[coinlist_index]), coinlist_index)
ways += list(map(lambda x : x + [coin_list[coinlist_index]], res))
return ways
def remove_dup(lolist):
res = []
for lst in lolist:
lst.sort()
if lst not in res:
res.append(lst)
return res
print remove_dup(make_change(4))

Python 3, any better/cleaner way to write these functions that use for loops?

I'm trying to write code in the most simplest and cleanest way possible. I've found a few ways to shorten and simplify my code through functions that I've never seen before or through using other methods. I'd like to expand my knowledge on writing code using various (but simple) methods, and also expand my function 'vocabulary'.
Here are the functions:
1. Perfect number:
If a number's divisors' sum is equal to the number itself, it is a perfect number. We dont count the number itself as a divisor. E.g. 6's divisors are 1, 2, 3. The sum of the divisors is 6. Therefore 6 is a perfect number.
def perfect_number(num):
if type(num) != int or num < 0:
return None
divisors = []
total = 0
for x in range(num):
if num % (x+1) == 0:
if num != x+1:
divisors += [x+1]
for x in divisors:
total += x
if total == num:
return True
return False
2. Pattern:
A function that takes a positive integer and prints a pattern as follows:
pattern(1): '#-'
pattern(2): '#-#--'
pattern(5): '#-#--#---#----#-----'
def pattern(num):
if type(num) != int or num < 0:
return None
output = ''
for x in range(num):
output += '#'+('-'*(x+1))
return output
3. Reversed Numbers:
A function that takes 2 integers. It goes through every number in the range between those 2 numbers, if one of those numbers is a palindrome (the same thing backwards e.g. 151 is a 'palindrome'), it will increase a variable by 1. That variable is then returned.
invert_number(num) returns the opposite of num as an integer.
def reversed_numbers(low, high):
output = 0
for x in range(low,high+1):
if invert_number(x) == x:
output += 1
return output
It is assumed that low is lower than high.
If I broke a rule or if this doesnt fit here, please tell me where I can post it/how I can improve. Thanks :)

sum even integer in a list of integer (without loop)

I have a task: create a function that takes a integer list as parameter and returns the sum of all even integer in an integer list, without using any kinds of loop. All addition must be done by + operator only. Below is my solution
def sumTest(list_Of_Integers):
return sum(list(filter(lambda x: x%2 == 0, list_Of_Integers)))
I want to ask if there is any better solution, like without using the built-in sum() of python.
Thanks
As stated above in the comments, a more pythonic way of doing this is to use a list comprehension:
def sumTest(list_Of_Integers):
return sum([x for x in list_Of_Integers where x % 2 == 0])
As #UloPe states, however, this sounds very much like a homework question, in which case a more recursive method may be expected (rather than using the sum() function):
def sumTest2(xs):
if len(xs) == 0:
return 0
total = xs[0] if xs[0] % 2 == 0 else 0
return total + sumTest2(xs[1:])
This will generate a function stack dependent on the size of the list.
If you want to generate a shallower stack, then you can do the following:
def sumTest3(xs):
if len(xs) == 0:
return 0
midpoint = len(xs) / 2
total = xs[midpoint] if xs[midpoint] % 2 == 0 else 0
return sumTest3(xs[:midpoint]) + total + sumTest3(xs[midpoint + 1:])
The stack depth of this version will be log(size of list)

Resources