Stop learning alone!

Learn faster and stay on-track by joining this free class with other self-learners.

Register for MIT OpenCourseWare 6.00 Introduction to Computer Science and Programming now.

MIT OpenCourseWare 6.00 Introduction to Computer Science and Programming

Class length: 24 weeks. Start anytime.

Creator: duallain

Status: Established

Join this class!

Lesson 14: Assignment 1: PS 8

Homework Submissions

8 total

sebrenner (Self-grade: Pretty good)
Submitted 3 months ago | Permalink | Time spent: 8 hours
# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name: Scott Brenner
# Collaborators: http://openstudy.com/studypads/Problem-Set-8-4c6bf092e6153a7f05c12e1e
# Time: problem 1: 20 minutes
#

import time
import string
from operator import itemgetter, attrgetter

SUBJECT_FILENAME = "my_subjects.txt"
SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".
    
    returns: dictionary mapping subject name to (value, work)
    """
    result = {}
    inputFile = open(filename)
    
    for line in inputFile:
        line = line.strip()
        line_as_list = line.split(',')
        result[line_as_list[0]] = (int(line_as_list[-2]),int(line_as_list[-1])) 
    return result
            
    # TODO: Instead of printing each line, modify the above to parse the name,
    # value, and work of each subject and create a dictionary mapping the name
    # to the (value, work).

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.
    
    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """    
    # TODO...
    #
    # Build function that sorts based on comparator returns list of subject names sorted by comparator criteria.
    #
    def sort(l, comparator) :
        """
        Sorts the list of subjects' names in descendig order
        acording to the comparator.
        """
        # print "l, comparator type:", type(l), type(comparator)
        # print 
        
        for i in range(1, len(l)) :
            value = l[i]
            j = i - 1
            done = False
            # print 'i, value, j', i, value, j
            # print
            while not done:
                # print "subjects[value], subjects[l[j]] type:", type(subjects[value]), type(subjects[l[j]])
                # print
                if comparator(subjects[value], subjects[l[j]]):
                    l[j+1] = l[j]
                    j -= 1
                    if j < 0 :
                        done = True
                else :
                    done = True
            l[j+1] = value
    #
    # Pick classes from top of sorted list until maxWork is reached
    #
    schedule_list = subjects.keys()
    #print 'schedule_list unsorted: ', schedule_list
    #print
    sort(schedule_list, comparator)
    # print 'schedule_list sorted: ', schedule_list
    #     print
    recommended_schedule = {}
    courseLoad = 0
    done = False
    for course in schedule_list:
        if subjects[course][1] <= maxWork - courseLoad:
            recommended_schedule[course] = subjects[course]
            courseLoad += subjects[course][1]
    return recommended_schedule


def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.
        
    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work) 
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects


counter = 0

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue, subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#

def bruteForceTime():
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    # TODO...
    trial_work = [15]
    total_times = {}
    for each in trial_work:
        start_time = time.time()
        print bruteForceAdvisor(subjects,each)
        end_time = time.time()
        total_times[each] = round(end_time - start_time, 2)
    print total_times

# Problem 3 Observations
# ======================
#
# TODO: write here your observations regarding bruteForceTime's performance
# Th brute force function is very slow for even moderately large course loads.
   # For a maxLoad of  2: 0.01 seconds
                           # 4: 0.22
                           # 6: 1.76
                           # 7: 3.70
                           # 8: 11.42
                           # 9: 27.57
                           # 10: 122.58
                           # 11: 354.55
                           # 12: 778.29
                           # 13: 1714.95
                           # 14: 2907.09
                           # 14: 2850.40
       # Unreasonable depends on a multiple  factors, including the importance of the results and the quality of the results of a faster, less optimal function.  In this case the greedy function probably produces results that nearly as good as the results of the brute force method.
   #  Considering MIT costs ~$200k in tuition and room and board.  Perhaps a few minutes to optimize a semester course load is worth it. 
   


#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.
    
    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    
    These are the results for running this full catalog:
    {8: 0.02, 10: 0.01, 45: 0.080000000000000002, 15: 0.02, 120: 0.23000000000000001, 90: 0.23000000000000001, 60: 0.11, 30: 0.050000000000000003}
        
    """
    # TODO...
    
    rec_dict = {}
    m = {}
        
    #   Build the work and value lists.
    work_list = []
    value_list = []
    key_list = []
    for each in subjects:
        work_list.append(subjects[each][1])
        value_list.append(subjects[each][0])
        key_list.append(each)
    
    # Build optimal list of courses to take.
    value, rec_list = dp_decision_tree(work_list,value_list,len(work_list)-1,maxWork,m)
    
    #   Build dictionary from list.
    for each in rec_list:
        rec_dict[key_list[each]] = (value_list[each],work_list[each])
    return rec_dict

def dp_decision_tree(w,v,i,aW,m):
    """
    Creates a course schedule that is optimized the maximum value.
    """
    
    ## check if value is already in the dictionary
    try: return m[(i,aW)]
    except KeyError:
        ##  Leaf/Bottom of the tree case decision
        if i == 0:
            if w[i] < aW:
                m[(i,aW)] = v[i], [i]
                return v[i],[i]
            else:
                m[(i,aW)] = 0, []
                return 0,[]
    
    ## Calculate with and without i branches
    without_i, course_list = dp_decision_tree(w,v,i-1,aW,m)
    if w[i] > aW:
        m[(i,aW)] = without_i, course_list
        return without_i, course_list
    else:
        with_i, course_list_temp = dp_decision_tree(w, v, i-1, aW - w[i], m)
        with_i += v[i]
    
    ## Take the branch with the higher value
    if with_i > without_i:
        i_value = with_i
        course_list = [i] + course_list_temp
    else:
        i_value = without_i
    
    ## Add this value calculation to the memo
    m[(i,aW)] = i_value, course_list
    return i_value, course_list
    


#
# Problem 5: Performance Comparison
#
def dpTime():
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    
    Prints total schedule, recommended schedule, time to complete each trial.
    """
    # TODO...
    trial_work = [8,10,15,30,45,60,90,120]
    total_times = {}
    for each in trial_work:
        print "Trial for max workload of %i." % each
        start_time = time.time()
        recommendation = dpAdvisor(subjects, each)
        end_time = time.time()
        total_times[each] = round(end_time - start_time, 2)
        printSubjects(recommendation)
    print total_times
    return
    
    

# Problem 5 Observations
# ======================
#
# TODO: write here your observations regarding dpAdvisor's performance and
# how its performance compares to that of bruteForceAdvisor.

subjects = loadSubjects(SUBJECT_FILENAME)
dpTime()

#print subjects
#print "Course Catalog"
#printSubjects(loadSubjects(SUBJECT_FILENAME))

# print 'greedy(cmpValue):'
# printSubjects(greedyAdvisor(subjects, 15, cmpValue))
# 
# print '\ngreedy(cmpWork):'
# printSubjects(greedyAdvisor(subjects, 15, cmpWork))
# 
# print '\ngreedy(cmpRatio)'
# printSubjects(greedyAdvisor(subjects, 15, cmpRatio))

# printSubjects(bruteForceAdvisor(subjects,15))
# 
#bruteForceTime()

#print dpAdvisor(subjects, 15)
tuckertuck (Self-grade: Pretty good)
Submitted 9 months ago | Permalink | Time spent: 2 days

Problem 4 was really hard. It was easy to get the dynamic programming sample from the lecture to return the proper value but to get it to return the dictionary was challenging.

# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name:
# Collaborators:
# Time:
#

import time

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1


#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """

    # The following sample code reads lines from the specified file and prints
    # each one.
    subjects = {}
    inputFile = open(filename)
    for line in inputFile:
        #print line
        l = line.strip() # strips line break
        l = l.split(',') # strips commas and converts into a list
        name = l[0]
        value = int(l[1])
        work = int(l[2])
        subjects[name] = (value, work) 
    return subjects

    # TODO: Instead of printing each line, modify the above to parse the name,
    # value, and work of each subject and create a dictionary mapping the name
    # to the (value, work).

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def printValue(subjects):
    """
    Prints a string containing total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = ''
    subNames = subjects.keys()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        totalVal += val
        totalWork += work
    res = res + 'Total Value: ' + str(totalVal)
    res = res + ' Total Work: ' + str(totalWork)
    print res
    

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """
    # performs a selection sort on the list of the subjects.keys()
    assert type(maxWork) == int and maxWork >= 0
    outputSubjects = {}
    nameList = subjects.keys()
    sortedList = nameList[:]
    sumWork = 0

    for i in range(len(sortedList) - 1):
        minIndx = i
        subInfo1 = subjects[sortedList[i]]
        j = i + 1
        while j < len(sortedList):
            subInfo2 = subjects[sortedList[j]]
            if not comparator(subInfo1, subInfo2):
                minIndx = j
                subInfo1 = subjects[sortedList[j]]
            j += 1
        temp = sortedList[i]
        sortedList[i] = sortedList[minIndx]
        sortedList[minIndx] = temp

    #selects the best answers by the amount of maxWork remaining
    for best in sortedList:
        work = subjects[best][WORK]
        if work + sumWork <= maxWork:
            outputSubjects[best] = subjects[best]
            sumWork += work
        if sumWork == maxWork: break
        
        
    return outputSubjects
        

def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#
def bruteForceTime():
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    maxWork = [1,2,3,4,5,6,7,8,9,10]
    for work in maxWork:
        start_time = time.time()
        bruteForceAdvisor(SUBJECTS, work)
        end_time = time.time()
        total_time = end_time - start_time
        print 'It took %0.3f seconds to complete bruteForceAdvisor for maxWork = %s' % (total_time, work)

# Problem 3 Observations
# ======================
#
# It took   0.002 seconds to complete bruteForceAdvisor for maxWork = 1
# It took   0.007 seconds to complete bruteForceAdvisor for maxWork = 2
# It took   0.026 seconds to complete bruteForceAdvisor for maxWork = 3
# It took   0.095 seconds to complete bruteForceAdvisor for maxWork = 4
# It took   0.306 seconds to complete bruteForceAdvisor for maxWork = 5
# It took   0.995 seconds to complete bruteForceAdvisor for maxWork = 6
# It took   2.875 seconds to complete bruteForceAdvisor for maxWork = 7
# It took   8.130 seconds to complete bruteForceAdvisor for maxWork = 8
# It took  21.928 seconds to complete bruteForceAdvisor for maxWork = 9
# It took  56.926 seconds to complete bruteForceAdvisor for maxWork = 10
# It took 143.230 seconds to complete bruteForceAdvisor for maxWork = 11
# It took 351.753 seconds to complete bruteForceAdvisor for maxWork = 12
# It took 832.148 seconds to complete bruteForceAdvisor for maxWork = 13
# A reasonable amount of time would be less than a minute so 10 hours or less

#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    i = len(nameList) - 1
    outputSubjects = {}
    memo = {}
    bestSubjectsValue, bestSubjectsIndex = dpHelper(tupleList, i, maxWork, memo, ())

    for index in bestSubjectsIndex:
        outputSubjects[nameList[index]] = tupleList[index]
    return outputSubjects

def dpHelper(tupleList, i, maxWork, memo, subjectsIndex):
    try: return memo[(i, maxWork)][0], memo[(i, maxWork)][1] 
    except KeyError:
        if i == 0:
            if tupleList[i][WORK] <= maxWork:
                # if there is room for the last course to be added
                # then it adds the index number of the course to tuple.
                subjectsIndex += (i,)
                memo[(i, maxWork)] = tupleList[i][VALUE], subjectsIndex
                return tupleList[i][VALUE], subjectsIndex
            else:
                memo[(i, maxWork)] = 0, subjectsIndex
                return 0, subjectsIndex
        without_value, without_i = dpHelper(tupleList, i-1, maxWork, memo, subjectsIndex)
        if tupleList[i][WORK] > maxWork:
            memo[(i, maxWork)] = without_value, without_i
            return without_value, without_i
        else:
            with_value, with_i = dpHelper(tupleList, i-1, maxWork - tupleList[i][WORK], memo, subjectsIndex)
            with_value += tupleList[i][VALUE]

        bestSubjectsValue = max(with_value, without_value)
        # conditional decides whether the course was added or not.
        # if the course was added then it adds the index number of the course
        # to the tuple.
        if with_value == bestSubjectsValue:
            subjectsIndex = with_i + (i,)
            memo[(i, maxWork)] = bestSubjectsValue, subjectsIndex
        else:
            subjectsIndex = without_i
            memo[(i, maxWork)] = bestSubjectsValue, subjectsIndex
    return bestSubjectsValue, subjectsIndex



#
# Problem 5: Performance Comparison
#
def dpTime():
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    """
    maxWork = [1,2,3,4,5,6,7,8,9,10,20,25,50,100,200,300,400,500]
    for work in maxWork:
        start_time = time.time()
        dpAdvisor(SUBJECTS, work)
        end_time = time.time()
        total_time = end_time - start_time
        print 'It took %0.3f seconds to complete dpAdvisor for maxWork = %s' % (total_time, work)


def greedyTime():
    maxWork = [1,2,3,4,5,6,7,8,9,10,20,25,50,100,200,300,400,500]
    for work in maxWork:
        start_time = time.time()
        greedyAdvisor(SUBJECTS, work, cmpRatio)
        end_time = time.time()
        total_time = end_time - start_time
        print 'It took %0.3f seconds to complete dpAdvisor for maxWork = %s' % (total_time, work)
    

# Problem 5 Observations
# ======================
#
# TODO: write here your observations regarding dpAdvisor's performance and
# how its performance compares to that of bruteForceAdvisor.
#
# Greedy
# It took 0.058 seconds to complete dpAdvisor for maxWork = 1
# It took 0.058 seconds to complete dpAdvisor for maxWork = 2
# It took 0.059 seconds to complete dpAdvisor for maxWork = 3
# It took 0.057 seconds to complete dpAdvisor for maxWork = 4
# It took 0.055 seconds to complete dpAdvisor for maxWork = 5
# It took 0.057 seconds to complete dpAdvisor for maxWork = 6
# It took 0.057 seconds to complete dpAdvisor for maxWork = 7
# It took 0.057 seconds to complete dpAdvisor for maxWork = 8
# It took 0.056 seconds to complete dpAdvisor for maxWork = 9
# It took 0.057 seconds to complete dpAdvisor for maxWork = 10
# It took 0.057 seconds to complete dpAdvisor for maxWork = 20
# It took 0.057 seconds to complete dpAdvisor for maxWork = 25
# It took 0.058 seconds to complete dpAdvisor for maxWork = 50
# It took 0.058 seconds to complete dpAdvisor for maxWork = 100
# It took 0.057 seconds to complete dpAdvisor for maxWork = 200
# It took 0.057 seconds to complete dpAdvisor for maxWork = 300
# It took 0.056 seconds to complete dpAdvisor for maxWork = 400
# It took 0.056 seconds to complete dpAdvisor for maxWork = 500
# 
# Dynamic Programming
# It took 0.002 seconds to complete dpAdvisor for maxWork = 1
# It took 0.003 seconds to complete dpAdvisor for maxWork = 2
# It took 0.003 seconds to complete dpAdvisor for maxWork = 3
# It took 0.005 seconds to complete dpAdvisor for maxWork = 4
# It took 0.006 seconds to complete dpAdvisor for maxWork = 5
# It took 0.006 seconds to complete dpAdvisor for maxWork = 6
# It took 0.008 seconds to complete dpAdvisor for maxWork = 7
# It took 0.009 seconds to complete dpAdvisor for maxWork = 8
# It took 0.010 seconds to complete dpAdvisor for maxWork = 9
# It took 0.010 seconds to complete dpAdvisor for maxWork = 10
# It took 0.023 seconds to complete dpAdvisor for maxWork = 20
# It took 0.024 seconds to complete dpAdvisor for maxWork = 21
# It took 0.029 seconds to complete dpAdvisor for maxWork = 25
# It took 0.067 seconds to complete dpAdvisor for maxWork = 50
# It took 0.144 seconds to complete dpAdvisor for maxWork = 100
# It took 0.316 seconds to complete dpAdvisor for maxWork = 200
# It took 0.491 seconds to complete dpAdvisor for maxWork = 300
# It took 0.700 seconds to complete dpAdvisor for maxWork = 400
# It took 0.921 seconds to complete dpAdvisor for maxWork = 500
#
# The performance of dpAdvisor increases slowly as maxWork increases because
# as maxWork increases the total number of solutions increases and since the
# complexity is O(ns) where n is number of subjects and s is the number of
# solutions.
#
# bruteForceAdvisor grows exponentially and isn't workable past maxWork > 10
# I'm guessing the complexity is O(2**ns) as the size of the solution grows
# when maxWork increases
#
# greedyAdvisor complexity is O(n) where n is the number of entries in subjects.
# The time it takes to solve doesn't change when maxWork changes. the comparator
# cmpRatio gives the correct answer most of the time, but not always.
#
# dpAdvisor is faster than greedyAdvisor when maxWork < 50
# dpAdvisor is faster than bruteForceAdvisor when maxWork > 1
#

if __name__ == '__main__':
    SUBJECTS = loadSubjects('subjects.txt')
    maxWork = 30
#
# Testing Problem 2
#

##print 'Greedy By Value'
##printSubjects(greedyAdvisor(SUBJECTS, maxWork, cmpValue))
##print 'Greedy By Work'
##printSubjects(greedyAdvisor(SUBJECTS, maxWork, cmpWork))
##print 'Greedy By Ratio'
##printSubjects(greedyAdvisor(SUBJECTS, maxWork, cmpRatio))


##if maxWork <= 10:
##    print 'Brute Force'
##    printSubjects(bruteForceAdvisor(SUBJECTS, maxWork))

#
# Testing Problem 4
#

##print 'Dynamic Programming'
printSubjects(dpAdvisor(SUBJECTS, maxWork))


##for work in range(1,60):
##    print 'Greedy By Ratio'
##    printValue(greedyAdvisor(SUBJECTS, work, cmpRatio))
##    print 'Dynamic Programming'
##    printValue(dpAdvisor(SUBJECTS, work))
##    print


#
# Testing Problem 5
#
               
##print 'Greedy'
##greedyTime()
##print
##print 'Brute Force'
##bruteForceTime()
##print
##print 'Dynamic Programming'
##dpTime()
mercutio22 (Self-grade: Outstanding)
Submitted 1 year ago | Permalink | Time spent: 1 minute
# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name: Hugo Arruda de Moura Torres
# Collaborators:
# Time: Sun Dec5 2010 03:01 AM to...
#

import time
import csv # this module should allow me to treat each value in the lines of the
           # input data file independently 


SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """

    # The following sample code reads lines from the specified file and prints
    # each one.
    work_value = {}
    with open(filename) as arquivo:
        for linha in csv.reader(arquivo):
            work_value[linha[0]] = int(linha[1]), int(linha[2])
    return work_value
        
        
    # TODO: Instead of printing each line, modify the above to parse the name,
    # value, and work of each subject and create a dictionary mapping the name
    # to the (value, work).

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#
def sel_sort(subject_dictionary, comparator):
    """Returns a new comparator-sorted list  with the same elements as subject_dictionary keys.
    A selection sort algorithm is used."""
    lista = subject_dictionary.keys()
    for i in range(len(lista)-1):
        #print lista
        maxIndx = i 
        maxVal = lista[i]
        j = i + 1 #this will walk the lenght of the list
        while j < len(lista):
            if comparator(subject_dictionary[lista[j]],subject_dictionary[maxVal]):
                maxIndx = j 
                maxVal = lista[j]
            j += 1
        temp = lista[i]
        lista[i] = lista[maxIndx]
        lista[maxIndx] = temp
    return lista
    

def quick_sort(subjects, comparator) :
    """
    Sorts the list of subjects' names in descendig order
    acording to the comparator. It uses a "merge sort" algorithm
    """
    lista = subjects.keys()
    
    def merge(left,right, comparator,subjects):
        """Assumes left and right are lists sorted according to 'comparator'.
        Returns a new sorted list containing the same elements
        as (left + right) would contain."""
        #print 'left:', left
        #print 'right', right 
        result = []
        i,j = 0, 0
        while i < len(left) and j < len(right):
            if comparator(subjects[left[i]], subjects[right[j]]):
                result.append(left[i])
                i = i + 1
            else:
                result.append(right[j])
                j = j + 1
        while (i < len(left)):
            result.append(left[i])
            i = i + 1
        while (j < len(right)):
            result.append(right[j])
            j = j + 1
        return result
        
    def mergesort(lista):
        """Returns a new sorted list with the same elements as lista"""
        #print lista
        if len(lista) < 2:
            return lista[:]
        else:
            middle = len(lista) / 2
            left = mergesort(lista[:middle])
            right = mergesort(lista[middle:])
            together = merge(left,right,comparator,subjects)
            #print 'merged', together
            return together
            
    return mergesort(lista)
    
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """
    
    #A greedy algorythm always gets the most valuable item up until the 
    #limit is reached.
    # - First, sort a list of subjects acording to the comparator function in 
    #   question.
    # - Add the values from the sorted list of subjetcs to the results dictionary until
    # - the work limit is reached. 
    
    #materias = sel_sort(subjects, comparator)
    materias = quick_sort(subjects, comparator)
    #print 'sorted:', materias

    GreedyCombo = {}
    sumWork = 0
    count = 0 #trick: this will count the times we take the next 'else' branch
    while sumWork < maxWork:
        for item in materias:
            if  sumWork + subjects[item][WORK] <= maxWork and subjects[item] not in GreedyCombo: # if work of item does not reach or  surpass threshold then I can add subject
                sumWork += subjects[item][WORK]
                GreedyCombo[item] = subjects[item]
            else: #if adding next subject surpasses maxWork do not add the subject
                count +=1
                if count >= len(materias):
                    sumWork = maxWork + 1 # this is a trick to break out of the while loop
                                         ###because all subjects were already tried in this condition
    return GreedyCombo


def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#
def bruteForceTime(subjects, maxwork, k):
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    start_time = time.time()
    # Do some computation. The only purpose of the computation is so we can
    # figure out how long your computer takes to perform a known task.
    result = bruteForceAdvisor(subjects,maxwork)
    end_time = time.time()
    totalTime = end_time - start_time
    print 'It took the brute force algorythm %s seconds to compute' %totalTime
    #printSubjects(result)
    return int(end_time - start_time) * k
    # TODO...

# Problem 3 Observations
# ======================
#
# TODO: write here your observations regarding bruteForceTime's performance
# For maxwork > 10 bruteForceAdvisor seems to take an unreasonable amount of time
# to halt on my PC.


#
# Problem 4: Subject Selection By Dynamic Programming
#

def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)

    Essentially, like the example used in class we are implementing a decision-
    tree comprising two vectors containing work and value entries and a
    maximum work restraint(e.g. available knapsack capacity).
    """
    #this dictionary will hold the best combination of classes to take
    result = {}
    
    #a list of subject names
    #subjectlist = subjects.keys()
    
    #a list of tuples containing subjects's (value, workload)
    tuple_list= subjects.values()
    #print 'tuplas:', tuple_list
    
    #the number of items to be analysed by dynamic programming
    item = len(tuple_list)-1
    magisteria = subjects.keys()
    
    #-memo = memoization dictionary containing precomputed values of the 
    #form item, maxWork: (value).
    memo= {}
    
    selectedSubjects=dpAdvisorHelper(subjects, magisteria, maxWork, item, memo)[1]
    
    for magisteria in selectedSubjects:
        #print magisteria
        result[magisteria]=subjects[magisteria]
    return result
    # this function is ok do not mess with it
        
def dpAdvisorHelper(subjects, magisteria, maxWork, item, memo):
    """ 
    -memo = memoization dictionary containing precomputed values of the 
     form item, maxWork: [value, (selected_subjects,)].
    - index = index of the first undecided object to include in the knapsack
    - reuturns a dictionary mapping (item, maxwork to optimal value and included subjects)
    24:26http://www.youtube.com/watch?v=6h6Fi6AQiRM&feature=related
    """
    
    memoIndex = (item, maxWork)
    
    #try and use pre-computed values:
    try: return memo[memoIndex]
    except KeyError: #if  not there, compute it and store for future use.
        # item 0 means we are at the last subject
        if item == 0:
            # We are considering the first item to include: if we have room, include it, if not, do not
            if subjects[magisteria[item]][WORK] <= maxWork:
                memo[memoIndex] = subjects[magisteria[item]][VALUE], (magisteria[item],) 
            else:
            #withoutItem = dpAdvisorHelper(subjects, tuple_list, item - 1, maxWork, selected_subjects, memo)
                memo[memoIndex] = 0, () # The empty lists represents no item included.
            return memo[memoIndex]
        
        #If we are NOT in the last item we have to consider 3 options
    
        #first, if the class exceeds maxWork, we can't take the class. Over.
        withoutItem = dpAdvisorHelper(subjects, magisteria, maxWork, item-1, memo)
        if subjects[magisteria[item]][WORK] > maxWork:
            memo[memoIndex] = withoutItem
            return memo[memoIndex]
    
        #Now if it doesn't we can either take it or not depending on the Knapsak value:
        
        #Case 1: Including the item: 
        #Get the value for the previous knapsack..
        PreviousValue, PreviousSelection = dpAdvisorHelper(subjects, magisteria,\
        maxWork - subjects[magisteria[item]][WORK], item - 1, memo)
        
        #And add the Item and its value to 
        ValueWithItem = PreviousValue + subjects[magisteria[item]][VALUE] 
        SelectionWithItem = PreviousSelection + (magisteria[item],)
        #=========================================================
        
        #Case 2 Excluding it:
        ValueWithoutItem, SelectionWithoutItem = dpAdvisorHelper(subjects, \
        magisteria, maxWork, item - 1, memo)
        #=========================================================
        
        # Compare case 1 and 2: choose whichever is more valuable:
        if ValueWithItem > ValueWithoutItem:
            memo[memoIndex] = ValueWithItem, SelectionWithItem
        else:
            memo[memoIndex] = ValueWithoutItem, SelectionWithoutItem
        return memo[memoIndex]
    


def dpTime(subjects,MaxWork):
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    """
    start=time.time()
    selection = dpAdvisor(subjects,MaxWork)
    end=time.time()
    totalTime = end - start
    print 'It took %s seconds for the dynamic programming algorythm to \
complete its job' %totalTime
    #printSubjects(selection)
    # TODO...
    

    
smallCatalog = {'teste2':(5,3),'6.00':(16,8),'1.00':(7,7),'6.01':(5,45),'15.01':(9,6), 'teste':(7,7)} #smaller dictionary for debugging purposes

subjects = loadSubjects(SUBJECT_FILENAME) #dictionary containing subjects 
                                          # mapped to work and value         
                                          
##print subjects                                                                                                     
##print 'Triar:'
##printSubjects(smallCatalog)
##selection = dpAdvisor(smallCatalog,15)
##print '==========='
##print 'Dynamic Programming selection: (MaxWork = 15)'
##printSubjects(selection)
##
##
##print 'Triar:'
##printSubjects(smallCatalog)
##selection = greedyAdvisor(smallCatalog, 15, cmpValue)
##print '==========='
##print 'Greedy selection: (MaxWork = 15)'
##printSubjects(selection)


#print subjects                                                                                                     
#print 'Triar:'
##printSubjects(subjects)
##selection = dpAdvisor(subjects,30)
##print '==========='
##print 'Dynamic Programming selection: (MaxWork = 30)'
##printSubjects(selection)

#dpTime(smallCatalog,15)
#bruteForceTime(subjects,30,1)
#bruteForceTime(smallCatalog,15,1)

dpTime(subjects,30)
bruteForceTime(subjects,30,1)


# Problem 5 Observations
# ======================
#
# TODO: write here your observations regarding dpAdvisor's performance and
# how its performance compares to that of bruteForceAdvisor.
#
#Well... it seems that for small datasets the bruteforce algorythm can be pretty quick 
#and finishes at comparable timespan as the dp algorythm
#But it is much slower and even fails to complete when using larger datasets as input,
#it fails to comply and the dp algorythm still is able to finish at relatively short time.
#For instance, calling:
#dpTime(smallCatalog,15)
#bruteForceTime(smallCatalog,15,1) :
#
#It took 7.48634338379e-05 seconds for the dynamic programming algorythm to complete its job
#It took the brute force algorythm 4.29153442383e-05 seconds to compute
#
#When calling:
#dpTime(subjects,30)
#bruteForceTime(subjects,30,1)
#It took 0.0338768959045 seconds for the dynamic programming algorythm to complete its job
#Bruteforce never halts.
#
geoB (Self-grade: Pretty good)
Submitted 1 year ago | Permalink | Time spent: 6 days
# 6.00 Problem Set 9
#
# Name: geoB
# Collaborators:
# Time: far too long!!

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

import random
import time

def makeSmall(subjects, n):
    """
    create a random subset of subjects dictionary with n elements
    """
    if n > len(subjects):
        print 'n is too big!'
        return
    i = 0
    small = {}
    subNames = subjects.keys()
    for i in random.sample(xrange(len(subjects)), n):
        small[subNames[i]] = subjects[subNames[i]]
    return small

def loadSubjects(filename):
    inputFile = open(filename)
    svw = {}
    for line in inputFile:
        str = line.split(',')
        svw[str[0]] = (int(str[1]), int(str[2].strip()))
    return svw

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

subjects = loadSubjects(SUBJECT_FILENAME)

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

small = makeSmall(subjects, 6)

def greedy(s, aW):
    """s: subjects; aW: available work"""
    advice = {}
    L = len(s)
    work = 0
    for i in range(L):
        key = s[i].keys()[0]
        s_work = s[i][key][WORK] + work
        if s_work >= aW:
            return advice
        advice[key] = s[i][key]
        work = s_work
    return advice

def greedyAdvisor(subjects, aW, comparator):
    start = time.time()
    keys = subjects.keys()
    s = selSort(subjects, comparator)
    greed = greedy(s, aW)
    end = time.time()
    deltaT = end - start
    printSubjects(greed)
    print 'Time to calculate: %0.2f seconds' % deltaT
    
def selSort(D, compare):
    """D: dictionary of subjects; compare: comparator function name"""
    keys = D.keys()
    sortedD = []
    for k in range(len(D) - 1):
        for i in range(len(D)):
            minI = i
            minD = D[keys[i]]
            dI = {keys[minI]:minD}
            if dI not in sortedD:
                for j in range(len(D)):
                    jI = {keys[j]:D[keys[j]]}
                    if jI not in sortedD:
                        if compare(D[keys[j]], minD):
                            minI = j
                            minD = D[keys[j]]
                sortedD.append({keys[minI]: minD})
    return sortedD

#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    # TODO...
    memo = {}
    i = len(subjects) - 1
    aW = maxWork
    keys = subjects.keys()
    result = {}
    start = time.time()
    advice = fastDP(subjects, keys, aW, i, memo)
    end = time.time()
    for i in advice[1]:
        result[keys[i]] = subjects[keys[i]]
    printSubjects(result)
    deltaT = end - start
    print 'Time to calculate: %0.2f seconds' % deltaT

def fastDP(subjects, keys, aW, i, memo):
    try: return memo[(i, aW)]
    except KeyError:
        if i == 0:
            if subjects[keys[i]][WORK] <= aW:
                memo[(i, aW)] = [subjects[keys[i]][VALUE], [i]]
                return [subjects[keys[i]][VALUE], [i]]
            else:
                memo[(i, aW)] = [0, []]
                return [0, []]
        without_i = fastDP(subjects, keys, aW, i-1, memo)
        if subjects[keys[i]][WORK] > aW:
            memo[(i, aW)] = without_i
            return without_i
        else:
            with_v = fastDP(subjects, keys, aW-subjects[keys[i]][WORK], i-1, memo)
            with_i = [subjects[keys[i]][VALUE] + with_v[0], with_v[1] + [i]]
        res = max(with_i, without_i)
        if with_i[0] > without_i[0]: res = with_i
        else: res = without_i
        memo[(i, aW)] = res
        return res
    

Comments:

mercutio22
1 year ago

I don't get why with_i is a list and whithou_i is not. There are no comments on the fastDP functions. I can see it works but I can't understand why some steps where taken in such and such way.

Sign up or log in to comment

rfh (Self-grade: Pretty good)
Submitted 1 year ago | Permalink | Time spent: 1 minute
import time
from pprint import pprint

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """

    subjectMapping = {}

    inputFile = open(filename)
    for line in inputFile:
        words = line.strip().split(',')
        subjectMapping[words[0]] = (int(words[1]), int(words[2]))

    return subjectMapping

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """
    subjectsToTake = {}
    currentBest = None
    done = False

    while not done:
        currentBest = None
        for subject in subjects:
            # If we have not selected the subject already and we either
            # do not have a "best" subject yet or the current subject is
            # better than our current best and we can fit it in with regards
            # to our maximum workload
            if not(subject in subjectsToTake) and (not currentBest or comparator(subjects[subject], subjects[currentBest])) and not(maxWork - subjects[subject][1] < 0):
                currentBest = subject

        # Cannot fit another subject in
        if not currentBest:
            done = True
        else:
            subjectsToTake[currentBest] = subjects[currentBest]
            maxWork -= subjects[currentBest][1]

    return subjectsToTake


def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#
def bruteForceTime():
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    start = time.time()
    print "Starting brute force"
    print bruteForceAdvisor(loadSubjects('subjects.txt'), 9)
    print "Ending brute force, took %0.2f seconds" % (time.time() - start)

# Problem 3 Observations
# ======================
#
# Anything over 8 for maxWork is unreasonable (10+ seconds is too much)

#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjectsInfoDict, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjectsInfoDict that provides the maximum value without exceeding maxWork.

    subjectsInfoDict: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    selectedSubjectsDict = {}

    # Just the subject names
    subjects = subjectsInfoDict.keys()

    # Just the subject workload/value pairs
    workloadValueTuples = subjectsInfoDict.values()

    # Start searching for the optimal selection, starting at the top of
    # the subjects stack
    maxValue, selectedSubjects = dpAdvisorHelper(subjects, workloadValueTuples, len(subjects) - 1, maxWork, (), {})

    for subject in selectedSubjects:
        selectedSubjectsDict[subject] = subjectsInfoDict[subject]

    return selectedSubjectsDict

def dpAdvisorHelper(subjects, workloadValueTuples, index, maxWork, selectedSubjects, memo):
    memoIndex = (index, maxWork)

    # Check if the current index/maxWork combination is memoized
    try: return memo[memoIndex]
    except KeyError: pass

    # Index 0 means we are at the last subject
    if index == 0:
        # If we have room, include it, if not, do not
        if workloadValueTuples[index][1] <= maxWork:
            memo[memoIndex] = (workloadValueTuples[index][0], (subjects[index],))
        else:
            memo[memoIndex] = (0, ())

        return memo[memoIndex]

    # Selections if we do not choose the current subject
    withoutCurrentSubject = dpAdvisorHelper(subjects, workloadValueTuples, index - 1, maxWork, selectedSubjects, memo)

    # If we cannot include the current subject, then skip it
    if workloadValueTuples[index][1] > maxWork:
        memo[memoIndex] = withoutCurrentSubject
        return memo[memoIndex]

    # Get the selected subjects and maximum value with the current
    # subject selected
    withValue, withSelected = dpAdvisorHelper(subjects, workloadValueTuples, index - 1, maxWork - workloadValueTuples[index][1], selectedSubjects, memo)
    withValue += workloadValueTuples[index][0]

    without_value, without_selected = withoutCurrentSubject

    # Compare the two and determine which one is better
    if withValue > without_value:
        memo[memoIndex] = (withValue, (subjects[index],) + withSelected)
    else:
        memo[memoIndex] = withoutCurrentSubject

    return memo[memoIndex]

#
# Problem 5: Performance Comparison
#
def dpTime():
    """
    Runs tests on dpAdvisor and measure the time required to compute
    an answer.
    """
    start = time.time()
    print "Starting dp"
    print dpAdvisor(loadSubjects('subjects.txt'), 30)
    print "Ending dp, took %0.2f seconds" % (time.time() - start)

dpTime()
printSubjects(dpAdvisor(loadSubjects('subjects.txt'), 30))
# Problem 5 Observations
# ======================
#
# As the input and maximum workload grows, the dpAdvisor function gets much
# faster than the bruteForceAdvisor function. With the subjects loaded from
# the subjects.txt file and a maximum workload of 9, dpAdvisor is 2226 times
# faster.
Joe (Self-grade: Pretty good)
Submitted 1 year ago | Permalink

problem 4 is really tough. I peeked someone else's code. I suggest read more about dynamic programming.

# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name: Joe Li
# Time: 10:00
#

import time

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """

    # The following sample code reads lines from the specified file and prints
    # each one.
    inputFile = open(filename)
    d={}
    for line in inputFile:
        line = line.split(',')
        subject = line[0]
        value = int(line[1])
        work = int(line[2].strip())
        d[subject]=(value, work)
    return d

    # TODO: Instead of printing each line, modify the above to parse the name,
    # value, and work of each subject and create a dictionary mapping the name
    # to the (value, work).

subjects=loadSubjects(SUBJECT_FILENAME)

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """
    assert type(subjects) == dict, 'subjects should be dictionary'
    assert type(maxWork) == int and maxWork >= 0, 'maxWork should be non-negative int'
    work = 0
    choice = {}
    sub=subjects.copy()
    while work < maxWork:
        b = []
        # b is a list which contains all the best sub in one travserse
        for s in sub:
            best = True
            # assume such subject is the best one
            if work + sub[s][WORK] <= maxWork:
            # if work load exceeds limit after addition, 
            # then we don't even consider such subject
                for others in sub:
                    if work + sub[others][WORK] <= maxWork:
                    # the other subjected must also under the work limit
                    # otherwise the best may not be found
                    # because the best may beyond the work limit
                        if best == True and comparator(sub[others], sub[s]):
                        # as soon as such subject turns out not the best
                        # then the comparason is saved
                            best = False
                            # if there is another subject better than such one
                            # such one cannot be the best, skip the following
                if best == True:
                # if such subject is the best one
                    choice[s] = sub[s]
                    # choose this subject
                    b.append(s)
                    # delete the subject from dictionary later
                    # otherwise it will be found again
                    work += choice[s][WORK]
        if b != []:
        # if there are best ones found
            for d in b:
                del sub[d]
                # delete them all
        else:
        # if no best one found, which may be caused by work load limit,
        # or there's nothing left in the sub
            return choice
    # jump out of the loop if work == maxWork
    return choice
        
        
            

def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#
def bruteForceTime():
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    for maxWork in range(9):
        start_time = time.time()
        bruteForceAdvisor(subjects, maxWork)
        end_time = time.time()
        time_used = round(end_time - start_time,3)
        print 'maxWork:', maxWork, 'time:', time_used


# Problem 3 Observations
# ======================
#
# maxWork time_used(s)
# maxWork: 0 time: 0.001
# maxWork: 1 time: 0.004
# maxWork: 2 time: 0.015
# maxWork: 3 time: 0.058
# maxWork: 4 time: 0.193
# maxWork: 5 time: 0.689
# maxWork: 6 time: 2.008
# maxWork: 7 time: 6.106
# maxWork: 8 time: 16.679

#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    name, v, w = extract(subjects)
    subIndex = range(len(subjects))
    workCap = range(maxWork+1)
    A = {}
    # A[(subIndex, workCap)]: int - given subjects in name[0:subIndex]
    # and work load limit workCap, the best value we can get
    B = {}
    # B[(subIndex, workCap)]: list - all the subjects chosen to get the above value
    for i in subIndex:
    # set base case
        A[(i, 0)] = 0
        B[(i, 0)] = []
    for j in workCap:
        A[(0, j)] = 0
        B[(0, j)] = []
    for i in subIndex[1:]:
    # base case excluded
        for j in workCap[1:]:
            if w[i] > j:
            # if work load limit exeeds, don't take it
                A[(i, j)] = A[(i - 1,j)]
                B[(i, j)] = B[(i - 1,j)]
            else:
                if v[i] + A[(i - 1, j - w[i])] > A[(i - 1 ,j)]:
                # if worth taking it, take it and store the choice to B
                    A[(i, j)] = v[i] + A[(i - 1, j - w[i])]
                    B[(i, j)] = B[(i - 1, j - w[i])]+[name[i]]
                else:
                # if not worth taking it, don't take it
                    A[(i, j)] = A[(i - 1, j)]
                    B[(i, j)] = B[(i - 1,j)]
    output={}
    for name in B[(len(subjects)-1,maxWork)]:
    # to get the form as same as subjects
        output[name] = (subjects[name][VALUE], subjects[name][WORK])
    return output
    

def extract(subjects):
    """
    input: type(subjects) == dict subjects[name] = (VALUE, WORK)
    output: name[i] = name, value[i] = VALUE, work[i] = WORK
    """
    name=[]
    value=[]
    work=[]
    for j in subjects:
        name.append(j)
        value.append(subjects[j][VALUE])
        work.append(subjects[j][WORK])
    return name, value, work
    


	

#
# Problem 5: Performance Comparison
#
def dpTime():
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    """
    for maxWork in range(8):
        start_time = time.time()
        dpAdvisor(subjects, maxWork)
        end_time = time.time()
        time_used = round(end_time - start_time,3)
        print 'maxWork:', maxWork, 'time:', time_used                        
        

# Problem 5 Observations
# ======================
#
# maxWork: 0 time: 0.001
# maxWork: 1 time: 0.002
# maxWork: 2 time: 0.003
# maxWork: 3 time: 0.003
# maxWork: 4 time: 0.007
# maxWork: 5 time: 0.006
# maxWork: 6 time: 0.006
# maxWork: 7 time: 0.007
BTheMad (Self-grade: Pretty good)
Submitted 1 year ago | Permalink
# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name: Alex
#

import time

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#
def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """

    # The following sample code reads lines from the specified file and prints
    # each one.
    inputFile = open(filename)
    subject_dict = {}
    for line in inputFile:
        name, value, work = line.split(',')
        value = int(value)
        work = int(work.strip())
        subject_dict[name] = (value, work)
    return subject_dict

    # TODO: Instead of printing each line, modify the above to parse the name,
    # value, and work of each subject and create a dictionary mapping the name
    # to the (value, work).

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    if val1 > val2:
        return 1
    else:
        return -1

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    if work1 < work2:
        return 1
    else:
        return -1

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    if float(val1) / work1 > float(val2) / work2:
        return 1
    else:
        return -1

#
# Problem 2: Subject Selection By Greedy Optimization
#
def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a bool
    returns: dictionary mapping subject name to (value, work)
    """
    adv_subjects = {}
    work_cnt = 0
    tmp_subj = [(v, w, n) for n, (v, w) in subjects.iteritems()]
    for v, w, n in sorted(tmp_subj, cmp=comparator, reverse=True):
        if (work_cnt + w < maxWork):
            adv_subjects[n] = (v, w)
            work_cnt += w
        else:
            break
    return adv_subjects

def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#
def bruteForceTime():
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    filename = 'subjects.txt'
    max_work = 5
    subject_dict = loadSubjects(filename)
    start_time = time.time()
    adv_subjects = bruteForceAdvisor(subject_dict, max_work)
    end_time = time.time()
    total_time = end_time - start_time
    printSubjects(adv_subjects)
    print "Total time with max_work = %d is %0.4f seconds" %(max_work, total_time)

# Problem 3 Observations
# ======================
#
# TODO: write here your observations regarding bruteForceTime's performance
#
# Problem 4: Subject Selection By Dynamic Programming
#
def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            dpAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0, {})
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def dpAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                                subset, subsetValue, subsetWork, memo):
    global numCalls
    numCalls += 1
    try: return memo[0]
    except KeyError:
        # Hit the end of the list.
        if i >= len(subjects):
            if bestSubset == None or subsetValue > bestSubsetValue:
                # Found a new best.
                memo[0] = subset[:], subsetValue
                return subset[:], subsetValue
            else:
                # Keep the current best.
                memo[0] = bestSubset, bestSubsetValue
                return bestSubset, bestSubsetValue
        else:
            s = subjects[i]
            # Try including subjects[i] in the current working subset.
            if subsetWork + s[WORK] <= maxWork:
                subset.append(i)
                bestSubset, bestSubsetValue = dpAdvisorHelper(subjects,
                        maxWork, i+1, bestSubset, bestSubsetValue, subset,
                        subsetValue + s[VALUE], subsetWork + s[WORK], memo)
                subset.pop()
            bestSubset, bestSubsetValue = dpAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue, subsetWork, memo)
            memo[0] = bestSubset, bestSubsetValue
            return bestSubset, bestSubsetValue

#
# Problem 5: Performance Comparison
#
def dpTime():
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    """
    filename = 'subjects.txt'
    max_work = 15
    # subject_dict = loadSubjects(filename)
    subject_dict = {'6.00': (16, 8), '1.00': (7, 7), '6.01': (5, 3), '15.01': (9, 6)}
    # start_time = time.time()
    
    adv_subjects = dpAdvisor(subject_dict, max_work)
    print numCalls
    # end_time = time.time()
    printSubjects(adv_subjects)
    adv_subjects = bruteForceAdvisor(subject_dict, max_work)
    # print numCalls
    # total_time = end_time - start_time
    printSubjects(adv_subjects)
    # print "Total time with max_work = %d is %0.4f seconds" %(max_work, total_time)

numCalls = 0
dpTime()
mrphud (Self-grade: Pretty good)
Submitted 1 year ago | Permalink
# 6.00 Problem Set 8
#
# Intelligent Course Advisor
#
# Name:
# Collaborators:
# Time:
#

import time

SUBJECT_FILENAME = "subjects.txt"
VALUE, WORK = 0, 1

#
# Problem 1: Building A Subject Dictionary
#

def loadSubjects(filename):
    """
    Returns a dictionary mapping subject name to (value, work), where the name
    is a string and the value and work are integers. The subject information is
    read from the file named by the string filename. Each line of the file
    contains a string of the form "name,value,work".

    returns: dictionary mapping subject name to (value, work)
    """
    # The following sample code reads lines from the specified file and prints
    # each one.
    inputFile = open(filename)
    subjectDict = {}
    for line in inputFile:
        templist = []
        templist += line.strip().split(',')
        subjectDict[templist[0]] = (int(templist[1]),int(templist[2]))
    return subjectDict

def printSubjects(subjects):
    """
    Prints a string containing name, value, and work of each subject in
    the dictionary of subjects and total value and work of all subjects
    """
    totalVal, totalWork = 0,0
    if len(subjects) == 0:
        return 'Empty SubjectList'
    res = 'Course\tValue\tWork\n======\t====\t=====\n'
    subNames = subjects.keys()
    subNames.sort()
    for s in subNames:
        val = subjects[s][VALUE]
        work = subjects[s][WORK]
        res = res + s + '\t' + str(val) + '\t' + str(work) + '\n'
        totalVal += val
        totalWork += work
    res = res + '\nTotal Value:\t' + str(totalVal) +'\n'
    res = res + 'Total Work:\t' + str(totalWork) + '\n'
    print res

def cmpValue(subInfo1, subInfo2):
    """
    Returns True if value in (value, work) tuple subInfo1 is GREATER than
    value in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    return  val1 > val2

def cmpWork(subInfo1, subInfo2):
    """
    Returns True if work in (value, work) tuple subInfo1 is LESS than than work
    in (value, work) tuple in subInfo2
    """
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return  work1 < work2

def cmpRatio(subInfo1, subInfo2):
    """
    Returns True if value/work in (value, work) tuple subInfo1 is 
    GREATER than value/work in (value, work) tuple in subInfo2
    """
    val1 = subInfo1[VALUE]
    val2 = subInfo2[VALUE]
    work1 = subInfo1[WORK]
    work2 = subInfo2[WORK]
    return float(val1) / work1 > float(val2) / work2

#
# Problem 2: Subject Selection By Greedy Optimization
#

def greedyAdvisor(subjects, maxWork, comparator):
    """
    Returns a dictionary mapping subject name to (value, work) which includes
    subjects selected by the algorithm, such that the total work of subjects in
    the dictionary is not greater than maxWork.  The subjects are chosen using
    a greedy algorithm.  The subjects dictionary should not be mutated.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    comparator: function taking two tuples and returning a boolean
    returns: dictionary mapping subject name to (value, work)
    """
    temp_dict = subjects.copy()
    select_dict = {}
    totalWork = 0
    while totalWork < maxWork:
        best = temp_dict.keys()[-1]
        for i in temp_dict:
            if comparator(temp_dict[i], temp_dict[best]):
                best = i
        if totalWork + temp_dict[best][WORK] <= maxWork:
            select_dict[best] = temp_dict.pop(best)
            totalWork += select_dict[best][WORK]
        else:
            break
    return select_dict

def bruteForceAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work), which
    represents the globally optimal selection of subjects using a brute force
    algorithm.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    bestSubset, bestSubsetValue = \
            bruteForceAdvisorHelper(tupleList, maxWork, 0, None, None, [], 0, 0)
    outputSubjects = {}
    for i in bestSubset:
        outputSubjects[nameList[i]] = tupleList[i]
    return outputSubjects

def bruteForceAdvisorHelper(subjects, maxWork, i, bestSubset, bestSubsetValue,
                            subset, subsetValue, subsetWork):
    # Hit the end of the list.
    if i >= len(subjects):
        if bestSubset == None or subsetValue > bestSubsetValue:
            # Found a new best.
            return subset[:], subsetValue
        else:
            # Keep the current best.
            return bestSubset, bestSubsetValue
    else:
        s = subjects[i]
        # Try including subjects[i] in the current working subset.
        if subsetWork + s[WORK] <= maxWork:
            subset.append(i)
            bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                    maxWork, i+1, bestSubset, bestSubsetValue, subset,
                    subsetValue + s[VALUE], subsetWork + s[WORK])
            subset.pop()
        bestSubset, bestSubsetValue = bruteForceAdvisorHelper(subjects,
                maxWork, i+1, bestSubset, bestSubsetValue, subset,
                subsetValue, subsetWork)
        return bestSubset, bestSubsetValue

#
# Problem 3: Subject Selection By Brute Force
#

def bruteForceTime(maxWork):
    """
    Runs tests on bruteForceAdvisor and measures the time required to compute
    an answer.
    """
    start_time = time.time()
    bruteForceAdvisor(subjects, maxWork)
    end_time = time.time()
    total_time = end_time - start_time
    return total_time

# Problem 3 Observations
# ======================
#
# The order of bruteForceAdvisor is clearly exponential in maxWork. 

#
# Problem 4: Subject Selection By Dynamic Programming
#

def dpAdvisor(subjects, maxWork):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)
    """
    nameList = subjects.keys()
    tupleList = subjects.values()
    optimal_value, optimal_tuple = dpAdvisorHelper(maxWork, len(subjects)-1, nameList, tupleList, (), {})
    optimal_dict = {}
    for i in optimal_tuple:
        optimal_dict[i] = subjects[i]
    return optimal_dict

def dpAdvisorHelper(maxWork, index, nameList, tupleList, best_tuple, memo):
    """
    Returns a dictionary mapping subject name to (value, work) that contains a
    set of subjects that provides the maximum value without exceeding maxWork.

    subjects: dictionary mapping subject name to (value, work)
    maxWork: int >= 0
    returns: dictionary mapping subject name to (value, work)"""
    #try and return the stored solution
    try:
        return memo[(index, maxWork)][0], memo[(index, maxWork)][1]
    except KeyError:
        # base case returns nothing if won't fit or the items if it does.
        if index == 0:
            if tupleList[index][WORK] <= maxWork:
                best_tuple += (nameList[index],)
                memo[(index, maxWork)] = (tupleList[index][VALUE], best_tuple)
                return tupleList[index][VALUE], best_tuple
            else:                
                memo[(index, maxWork)] = (0, best_tuple)
                return 0, best_tuple
        # if the course won't fit then skip it.
        without_value, without_tuple = dpAdvisorHelper(maxWork, index-1, nameList, tupleList, best_tuple, memo)
        if tupleList[index][WORK] > maxWork:
            memo[(index, maxWork)] = (without_value, without_tuple) 
            return without_value, without_tuple 
        # if the course will fit then choose the better value of adding the item or skipping it.
        else:
            with_value, with_tuple = dpAdvisorHelper(maxWork - tupleList[index][WORK], index-1, nameList, tupleList, best_tuple, memo)
            total_with_value = with_value + tupleList[index][VALUE]
    if total_with_value > without_value:
        best_tuple = with_tuple + (nameList[index],)
        memo[(index, maxWork)] = (total_with_value, best_tuple)
        return total_with_value, best_tuple
    else:
        return without_value, without_tuple
    
#
# Problem 5: Performance Comparison
#

def dpTime(maxWork):
    """
    Runs tests on dpAdvisor and measures the time required to compute an
    answer.
    """
    start_time = time.time()
    dpAdvisor(subjects, maxWork)
    end_time = time.time()
    total_time = end_time - start_time
    return total_time

# Problem 5 Observations
# ======================
#
# TODO: write here your observations regarding dpAdvisor's performance and
# how its performance compares to that of bruteForceAdvisor.

subjects = loadSubjects(SUBJECT_FILENAME)
smallset = {'6.00':(16, 8),'1.00':(7,7), '6.01':(5,3), '15.01':(9,6)}