Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lecture "Dynamic programming algorithms", exercise 1 #27

Open
essepuntato opened this issue Dec 4, 2018 · 15 comments
Open

Lecture "Dynamic programming algorithms", exercise 1 #27

essepuntato opened this issue Dec 4, 2018 · 15 comments
Labels
Exercise The exercises that are introduced in the lectures.

Comments

@essepuntato
Copy link
Contributor

Write an extension of the multiplication function introduced in the lecture "Recursion", i.e. def multiplication(int_1, int_2, solution_dict), by using a dynamic programming approach. This new function takes in input two integer numbers to multiply and a dictionary with solutions of multiplications between numbers, which can be used to retrieve directly the result of such multiplication if already present in it. The function returns the result of the multiplication and, at the same time, modifies the solution dictionary adding additional solutions when found.

Accompany the implementation of the function with the appropriate test cases.

@essepuntato essepuntato added the Exercise The exercises that are introduced in the lectures. label Dec 4, 2018
@HiImBono
Copy link

HiImBono commented Dec 5, 2018

Hi prof and others,

I did not really understand the point of this exercise since,

If one would draw a tree chart for the multiplication 3×4 according to the example (listing 3) in lecture 'Recursion' there will be no repeating sub-solutions. Meaning that when a sub-solution is stored in the dictionary it will not be used again.

I am correct or am I missing the point?

@hizclick
Copy link

hizclick commented Dec 5, 2018

@HiImBono
example:
I t
2* 4 means 2 + 2 + +2 + 2 or 4 + 4
so
step 1: 2 + 2
step 2: 2 + 4
step 3: 2 + 6
we can use recurssion to solve this problem.
i think in this way we can compute it

def test_multiplication(n1, n2,  expected, d=dict()):
    if multiplication(n1, n2, d=dict()) == expected:
        return True
    else:
        return False
def multiplication(n1, n2, d=dict()):
    if n2 not in d:
        if n2 == 0: 
            d[n2] = n2
        else:
            d[n2]= n1 + multiplication(n1, n2 - 1, d)
    
    return d[n2]
print(test_multiplication(4,3,12)) #True
print(test_multiplication(5,2,10)) #True
print(test_multiplication(3,3,9)) #True

@federicabologna
Copy link

federicabologna commented Dec 5, 2018

def test_mult_dp(n1, n2, d, exp):
    if exp == mult_dp(n1, n2, d):
        return True
    else:
        return False

def mult_dp(n1, n2, d):
    if n2 not in d:
        if n2 == 0:
           d[n2] = 0
        elif n2 == 1:
           d[n2] = n1
        else:
            d[n2] = n1 + mult_dp(n1, n2-1, d)

    return d[n2]

print(test_mult_dp(15, 67, {}, 1005))
print(test_mult_dp(45, 6, {}, 270))
print(test_mult_dp(39, 16, {}, 624))

True
True
True

@EleonoraPeruch
Copy link

# test for the algorithm
def test_multiplication(int_1, int_2, expected, solution_dict):
    result = multiplication(int_1, int_2, solution_dict)
    if expected == result:
        return True
    else:
        return False

# code of the algorithm
def multiplication(int_1, int_2, solution_dict):
    # checking if a solution exists
    if int_2 not in solution_dict:
        if int_2 == 0: # base case
            solution_dict[int_2] = int_2  # if the input int is 0 return that input int 
        else:   # recursive step
            solution_dict[int_2] = int_1 + multiplication(int_1, int_2 - 1, solution_dict) # store the result of the multiplication in the
    return solution_dict[int_2]                                                                                          # dictionary using the original input as key
    

# run some tests
print(test_multiplication(0, 1, 0, ({})))
print(test_multiplication(3, 0, 0, ({})))
print(test_multiplication(0, 0, 0, ({})))
print(test_multiplication(3, 4, 12, ({})))
print(test_multiplication(1, 1, 1, ({})))

True
True
True
True
True

@delfimpandiani
Copy link

def test_mult_dp(multiplied, multiplier, expected):
    result = mult_dp(multiplied, multiplier)
    if expected == result:
        return True
    else:
        return False

# the dictionary is going to be specific for our multiplied,
# and will hold keys of the form --> [multiplier] : result
def mult_dp(multiplied, multiplier, d=dict()):
    
    if multiplier not in d: # Checking if a solution exists
        if multiplier == 0:  # base case
            d[multiplier] = multiplier
        elif multiplier == 1:  # second base case
            d[multiplier] = multiplied
        else:  # recursive step
            # the dictionary will be passed as input of the recursive calls of the algorithm
            d[multiplier] = multiplied + mult_dp(multiplied, (multiplier-1), d)
        return d[multiplier]


print(test_mult_dp(3, 4, 12))
print(test_mult_dp(0, 0, 0))

@simayguzel
Copy link


def test_multiplication(a, b, dic, expected):

    result = multiplication(a, b, dic)
    if result == expected:
        return True
    else:
        return False

def multiplication(a, b, dic=dict()):
    if b not in dic:
        if b == 0:
            dic[b] = 0
        if b == 1:
            dic[b] = a
        elif b < 0:
            dic[b] = -(a - multiplication(a, b+1, dic))
            return dic[b]
        else:
            dic[b] = a + multiplication(a, b-1, dic)
    return dic[b]
print(test_multiplication(3, 4,({}),12))   #True
print((test_multiplication(-3,-4,({}), 12)))   #True
print(test_multiplication(2,0,({}),0))    #True
print(test_multiplication(-10,1,({}),-10))    #True

@beccadelbens
Copy link

def test_multiplication(n1, n2, d, expected):
    result = multiplication(n1, n2, d)
    if result == expected:
        return True
    else:
        return False

def multiplication(n1, n2, d={}):
    if n2 < 0:
        n1 = -n1
        n2 = -n2

    key = str(n1) + "-" + str(n2)

    if key not in d:
        if n2 == 0:
            return 0
        d[key] = n1 + multiplication(n1, n2-1, d)

    return d[key]

d = {}

print(test_multiplication(3, 4, d, 12)) # True
print(test_multiplication(5, 5, d, 25)) # True
print(test_multiplication(-6, 3, d, -18)) # True

@SeverinJB
Copy link

Note:
This dynamic multiplication algorithm recognises if two numbers had been multiplied before. The algorithm creates a dictionary which contains all the product which had been calculated during previous execution. The key is created based on the two factors. The content of this dictionary can be used even if the algorithm is called with new entry values. For example, after multiplying the factors "5" and "7", the product for "3x5" or "5x1" can be retrieved from the dictionary. (The order of the factors makes no difference while searching for previous multiplication.)

+++ Dynamic Multiplication Algorithm +++

# Test Function
def test_multiplication_dp(int_1, int_2, expected): 
    return expected == multiplication_dp(int_1, int_2)  
        
# Algorithm 
def multiplication_dp(int_1, int_2, d=dict()):
    if int_1 < int_2:
        key = str(int_1) + str(int_2)
    else:
        key = str(int_2) + str(int_1)

    if key not in d:
        if int_2 == 0: 
            return 0 
        else:            
            d[key] = int_1 + multiplication_dp(int_1, int_2 - 1) 

    return d[key]

# Test Cases
print(test_multiplication_dp(5, 7, 35))
print(test_multiplication_dp(5, 1, 5))
print(test_multiplication_dp(2, 5, 10))
print(test_multiplication_dp(5, 3, 15))
print(test_multiplication_dp(5, 4, 20))
print(test_multiplication_dp(5, 5, 25))
print(test_multiplication_dp(7, 5, 35))

@tceron
Copy link

tceron commented Dec 9, 2018

def test_multiplication(int_1, int_2, solution_dict, expected):     
    result = multiplication(int_1, int_2, solution_dict)     
    if expected == result:         
        return True     
    else:         
        return False 

def multiplication(int_1, int_2, solution_dict):
    if int_2 not in solution_dict:   #check whether it's in the dict
        if int_2 == 0:
            solution_dict[int_2] = int_2
        else:
            solution_dict[int_2] = int_1 + multiplication(int_1, int_2 -1, solution_dict)  #store info in the dict
    return solution_dict[int_2]

print(test_multiplication(0, 0, ({}), 0)) #true
print(test_multiplication(1, 1, ({}), 1)) #true
print(test_multiplication(5, 2, ({}), 10)) #true
print(test_multiplication(8, 3, ({}), 24)) #true
print(test_multiplication(6, 5, ({}), 30)) #true

@SeverinJB
Copy link

SeverinJB commented Dec 9, 2018

Guys, I'd like to give some feedback if you don't mind.

It seems like only the algorithm of @beccadelbens works. All the other algorithms contain at least one of the following two errors:

  1. Resetting the Dictionary
    The dictionary is only useful if it is available for future executions of the algorithm. Given the formula n1 x n2 = n1 + (n​1 x (n​2 - 1)), the algorithm has to calculate every pair of two factors only once. Hence, the content of the dictionary is not used during the first run of the algorithm. However, if the dictionary is available for future executions the algorithm can save​ computation time if the searched product has common factors with previous products. For example, after having calculated "6x6" the algorithm can retrieve the product for "6x3" from the dictionary.
    The reset was done with an empty dictionary as input value "test_multiplication_dp(5, 7, 35, ({}))" or "test_multiplication_dp(5, 7, 35, d=dict())". (The "d=dict()" is only necessary to define a standard value for d in the def of a new function, but will reset the dictionary if used during the execution of an algorithm.)

  2. Using One Factor as Key
    If only one factor is used as a key, the algorithm fails for many multiplications​ in which one factor is matching the key, but the other factor has changed. For example, if the factor "6" of the calculation "9x6" was used as a key, the algorithm will save "54" as value for the key "6". Hence, the algorithm will return a wrong value for the calculation "10x6". Since the algorithm will search the dictionary for the key "6" and will return the value "54" instead of returning "60".

Hopefully, this is helpful. Correct me if I am mistaken.

Cheers,
Sevi ✌️

@andreamust
Copy link

def test_multiplication(n_1, n_2, expected, d=dict()):
    result = multiplication(n_1, n_2, d=dict())
    if result == expected:
        return True
    else:
        return False

def multiplication(n_1, n_2, d=dict()):
    if n_2 not in d:
        if n_2 == 0:
            d[n_2] = 0
        else:   #recursive step
            d[n_2] = n_1 + multiplication(n_1, n_2 - 1, d)
        return d[n_2]

print(test_multiplication(8, 8, 64, ({})))    #True
print(test_multiplication(2, 9, 18, ({})))    #True
print(test_multiplication(20, 0, 0, ({})))    #True

@lisasiurina
Copy link

lisasiurina commented Dec 9, 2018

@SeverinJB thank you for pointing out the errors:

    result = multiplication(int_1, int_2, solution_dict)
    if result == expected:
        return True
    else:
        return False

def multiplication(int_1, int_2, solution_dict={}):
    if int_2 not in solution_dict:
     if int_2 < 0:
        int_1 = -int_1
        int_2 = -int_2

    key = str(int_1) + "-" + str(int_2)

    if key not in solution_dict:
        if (int_2) == 0:
            return 0
        solution_dict[key] = int_1 + multiplication(int_1, int_2 - 1, solution_dict)
        
    return solution_dict[key]

@friendlynihilist
Copy link

Thank you @SeverinJB for your feedback, it was really helpful.

def test_multiplication(int_1, int_2, d, expected):
    result = multiplication(int_1, int_2, d)
    if expected == result:
        return True
    else:
        return False


def multiplication(int_1, int_2, d={}):

    cache = str(int_1) + "-" + str(int_2)
    if cache not in d:
        if int_2 == 0:
            return 0
        else:
            d[cache] = int_1 + multiplication(int_1, int_2 - 1, d)
    return d[cache]


print(test_multiplication(5, 5, ({}), 25))
print(test_multiplication(7, 5, ({}), 35))
print(test_multiplication(0, 0, ({}), 0))
print(test_multiplication(44, 44, ({}), 1936))

@MilenaCorbellini
Copy link

def test_multiplication(n_1, n_2, expected, d = dict()):
    if multiplication(n_1, n_2, d=dict()) == expected:
        return True
    else:
        return False

def multiplication(n_1, n_2, d=dict()):

    if n_2 not in d:
        if n_2 == 0:
            d[n_2] = n_2
        else:
             d[n_2] = n_1 + multiplication(n_1, n_2 - 1, d)

    return d[n_2]
print(test_multiplication(0, 0, 0, ({})))
print(test_multiplication(1, 0, 0, ({})))
print(test_multiplication(5, 7, 35, ({})))

@essepuntato
Copy link
Contributor Author

Hi all,

Here my take on the exercise - source codes available online.

# Test case for the algorithm
def test_multiplication(int_1, int_2, solution_dict, expected):
    result = multiplication(int_1, int_2, solution_dict)
    if expected == result:
        return True
    else:
        return False


# Code of the algorithm
def multiplication(int_1, int_2, solution_dict):
    if int_1 < int_2:
        mult_pair = (int_1, int_2)
    else:
        mult_pair = (int_2, int_1)

    if mult_pair not in solution_dict:
        if int_2 == 0:
            solution_dict[mult_pair] = 0
        else:
            solution_dict[mult_pair] = int_1 + multiplication(int_1, int_2 - 1, solution_dict)

    return solution_dict[mult_pair]


my_dict = {}
print(test_multiplication(0, 0, my_dict, 0))
print(test_multiplication(1, 0, my_dict, 0))
print(test_multiplication(5, 7, my_dict, 35))
print(test_multiplication(7, 7, my_dict, 49))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Exercise The exercises that are introduced in the lectures.
Projects
None yet
Development

No branches or pull requests