How can I eliminate one of the arguments in this function? - python

I want to eliminate the third argument in my code below which is just an empty array that I think I should be able to create as a local variable within the function itself.
As a bonus, I would also like to build this into a single function although I don't think this can be achieved with the current recursive structure of the code.
I tried creating an empty array as a local variable (See the commented out integers list)
I also tried to create a count variable to increment with each combination found (See the commented out count variable)
def count_combinations(number, integers_available, integers):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
def count_total(number, integers_available, integers):
return len(count_combinations(number, integers_available, integers))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available, []))
I expect to get the same results but with fewer arguments in the function since one of the arguments will have been switched to a local variable instead.

As discussed in the comments, a dynamic programming approach may be more Pythonic here.
from collections import Counter
def ways(total, coins=(1,2,5,10,20,50,100)):
counts = [[Counter()]] + [[] for _ in range(total)]
for coin in coins:
for i in range(coin, total + 1):
counts[i] += [c + Counter({coin: 1}) for c in counts[i-coin]]
return counts[total]
Demo:
>>> ways(15, coins=(1,5,10))
[Counter({1: 15}),
Counter({1: 10, 5: 1}),
Counter({1: 5, 5: 2}),
Counter({5: 3}),
Counter({1: 5, 10: 1}),
Counter({5: 1, 10: 1})]
>>> len(ways(15, coins=(1,5,10)))
6

Without regard to the algorithm or the goal of the code, here's how you can tidy it up a little by making it into a single function and giving integers parameter a default value:
def count_total(number, integers_available):
def count_combinations(number, integers_available, integers=[]):
coin_set = []
# integers = []
# count = 0
if sum(integers) == number:
coin_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers + [integers_available[0]]):
coin_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
coin_set.append(c)
# count += 1
# return count += 1
return coin_set
return len(count_combinations(number, integers_available))
# Testing the code
number = 15
integers_available = [1, 5, 10]
print(count_total(number, integers_available))

Related

Selection sort code is not outputting the right values

I am trying my hand at algorithms for the first time and tried to create a sequential sorting algorithm. I have come up with the code below.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
prev_num = i
counter += 1
new_list.append(i)
else:
prev_num = i
for n in new_list:
if i >= n:
new_list_counter += 1
pass
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
return my_list
print(SelectionSort([1, 3, 4, 2, 6, 5]))
print(SelectionSort([3, 4, 2, 6]))
print(SelectionSort([3, 7, 4, 1]))
print(SelectionSort([3, 10, 2, 5]))
Whilst it seems to sort the first 3 lists just fine, when it comes to the last list it outputs [2, 3, 10, 5].
Can anyone possible lend a hand?
I am sure the logic of your code could be improved and simplified. But with a small change it's working, at least with your 4 test cases.
My solution is to save "prev_num" with the higest existing number in the list using max(). And only save this value at the end of each loop.
def SelectionSort(my_list):
prev_num = None
counter = 0
new_list = []
index_num = 0
new_list_counter = 0
for i in my_list:
if my_list.index(i) == 0:
prev_num = i
counter += 1
new_list.append(i)
else:
if i > prev_num:
counter += 1
new_list.append(i)
else:
for n in new_list:
if i >= n:
new_list_counter += 1
else:
new_list.insert(new_list_counter, i)
index_num = new_list.index(i)
new_list_counter = 0
my_list.remove(i)
my_list.insert(index_num, i)
break
counter += 1
prev_num = i
prev_num = max(new_list) # <-- This is the key: Save at the end of each loop the highest existing number
return my_list
Improvements:
The renaming of the "prev_num" variable to something like "highest_num".
"elif" could be used instead of "else - if"
Comment the code, not only for the reviewrs, it's useful for yourself too.

The sum of numbers

I've been making a program that finds two numbers in a random list and prints them if their sum is 8.
Honestly, I've been sitting here for half an hour and idk what's going on. I think I'm pretty close, but in rare cases it doesn't find an exitsting combination(list = [1,4,4,9] -> No combination). Also in rare cases I will get an error saying
RecursionError: maximum recursion depth exceeded in comparison
Here's my code:
import random
list = []
for i in range(1,5,1):
newNum = random.randint(1,10)
list.append(newNum)
list.sort()
sum = 8
print('\nRandomly generated list:')
print(list)
firstNum = list[0]
lastNum = list[-1]
newList = []
def isSum(a,b):
if a + b == sum:
if list.index(a) == list.index(b):
print('\nThere isnt a combination.')
else:
newList.append(a)
newList.append(b)
print('\nCombination:')
print(newList)
elif a + b < sum:
temp = list.index(a)
temp += 1
if temp > list.index(lastNum):
print('\nThere isnt a combination.')
else:
a = list[temp]
isSum(a,b)
else:
temp = list.index(b)
temp -= 1
if temp < list.index(firstNum):
print('\nThere isnt a combination.')
else:
b = list[temp]
isSum(a,b)
isSum(firstNum,lastNum)
I'm just a beginner, don't get angry if I made a stupid mistake :3
You can use itertools module for generating all combinations of your list, then filter that by calculating the sum of each combination, for example this:
import itertools
a = [1, 4, 4, 9] # any list of nums
groups = 2
result = 8
combinations = [combination for combination in itertools.combinations(a, groups)]
output = [combination for combination in combinations if sum(combination) == result]
print(output)
>>> [(4, 4)]
Recursion really isn't ideal in Python, and your code could certainly be simplified.
This should return all the pairs.
import itertools as itt
import random
from typing import List, Tuple
def is_comb_sum(nums: List[int], comb_size: int, target_sum: int) -> List[Tuple[int, ...]]:
combs = []
for curr_pair in itt.combinations(nums, comb_size):
curr_sum = sum(curr_pair)
if curr_sum == target_sum:
combs.append(curr_pair)
return combs
nums_list = [random.randint(0, 10) for _ in range(5)]
print(nums_list)
res = is_comb_sum(nums_list, 2, 8)
print(res)
If you only want to print each combination once, you can use a set to identify the distinct numbers that are present. Then, for each of these number, you determine which complementing value is need to reach your target (8) and if it is also in the set then the pair exists. The only exception to this is when the number is exactly half of the target (i.e. 4) in which case you have to make sure there are at least two instances of that number in the list:
target = 8
count = 4
numbers = [random.randint(1,10) for _ in range(count)]
print(numbers)
numberSet = set(numbers)
for number in numberSet:
other = target-number
if other not in numberSet: continue
if other > number: continue # avoid duplicates such as 2+6=8 and 6+2=8
if other == number and numbers.count(number) < 2: continue
print(number,"+",other,"=",target)
Output:
[7, 2, 6, 1]
6 + 2 = 8
7 + 1 = 8
If you want to print all the combinations, you can use the Counter object from the collection modules and either print the number of occurrences or repeat the printed lines:
target = 12
count = 8
numbers = [random.randint(1,10) for _ in range(count)]
print(numbers)
from collections import Counter
numberCounts = Counter(numbers)
for number in numberCounts:
other = target-number
if other > number: continue
pairCount = numberCounts[number] * numberCounts[other]
if number == other:
pairCount = (pairCount - numberCounts[number]) // 2
if pairCount > 0:
print(number,"+",other,"=",target,"occurred",pairCount,"time(s)")
Output (target of 12 in list of 8):
[7, 6, 5, 5, 6, 6, 3, 4]
7 + 5 = 12 occurred 2 time(s)
6 + 6 = 12 occurred 3 time(s)

Return index with enumerate

I am basically trying to return the starting index of a DNA string where A and G form the majority over the next five letters.
See code
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
I always get a string index out of range error. The correct answer for the dna at the end is [0,1,4,5,8,9]
Use the count method to count the letters of interest. Start the run of five up until you don't have enough positions left:
def a_g_majority_positions(dna):
lst = []
for start5 in range(len(dna)-4):
five = dna[start5:start5+5]
if five.count('A') + five.count('G') >= 3:
lst.append(start5)
return lst
Or, for the one-statement version, checking each character for being in "AG":
lst = [start5 for start5 in range(len(dna)-4)
if sum(char in "AG"
for char in dna[start5:start5+5]) >= 3]
Output in either case is
[0, 1, 4, 5, 8, 9]
You need to break out of the for loop when there are less than 5 letters left:
def a_g_majority_positions(dna):
result = list()
for index, item in enumerate(dna):
if index + 5 >= len(dna):
break
count = 0
x = 0
while count < 5:
letter = dna[index + count]
count += 1
if letter == 'A':
x += 1
elif letter == 'G':
x += 1
if x >= 3:
result.append(index)
return result
print(a_g_majority_positions("AACCGGTTAACCGGTT"))
Output
[0, 1, 4, 5, 8, 9]
Note
Don't use list as a variable name. It's built-in class and you will introduce hard to find errors by using it as a variable name.
You need to break early from the function when index is greater than len(dna) - 5. Otherwise you try to access dna[len(dna)], which is out of range.
def a_g_majority_positions(dna):
### BEGIN SOLUTION
list=[]
for index, item in enumerate(dna):
count=0
x=0
index1=index
if index > len(dna) - 5:
break
while count < 5:
letter=dna[index1]
index1+=1
count+=1
if letter == 'A':
x+=1
elif letter == 'G':
x+=1
if x>=3:
list.append(index)
return list
### END SOLUTION
a_g_majority_positions("AACCGGTTAACCGGTT")
# Result [0, 1, 4, 5, 8, 9]

How to get number of items in list from Python Function

My function returns a list but fails to return the length of the list.
When I return the list itself (commented in the code below) it is successful but on attempting to return the length of the list it fails yet this usually works with other lists that I work with??!!
I tried using a count variable that I incremented by 1 every time a sub-list was added to the list but on attempting to print the count, the error below still occurred. You can see it commented out in the code as well.
def count_combinations(number, integers_available, integers):
combination_set = []
# count = 0
if sum(integers) == number:
combination_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in count_combinations(number, integers_available[:], integers+[integers_available[0]]):
combination_set.append(c)
# count += 1
for c in count_combinations(number, integers_available[1:], integers):
combination_set.append(c)
# count += 1
return len(combination_set)
# return combination_set
# return count
# Testing the code
number = 10
integers_available = [5, 2, 3]
print(count_combinations(number, integers_available, []))
I expect the result to be 4 since the list is:
[[5, 5], [5, 2, 3], [2, 2, 2, 2, 2], [2, 2, 3, 3]]
But I get the following error:
Traceback (most recent call last):
File "ask.py", line 22, in <module>
print(count_combinations(number, integers_available, []))
File "ask.py", line 12, in count_combinations
for c in count_combinations(number, integers_available[:], integers+[integers_available[0]]):
File "ask.py", line 12, in count_combinations
for c in count_combinations(number, integers_available[:], integers+[integers_available[0]]):
TypeError: 'int' object is not iterable
Your function is recursive. You can wait to convert the list to a length at the end of the work like:
def get_combinations(number, integers_available, integers):
combination_set = []
# count = 0
if sum(integers) == number:
combination_set.append(integers)
# count += 1
elif sum(integers) > number:
pass
elif integers_available == []:
pass
else:
for c in get_combinations(number, integers_available[:],
integers + [integers_available[0]]):
combination_set.append(c)
# count += 1
for c in get_combinations(number, integers_available[1:],
integers):
combination_set.append(c)
# count += 1
return combination_set
def count_combinations(number, integers_available, integers):
return len(get_combinations(number, integers_available, integers))
# Testing the code
number = 10
integers_available = [5, 2, 3]
print(count_combinations(number, integers_available, []))
You are using get_combinations(number, integers_available, integers) as recursive function so this function returns return len(combination_set) the length the list combination_set = []. when you use this function like
for c in get_combinations(number, integers_available[:],
integers + [integers_available[0]]):
since the function returns an integer which is the length of the list, the above for each loop means
for c in len(combination_set):
Note:
lets say the length is 4
for c in 4: # throws 'int' object is not iterable exception.
So your function should return the list not the length of the list
It should be return combination_set
Lets assume that number is valid when it is not 0 then You can then try the code below:
def get_combinations2(number, integers_available, integers):
return len(integers_available)+len(integers)+1 if number != 0 else len(integers_available)+len(integers)

Generate triangular numbers

This function is supposed to take an integer, generate the list of triangular numbers within range of that integer, check that list for the longest list of numbers whose sum == number and return them in a list, otherwise if there is no such list within that range of triangular numbers, return an empty list. I thought I had it somewhat, and it runs on python tutor.com, but when I run it in IDLE, nothing happens.
def checkio(number):
x = 4
lst = [1, 3, 6]
new = []
if number == 0:
return []
elif number == 1:
return [1]
elif number == 3:
return []
elif number == 6:
return []
elif number == 4:
return [1, 3]
elif number == 7:
return [1, 6]
elif number == 10:
return [1, 3, 6]
elif number > 10:
while number > lst[-1]: # Generates a list of all the triangular numbers up to number
for item in range(lst[-1]):
lst.append(x + lst[-1])
x += 1
go = []
start = 0
end = 0
count = 0
while count < len(lst) * 2:
if sum(lst[start:end+1]) < number:
end += 1
count += 1
elif sum(lst[start:end+1]) > number:
start += 1
count += 1
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
if count >= len(lst) * 2:
return []
In the code you post you are just declaring a function. In order to run it, you have to make a call to that function. In your case, it receives one argument, so you have to pass it inside the parentheses ():
number = 5 # for example
checkio(number) # this is the function call
As Bakuriu commented: If you want to get a result change the order of this lines:
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
break
return go
To :
elif sum(lst[start:end+1]) == number:
go.append(lst[start:end+1])
return go
break
This will return a value before escaping the while loop. As noted in the comments (thanks Andrea Corbellini) you can also remove the break statement and it will work well. Because after the return statement by definition escapes the function.
Also to run in idle once defined (you copied the code and pressed return), call it as Christian says.
This way you will check if works.
Note that you don't check in the ifelse clauses for the numbers 2, 5, 8 and 9. If you call this function with checkio(5), like suggested by Crhistian, it will not return anything because it doesn't have anything to return!

Categories

Resources