Python Beginner: Clerk, smallest number of bills - python

I need help with the following problem for computer science
A clerk works in a store where the cost of each item is a positive integer number of dollars. So, for example,
something might cost $21, but nothing costs $9.99.
In order to make change a clerk has an unbounded number
of bills in each of the following denominations: $1, $2, $5, $10, and $20.
Write a procedure that takes two
arguments, the cost of an item and the amount paid, and prints how to make change using the smallest
possible number of bills.

Since I am also a beginner, I'll take it as a practice on python. Please see the codes below:
def pay_change(paid, cost):
# set up the change and an empty dictionary for result
change = paid - cost
result = {}
# get the result dictionary values for each bill
n_twenty = change // 20
result['$20'] = n_twenty
rest = change % 20
n_ten = rest // 10
result['$10'] = n_ten
rest = rest % 10
n_five = rest // 5
result['$5'] = n_five
rest = rest % 5
n_two = rest // 2
result['$2'] = n_two
rest = rest % 2
n_one = rest // 1
result['$1'] = n_one
# print(result) if you want to check the result dictionary
# present the result, do not show if value is 0
for k, v in result.items():
if v != 0:
print('Need', v, 'bills of', k)
The logic is to assume the change is over 20, and slowly calculated down, by using //, and calculate the rest by using %. No matter what, we end up with a dictionary, that gives how many bills are needed for each dollar bill.
And then, for those dollar bills that the value is 0, we don't need to show them, so I wrote a for loop to exam the values in this dictionary.
OK, now I've simplified to codes to avoid repeating snippets, I am quite happy with it:
def pay_change(paid, price):
# set up the change and an empty dictionary for result
global change
change = paid - price
bills = ['$20', '$10', '$5', '$2', '$1']
# create a function to calculate the change for each bills
def f(x):
global change
result = divmod(change, x)[0]
change = divmod(change, x)[1]
return result
temp = list(map(f, (20, 10, 5, 2, 1)))
# generate the final result as a dictionary
result = dict(zip(bills, temp))
# present the result, do not show if value is 0
for k, v in result.items():
if v != 0:
print('Need', v, 'bills of', k)

Related

Find minimum number of elements in a list that covers specific values

A recruiter wants to form a team with different skills and he wants to pick the minimum number of persons which can cover all the required skills.
N represents number of persons and K is the number of distinct skills that need to be included. list spec_skill = [[1,3],[0,1,2],[0,2,4]] provides information about skills of each person. e.g. person 0 has skills 1 and 3, person 1 has skills 0, 1 and 2 and so on.
The code should outputs the size of the smallest team that recruiter could find (the minimum number of persons) and values indicating the specific IDs of the people to recruit onto the team.
I implemented the code with brute force as below but since some data are more than thousands, it seems I need to be solved with heuristic approaches. In this case it is possible to have approximate answer.
Any suggestion how to solve it with heuristic methods will be appreciated.
N,K = 3,5
spec_skill = [[1,3],[0,1,2],[0,2,4]]
A = list(range(K))
set_a = set(A)
solved = False
for L in range(0, len(spec_skill)+1):
for subset in itertools.combinations(spec_skill, L):
s = set(item for sublist in subset for item in sublist)
if set_a.issubset(s):
print(str(len(subset)) + '\n' + ' '.join([str(spec_skill.index(item)) for item in subset]))
solved = True
break
if solved: break
Here is my way of doing this. There might be potential optimization possibilities in the code, but the base idea should be understandable.
import random
import time
def man_power(lst, K, iterations=None, period=0):
"""
Specify a fixed number of iterations
or a period in seconds to limit the total computation time.
"""
# mapping each sublist into a (sublist, original_index) tuple
lst2 = [(lst[i], i) for i in range(len(lst))]
mini_sample = [0]*(len(lst)+1)
if period<0 or (period == 0 and iterations is None):
raise AttributeError("You must specify iterations or a positive period")
def shuffle_and_pick(lst, iterations):
mini = [0]*len(lst)
for _ in range(iterations):
random.shuffle(lst2)
skillset = set()
chosen_ones = []
idx = 0
fullset = True
# Breaks from the loop when all skillsets are found
while len(skillset) < K:
# No need to go further, we didn't find a better combination
if len(chosen_ones) >= len(mini):
fullset = False
break
before = len(skillset)
skillset.update(lst2[idx][0])
after = len(skillset)
if after > before:
# We append with the orginal index of the sublist
chosen_ones.append(lst2[idx][1])
idx += 1
if fullset:
mini = chosen_ones.copy()
return mini
# Estimates how many iterations we can do in the specified period
if iterations is None:
t0 = time.perf_counter()
mini_sample = shuffle_and_pick(lst, 1)
iterations = int(period / (time.perf_counter() - t0)) - 1
mini_result = shuffle_and_pick(lst, iterations)
if len(mini_sample)<len(mini_result):
return mini_sample, len(mini_sample)
else:
return mini_result, len(mini_result)

Nested FOR cycle wih a dictionary. How to start checking the second FOR from the value the first FOR uses

So I have a dictionary in which the tag is the patient code or name(string), and the number associated is the consult time(int). For example I have this:
{'0001': 15, 'Charles': 20} The person '0001' takes 15 minutes in a consult, the person 'Charles' takes 20 minutes.
I do not know in advance where there is a name or a number identifying a person(but they are both strings), that is, I don't know where Charles is located in the dictionary or even if it exists at all
I want to run this dictionary to check the patients that have the same consult time and if they have I want to run a certain function which is not relevant. For now I have this:
for pat_i in self.MD_dict:
for pat_j in self.MD_dict:
if(self.MD_dict[pat_i]==self.MD_dict[pat_j]):
aux=aux+1
funtion(pat_i, pat_j);
However I want j to start from i+1. How do I do that?
I tried using the range fucntion:
for pat_i in range(len(self.MD_dict)):
for pat_j in range(i+1,len(self.MD_dict)):
if(self.MD_dict[pat_i]==self.MD_dict[pat_j]):
aux=aux+1
funtion(pat_i, pat_j);
But because pat_i could be a string this doens't work . For example with pat_i=1 and pat_j=2 the code necessary for the if to work would be '0001' and Charles. But since pat_i!='0001' and pat_j!='Charles' it doesn't work
In the original dictionary, patient name/id is key and time is value. In the new dictionary, time is key and list of paitent name/id is the value.
This is the idea of making a dictionary for inverse look up and then combine patient name/id with the same time into a list.
def make_inverse_dict(x):
# Time is key. List of patients is value
time_dict = {}
for name, time in x.items():
if time in time_dict:
time_dict[time].append(name)
else:
time_dict[time] = [name]
return time_dict
def f(p1, p2):
print(f" {p1}, {p2}")
def make_patient_pair(patient_list):
# Triangular loop to make pairs of patients
# without duplication
n_patients = len(patient_list)
out = []
for i in range(n_patients):
for j in range(i):
pair = (patient_list[i], patient_list[j])
out.append(pair)
return out
def main():
patient_dict = {'0001': 15,
'0002': 10,
'Charles': 20,
'Ann': 20,
'0003': 15}
time_dict = make_inverse_dict(patient_dict)
for time, patient_list in time_dict.items():
n_patients = len(patient_list)
print("time: ", time)
if n_patients > 1:
for p1, p2 in make_patient_pair(patient_list):
f(p1, p2)
main()
result:
time: 15
0003, 0001
time: 10
time: 20
Ann, Charles
The issue with your code is that you are iterating through it more than once. On the second iteration, you then check if MD_dict[pat_i] is equal to MD_dict[pat_j]. This means that you are going to get double the amount of data because at one point, the second loop will reach the same exact point that the first loop is on. For example, when the first loop reaches a value, let's say, "Charles," the second loop will first start at the beginning and then it will eventually reach "Charles." Obviously, the value for "Charles" isn't going to change between the first and second loop - this will result in an instance in which you would be passing "Charles" and "Charles" into your function, which is not what you want.
Let's say MD_dict is:
MD_dict = {'0001': 15, 'Charles': 20, '0002':15, 'Alex': 20, 'Jack':15}
What we can do is use enumerate to make sure these duplicates don't happen:
pats = list(MD_dict.keys())
enum = list(enumerate(pats))
Above, enum pairs the patients' name and the index at which they appear:
[(0, '0001'), (1, 'Charles'), (2, '0002'), (3, 'Alex'), (4, 'Jack')]
Then, we can iterate over enum twice:
for x, pat_i in enum:
for y,pat_j in enum:
if x != y and MD_dict[pat_i] == MD_dict[pat_j]:
function(pat_i, pat_j) # apply function here
Notice how x != y appears in the conditional statement. This is to avoid pairing the same person with itself. This ensures that the second loop will not consider the person that the first loop is on. The result is:
function(0001, 0002) # consult times == 15
function(0001, Jack) # consult times == 15
function(Charles, Alex) # consult times == 20
function(0002, 0001) # consult times == 15
function(0002, Jack) # consult times == 15
function(Alex, Charles) # consult times == 20
function(Jack, 0001) # consult times == 15
function(Jack, 0002) # consult times == 15
However, there is one issue with the function above, and I am not totally clear on what you were asking for in the question. This problem is that the function will be applied to "Charles" and "Alex" and then "Alex" and "Charles" later on. This is because we run through the dictionary twice, so it's going to pick up "Alex" as a match when the first loop hits "Charles" and it will pick up "Charles" as a match when the first loop hits "Alex." If this is not what you want, we can slice enum on the second loop:
pats = list(MD_dict.keys())
enum = list(enumerate(pats))
for x, pat_i in enum:
for y,pat_j in enum[x+1:]:
if MD_dict[pat_i] == MD_dict[pat_j]:
function(pat_i, pat_j) # apply function here
Above, for y,pat_j in enum[x+1:] will only consider the people that follow the person that the first loop is on. We then do not have to check if x != y. The output is as follows:
function(0001, 0002) # consult times == 15
function(0001, Jack) # consult times == 15
function(Charles, Alex) # consult times == 20
function(0002, Jack) # consult times == 15
sorted_items = sorted(self.MD_dict.items(), key=lambda x: x[1])
for i, (pat_i, pat_i_val) in enumerate(sorted_items):
for (pat_j, pat_j_val) in sorted_items[i+1:]
if pat_i_val == pat_j_val:
aux=aux+1
funtion(pat_i, pat_j)

Change the string output to int to obtain the maximum number?

I am quite new to python so still getting to grips with the language.
I have the following function which takes a string and apply it to an algorithm which tells us if it aligns to models 1, 2, 3, 4, or 5.
Currently this piece of code:
def apply_text(text):
test_str = [text]
test_new = tfidf_m.transform(test_str)
prediction = 0
for m in range(0,5):
percentage = '{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
print(percentage)
And running the following function: apply_text('Terrible idea.')
Gives the following output:
71.4%
33.1%
2.9%
1.6%
4.9%
With Model 1 = 71.4%, Model 2 = 33.1%, ... Model 5 = 4.9%.
I want to only output the Model number where there is the highest percentage. So in the above example, the answer would be 1 as this has 71.4%.
As the output is a string type I am finding it difficult to find ways of converting this to an int and then comparing each value (probably in a loop of some sort) to obtain the maximum value
I think you want to save the percentages along with the model number, sort it and then return the highest.
This can be done by something like this:
def apply_text(text):
test_str = [text]
test_new = tfidf_m.transform(test_str)
prediction = 0
percentage_list = []
for m in range(0,5):
percentage = '{P:.1}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
percentage_list.append([m+1, float(percentage)])
percentage_list.sort(reverse=True, key=lambda a: a[1])
return percentage_list[0][0]
Things to note:
Sorting in reverse order as default is ascending. You could skip reversing and access the last element of precentage_list by accessing -1 element
The key function is used as we need to sort using the percentage
Try putting values in a list then you can utilize list methods:
percentage = []
for m in range(0, 5):
percentage.append('{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1]))
print(*percentage, sep='\n')
print('Max on model', percentage.index(max(percentage)))
Or using a dictionary:
percentage = {}
for m in range(0, 5):
percentage['Model ' + str(m)] = '{P:.1%}'.format(M=cat[m], P=lr_m[m].predict_proba(test_new)[0][1])
print(*percentage, sep='\n')
print('Max on', max(percentage.keys(), key=(lambda key: percentage[key])))

Sorting from smallest to biggest

I would like to sort several points from smallest to biggest however.
I will wish to get this result:
Drogba 2 pts
Owen 4 pts
Henry 6 pts
However, my ranking seems to be reversed for now :-(
Henry 6 pts
Owen 4 pts
Drogba 2 pts
I think my problem is with my function Bubblesort ?
def Bubblesort(name, goal1, point):
swap = True
while swap:
swap = False
for i in range(len(name)-1):
if goal1[i+1] > goal1[i]:
goal1[i], goal1[i+1] = goal1[i+1], goal1[i]
name[i], name[i+1] = name[i+1], name[i]
point[i], point[i + 1] = point[i + 1], point[i]
swap = True
return name, goal1, point
def ranking(name, point):
for i in range(len(name)):
print(name[i], "\t" , point[i], " \t ")
name = ["Henry", "Owen", "Drogba"]
point = [0]*3
goal1 = [68, 52, 46]
gain = [6,4,2]
name, goal1, point = Bubblesort( name, goal1, point )
for i in range(len(name)):
point[i] += gain[i]
ranking (name, point)
In your code:
if goal1[i+1] > goal1[i]:
that checks if it is greater. You need to swap it if the next one is less, not greater.
Change that to:
if goal1[i+1] < goal1[i]:
A bunch of issues:
def Bubblesort - PEP8 says function names should be lowercase, ie def bubblesort
You are storing your data as a bunch of parallel lists; this makes it harder to work on and think about (and sort!). You should transpose your data so that instead of having a list of names, a list of points, a list of goals you have a list of players, each of whom has a name, points, goals.
def bubblesort(name, goal1, point): - should look like def bubblesort(items) because bubblesort does not need to know that it is getting names and goals and points and sorting on goals (specializing it that way keeps you from reusing the function later to sort other things). All it needs to know is that it is getting a list of items and that it can compare pairs of items using >, ie Item.__gt__ is defined.
Instead of using the default "native" sort order, Python sort functions usually let you pass an optional key function which allows you to tell it what to sort on - that is, sort on key(items[i]) > key(items[j]) instead of items[i] > items[j]. This is often more efficient and/or convenient than reshuffling your data to get the sort order you want.
for i in range(len(name)-1): - you are iterating more than needed. After each pass, the highest value in the remaining list gets pushed to the top (hence "bubble" sort, values rise to the top of the list like bubbles). You don't need to look at those top values again because you already know they are higher than any of the remaining values; after the nth pass, you can ignore the last n values.
actually, the situation is a bit better than that; you will often find runs of values which are already in sorted order. If you keep track of the highest index that actually got swapped, you don't need to go beyond that on your next pass.
So your sort function becomes
def bubblesort(items, *, key=None):
"""
Return items in sorted order
"""
# work on a copy of the list (don't destroy the original)
items = list(items)
# process key values - cache the result of key(item)
# so it doesn't have to be called repeatedly
keys = items if key is None else [key(item) for item in items]
# initialize the "last item to sort on the next pass" index
last_swap = len(items) - 1
# sort!
while last_swap:
ls = 0
for i in range(last_swap):
j = i + 1
if keys[i] > keys[j]:
# have to swap keys and items at the same time,
# because keys may be an alias for items
items[i], items[j], keys[i], keys[j] = items[j], items[i], keys[j], keys[i]
# made a swap - update the last_swap index
ls = i
last_swap = ls
return items
You may not be sure that this is actually correct, so let's test it:
from random import sample
def test_bubblesort(tries = 1000):
# example key function
key_fn = lambda item: (item[2], item[0], item[1])
for i in range(tries):
# create some sample data to sort
data = [sample("abcdefghijk", 3) for j in range(10)]
# no-key sort
assert bubblesort(data) == sorted(data), "Error: bubblesort({}) gives {}".format(data, bubblesort(data))
# keyed sort
assert bubblesort(data, key=key_fn) == sorted(data, key=key_fn), "Error: bubblesort({}, key) gives {}".format(data, bubblesort(data, key_fn))
test_bubblesort()
Now the rest of your code becomes
class Player:
def __init__(self, name, points, goals, gains):
self.name = name
self.points = points
self.goals = goals
self.gains = gains
players = [
Player("Henry", 0, 68, 6),
Player("Owen", 0, 52, 4),
Player("Drogba", 0, 46, 2)
]
# sort by goals
players = bubblesort(players, key = lambda player: player.goals)
# update points
for player in players:
player.points += player.gains
# show the result
for player in players:
print("{player.name:<10s} {player.points:>2d} pts".format(player=player))
which produces
Drogba 2 pts
Owen 4 pts
Henry 6 pts

Do dictionaries keep track of the point in time where a item was assigned?

I was coding a High Scores system where the user would enter a name and a score then the program would test if the score was greater than the lowest score in high_scores. If it was, the score would be written and the lowest score, deleted. Everything was working just fine, but i noticed something. The high_scores.txt file was like this:
PL1 50
PL2 50
PL3 50
PL4 50
PL5 50
PL1 was the first score added, PL2 was the second, PL3 the third and so on. Then I tried adding another score, higher than all the others (PL6 60) and what happened was that the program assigned PL1 as the lowest score. PL6 was added and PL1 was deleted. That was exactly the behavior I wanted but I don't understand how it happened. Do dictionaries keep track of the point in time where a item was assigned? Here's the code:
MAX_NUM_SCORES = 5
def getHighScores(scores_file):
"""Read scores from a file into a list."""
try:
cache_file = open(scores_file, 'r')
except (IOError, EOFError):
print("File is empty or does not exist.")
return []
else:
lines = cache_file.readlines()
high_scores = {}
for line in lines:
if len(high_scores) < MAX_NUM_SCORES:
name, score = line.split()
high_scores[name] = int(score)
else:
break
return high_scores
def writeScore(file_, name, new_score):
"""Write score to a file."""
if len(name) > 3:
name = name[0:3]
high_scores = getHighScores(file_)
if high_scores:
lowest_score = min(high_scores, key=high_scores.get)
if new_score > high_scores[lowest_score] or len(high_scores) < 5:
if len(high_scores) == 5:
del high_scores[lowest_score]
high_scores[name.upper()] = int(new_score)
else:
return 0
else:
high_scores[name.upper()] = int(new_score)
write_file = open(file_, 'w')
while high_scores:
highest_key = max(high_scores, key=high_scores.get)
line = highest_key + ' ' + str(high_scores[highest_key]) + '\n'
write_file.write(line)
del high_scores[highest_key]
return 1
def displayScores(file_):
"""Display scores from file."""
high_scores = getHighScores(file_)
print("HIGH SCORES")
if high_scores:
while high_scores:
highest_key = max(high_scores, key=high_scores.get)
print(highest_key, high_scores[highest_key])
del high_scores[highest_key]
else:
print("No scores yet.")
def resetScores(file_):
open(file_, "w").close()
No. The results you got were due to arbitrary choices internal to the dict implementation that you cannot depend on always happening. (There is a subclass of dict that does keep track of insertion order, though: collections.OrderedDict.) I believe that with the current implementation, if you switch the order of the PL1 and PL2 lines, PL1 will probably still be deleted.
As others noted, the order of items in the dictionary is "up to the implementation".
This answer is more a comment to your question, "how min() decides what score is the lowest?", but is much too long and format-y for a comment. :-)
The interesting thing is that both max and min can be used this way. The reason is that they (can) work on "iterables", and dictionaries are iterable:
for i in some_dict:
loops i over all the keys in the dictionary. In your case, the keys are the user names. Further, min and max allow passing a key argument to turn each candidate in the iterable into a value suitable for a binary comparison. Thus, min is pretty much equivalent to the following python code, which includes some tracing to show exactly how this works:
def like_min(iterable, key=None):
it = iter(iterable)
result = it.next()
if key is None:
min_val = result
else:
min_val = key(result)
print '** initially, result is', result, 'with min_val =', min_val
for candidate in it:
if key is None:
cmp_val = candidate
else:
cmp_val = key(candidate)
print '** new candidate:', candidate, 'with val =', cmp_val
if cmp_val < min_val:
print '** taking new candidate'
result = candidate
return result
If we run the above on a sample dictionary d, using d.get as our key:
d = {'p': 0, 'ayyy': 3, 'b': 5, 'elephant': -17}
m = like_min(d, key=d.get)
print 'like_min:', m
** initially, result is ayyy with min_val = 3
** new candidate: p with val = 0
** taking new candidate
** new candidate: b with val = 5
** new candidate: elephant with val = -17
** taking new candidate
like_min: elephant
we find that we get the key whose value is the smallest. Of course, if multiple values are equal, the choice of "smallest" depends on the dictionary iteration order (and also whether min actually uses < or <= internally).
(Also, the method you use to "sort" the high scores to print them out is O(n2): pick highest value, remove it from dictionary, repeat until empty. This traverses n items, then n-1, ... then 2, then 1 => n+(n-1)+...+2+1 steps = n(n+1)/2 = O(n2). Deleting the high one is also an expensive operation, although it should still come in at or under O(n2), I think. With n=5 this is not that bad (5 * 6 / 2 = 15), but ... not elegant. :-) )
This is pretty much what http://stromberg.dnsalias.org/~strombrg/python-tree-and-heap-comparison/ is about.
Short version: Get the treap module, which works like a sorted dictionary, and keep the keys in order. Or use the nest module to get the n greatest (or least) values automatically.
collections.OrderedDict is good for preserving insertion order, but not key order.

Categories

Resources