comp-think / 2020-2021

The GitHub repository containing all the material related to the Computational Thinking and Programming course of the Digital Humanities and Digital Knowledge degree at the University of Bologna (a.a. 2020/2021).
14 stars 9 forks source link

Lecture "Greedy algorithms", exercise 1 #38

Open essepuntato opened 3 years ago

essepuntato commented 3 years ago

Implement the algorithm introduced in Section "Greedy algorithms" for returning the minimum amount of coins for a change. Accompany the implementation of the function with the appropriate test cases.

dbrembilla commented 3 years ago

I am not really sure why, but while trying to run this function it considered change_value, which was 0, bigger than 0. Is there some problem (maybe with the type float) that might do that?

def coins_for_change(money_to_change):
    change_value = money_to_change
    dict_change = dict()
    while change_value - 0.01 >= 0:
        if change_value >= 2:
            if "2€" not in dict_change:
                dict_change["2€"] = 1
                change_value -= 2
            else:
                dict_change["2€"] += 1
                change_value -= 2
        elif change_value >= 1:
            if "1€" not in dict_change:
                dict_change["1€"] = 1
                change_value -= 1
            else:
                dict_change["1€"] += 1
                change_value -= 1
        elif change_value >= 0.5:
            if "0,50€" not in dict_change:
                dict_change["0,50€"] = 1
                change_value -= 0.5
            else:
                dict_change["0,50€"] += 1
                change_value -= 0.5
        elif change_value >= 0.2:
            if "0,20€" not in dict_change:
                dict_change["0,20€"] = 1
                change_value -= 0.2
            else:
                dict_change["0,20€"] += 1
                change_value -= 0.2
        elif change_value >= 0.1:
            if "0,10€" not in dict_change:
                dict_change["0,10€"] = 1
                change_value -= 0.1
            else:
                dict_change["0,10€"] += 1
                change_value -= 0.1
        elif change_value >= 0.05:
            if "0,05€" not in dict_change:
                dict_change["0,05€"] = 1
                change_value -= 0.05
            else:
                dict_change["0,05€"] += 1
                change_value -= 0.05
        elif change_value >= 0.02:
            if "0,02€" not in dict_change:
                dict_change["0,02€"] = 1
                change_value -= 0.02
            else:
                dict_change["0,02€"] += 1
                change_value -= 0.02
        else:
            if "0,01€" not in dict_change:
                dict_change["0,01€"] = 1
                change_value -= 0.01
            else:
                dict_change["0,01€"] += 1
                change_value -= 0.01
    result = set(dict_change.items())
    return result

def test(money_to_change, expected_set):
    result= coins_for_change(money_to_change).difference(expected_set)
    print(result)
    return result == set()
print(test(1.36, {('0,10€', 1), ('0,01€', 1), ('0,05€', 1), ('0,20€', 1), ('1€', 1)}))
print(test(2.63, {('2€', 1), ('0,50€', 1), ('0,10€', 1), ('0,02€', 1), ("0,01", 1)}))
print(test(3.72, {('0,50€', 1), ('0,02€', 1), ('0,20€', 1), ('1€', 1), ('2€', 1)}))
ChiaraCati commented 3 years ago
def test_coin_change(coins_list, total, given, expected):
    t_result = coin_change(coins_list, total, given)
    if t_result== expected:
        return True
    else:
        return False

def coin_change(coins_list, total, payed):  
    coins_d = dict()
    change = payed - total
    secure = change + 0.001
    tot_amount = 0
    if total >= payed:
        return None

    for coin in coins_list:
        amount = int(secure // coin)
        if amount > 0 and change > 0.001:
            coins_d[coin] = int(amount)
            tot_amount += int(amount)
            change -= (int(amount) * coin)
            secure -= (int(amount) * coin)

    return tot_amount

eu_coins = [2.0, 1.0, 0.50, 0.20, 0.10, 0.05, 0.02, 0.01]

print(test_coin_change(eu_coins, 20, 12.95, None))
print(test_coin_change(eu_coins, 20, 20, None))
print(test_coin_change(eu_coins, 15.36, 20, 6))
print(test_coin_change(eu_coins, 15, 20, 3))
print(test_coin_change(eu_coins, 15.91, 20, 5))
print(test_coin_change(eu_coins, 15.9, 20, 3))
print(test_coin_change(eu_coins, 15.99, 20, 3))
print(test_coin_change(eu_coins, 15.95, 20, 3))
print(test_coin_change(eu_coins, 12.95, 20, 5))
fcagnola commented 3 years ago

I know the instructions were to only return the amount of the change, but I thought it would be useful to see if the function actually worked in real-life situations. I opted for returning a tuple with the list of coins to give back as change and the value of the change in euros.

def test_2_parameter(function, p1, p2, expected):
    result = function(p1, p2)
    return result == expected

def greedy_coin_change(amount, paid):
    coin_types = [2, 1, 0.5, 0.2, 0.1, 0.02, 0.01]  # this list contains all coin types decrementally ordered
    coins = []     # this list will contain the result, with all coins used as change
    if paid == amount:
        return None
    else:
        for coin in coin_types:  # loops through coins
            if amount + coin <= paid:
                while amount + coin <= paid:  # continue adding change until the paid sum is reached
                    amount += coin
                    coins.append(coin)  # add each coin to the result list
    return coins, sum(coins)

print(test_2_parameter(greedy_coin_change, 13, 20, ([2,2,2,1], 7))) # returns True
print(test_2_parameter(greedy_coin_change, 16.73, 20, ([2, 1, 0.2, 0.02, 0.02, 0.02, 0.01], 3.27))) # returns True
print(test_2_parameter(greedy_coin_change, 20, 20, None)) # returns True
AleRosae commented 3 years ago

Since it is supposed to be a greedy algorithm I tried not only to return a collection of coins needed to reach a specific change, but also the "best" set of coins avoiding as much as possible small coins. This code apparently works, but for reasons I cannot understand sometimes it skips an if statement (I have checked that on python tutor), thus returning a different list from the one expected. Can someone explain to me why?

def test_greedy_coins(amount, price, expected):
    if greedy_coins(amount, price) == expected:
        return True
    else:
        return False

def greedy_coins(amount, price):

    change = abs(price - amount) #calculate the change value
    result = list() #create the list of coins to return
    while change != 0: #iterate until there is no charge left
        if (change - 2) >= 0:
            result.append("2€")
            change = change - 2
        else:
            if (change - 1) >= 0:
                    result.append("1€")
                    change = change - 1
            else:
                if (change - 0.50) >= 0:
                        result.append("0,50€")
                        change = change - 0.50
                else:
                    if (change - 0.20) >= 0:
                            result.append("0,20€")
                            change = change - 0.20
                    else:
                        if (change - 0.10) >= 0:
                                result.append("0,10€")
                                change = change - 0.10
                        else:
                            if (change - 0.05) >= 0:
                                    result.append("0,05€")
                                    change = change - 0.05
                            else:
                                if (change - 0.02) >= 0:
                                        result.append("0,02€")
                                        change = change - 0.02
                                else:
                                    if (change - 0.01) >= 0:
                                            result.append("0,01€")
                                            change = change - 0.01
                                    else:
                                        result.append("0,01€")
                                        change = 0

    return result

print(test_greedy_coins(20,16,["2€", "2€"]))
print(test_greedy_coins(20,15.50,["2€", "2€", "0,50€"]))
print(test_greedy_coins(20,13.64,["2€", "2€", "2€", "1€", "0,20€", "0,10€", "0,05€", "0,01€"]))
print(test_greedy_coins(20,13.38,['2€', '2€', '2€', '1€', '0,50€', '0,10€', '0,02€'])) # returns false because the output is ['2€', '2€', '2€', '1€', '0,50€', '0,10€', '0,01€', '0,01€']
yunglong28 commented 3 years ago

Not as precise as @essepuntato's solution for sure but I think this reflects at least the greedy algorithm logic:


   def test_money_change(amount_to_change, coins_list, expected):
    if money_change(amount_to_change, coins_list) == expected:
        return True
    else:
        return False

def money_change(amount_to_change, coins_list):
    change = []

    for coin in coins_list:
        still_to_add = amount_to_change
        maximal = max(coins_list)
        change.append(maximal)  #being greedy: always append the max value
        coins_list.remove(maximal)
        still_to_add -= sum(change)
        if still_to_add > 0:
            if still_to_add in coins_list:  #if I have the exact other value I go with that
                idx = coins_list.index(still_to_add)
                change.append(coins_list[idx])
                still_to_add -= coins_list[idx]
                if still_to_add == 0:
                    return change
        elif still_to_add < 0:
            change.clear()
            still_to_add = 0
        elif still_to_add == 0:
            return change
    return []

print(test_money_change(0.5, [2, 2, 0.5, 0.5, 0.2, 0.5, 1], [0.5]))  #True
print(test_money_change(5, [2, 2, 0.5, 0.5, 0.2, 0.5, 1], [2, 2, 1]))  #True
print(test_money_change(3, [2, 2, 0.5, 0.5, 0.2, 0.5, 1], [2, 1])) #True 
`