Counting change dynamic programming: Does order of coin matter? - python

This is the typical count change problem. I tried translating into a tabulation solution a couple of times, until I found one particular translation by another person strange.
def first_denomination(kinds):
x=[50,20,10,5,1]#erroneous for the given DP code
#x=[1,5,10,20,50] #works with DP
return x[kinds-1]
def cc(amt,kinds): #either order of x gets cc(100,5)= 343
if amt==0:
return 1
elif amt<0 or kinds==0:
return 0
else:
return cc(amt,kinds-1)+cc(amt-first_denomination(kinds),kinds)
def dp_cc (amount , kinds_of_coins ): #must put coins in ascending order to get 343
row = [1]*( kinds_of_coins )
table = []
for i in range ( amount +1):
table . append (list(row))
for j in range(kinds_of_coins):
table[0][j]=1
for i in range (1, amount +1):
for j in range (1, kinds_of_coins ):
d = first_denomination ( kinds_of_coins -j+1)
if i >=d:
table [i][j] = table [i][j-1] + table [i-d][j]
else :
table [i][j] = table [i][j-1]
return table [ amount ][ kinds_of_coins -1]
The problem here is if I arrange x in descending order of coins, I get dp_cc(100,5)=8061 which is strange, while only the ascending implementation gets dp_cc(100,5)=343. I have tried other implementations that works for both cases, but I'm not sure why this particular one doesn't work, since order shouldn't matter in these kind of questions. Can anyone enlighten me why?

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)

Write code that find maximum cuts of an iron bar

I'm currently trying to write a Python programm that can find the maxium cuts for a 5m iron bar.
The allowed cuts are : 0.5m, 1m, 1.2m, and 2m.
Given that I have to find all the combinations that match or get close to 5m.
Combinations exemple:
10*0.5 = 5m
7*0.5 + 1.2 = 4.7m
The second combination is also good because we cant produce a 0.3m cut so we throw it away.
The order inside a combination dosent matter,
7x0.5 + 1.2 = 1.2 + 7x0.5
so we have to count it only once.
So ideally my programm take a file in, and write a file with all the combinations like:
c1 = 10*0.5
c2 = 7*0.5 + 1.2
....
Can anyone share advice from where to start please ? Thank you.
Perhaps this is what you are looking for ?
x = [2, 1.2, 1, 0.5]
max_val = 5
def find_next(state, idx, check=False):
if(idx == len(x)):
return print_it(state, check)
if(sum(state) + x[idx] > max_val):
find_next(state, idx + 1, True)
else:
find_next(state + [x[idx]], idx)
find_next(state, idx + 1)
def print_it(state, check):
if check:
print("")
print(sum(state),state)
return
find_next([],0)
It recursively finds the next value until it runs out of values, and it tracks the current state with a simple list which it prints with print_it function. I will leave the formatting and file storage up to you, which can easily be implemented in that function.

Python Beginner: Clerk, smallest number of bills

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)

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

How to Memoize the solution to Unique Paths in Python

I've been trying to solve this problem for a while. A M x N grid is given and we've to find number to paths from top left corner to bottom right corner.
Simple problem though; there are many solutions as well. Here're the details.
http://www.interviewbit.com/courses/programming/topics/math/problems/paths/
http://articles.leetcode.com/2010/11/unique-paths.html
I solved this problem in Java, and wrote one more solution in Python. Now I want to modify the previous solution with Memoized table so that the final answer gets collected at the bottom right cell. Value of a cell is the sum of its right and left adjacent cells.
Here's the code I can't debug:-
class Solution:
#Actual Recursive function
def paths(self,row,col):
if row == 0 or col == 0:
self.Mat[row][col] = 1
return 1
self.Mat[row][col-1] = self.paths(row, col-1)
self.Mat[row-1][col] = self.paths(row-1, col)
self.Mat[row][col] = self.Mat[row][col-1] + self.Mat[row-1][col]
return self.Mat[row][col]
# Driver Function. This will be called
def uniquePaths(self, A, B):
self.Mat = [[-1]*B]*A
ans = self.paths(A-1, B-1)
return self.Mat[A-1][B-1]
And here is my previous solution that works - But doesn't use memoized table.
class OldSolution:
def paths(self,row,col):
if row==0 or col==0:
return 1
elif row<0 or col<0:
return 0
elif row >0 and col > 0:
return self.paths(row-1,col) + self.paths(row,col-1)
def uniquePaths(self, A, B):
Mat = [ [-1] * B ] *A
return self.paths(A-1, B-1)
sol = OldSolution()
print sol.uniquePaths(3,3) # Prints 6
Test Cases:-
3, 3 = 6
15, 9 = 319770
The issue is with the initialization of the matrix. You essentially create the same row duplicated in every column so when you update a cell, all corresponding cells in all columns get updated.
Instead of:
self.Mat = [[-1]*B]*A
Use:
self.Mat = [[-1 for i in range(B)] for j in range(A)]

Categories

Resources