MIT6.0002 笔记,LECTURE1&2 Optimization

最优化问题

这是MIT课程MIT60002 Computational thinking and data science 的第一二课,主要内容是最优化,包括贪心算法,暴力算法和动态编程(dynamic programming)。

贪心算法(greedy algorithm)

贪心算法的优缺点

优点

  1. 容易实现
  2. 效率很高

缺点

  1. 不一定能找到全局最优,往往找到的是局部最优。

暴力算法(brute force algorithm)

优点

  1. 一定能找到全局最优

缺点

  1. 运算复杂度很高

动态规划(dynamic programming)

综合了以上两者的优点,一定能找到全局最优,复杂度介于两者之间。

Lecture1&2 总结

  1. 大部分的实际问题都能转化为最优化问题
  2. 贪心算法能提供最够优秀的解(虽然不是最优解)
  3. 找到最优解的运算复杂度一般是指数级
  4. 对于最优化问题的一个子类,动态规划能够提供最够好的性能。这个子类需要同时满足optimal substructure和overlapping subprograms两个条件。
    在这里插入图片描述

动态规划

动态规划原理
虽然已经用动态规划方法解决了上面两个问题,但是大家可能还跟我一样并不知道什么时候要用到动态规划。动态规划问题需要满足两个条件:重叠子问题,和最优子结构。

最优子结构(optimal substructure)
用动态规划求解最优化问题的第一步就是刻画最优解的结构,如果一个问题的解结构包含其子问题的最优解,就称此问题具有最优子结构性质。因此,某个问题是否适合应用动态规划算法,它是否具有最优子结构性质是一个很好的线索。使用动态规划算法时,用子问题的最优解来构造原问题的最优解。因此必须考查最优解中用到的所有子问题。

重叠子问题(overlapping subproblems)
在斐波拉契数列结构图中,可以看到大量的重叠子问题,比如说在求fib(6)的时候,fib(2)被调用了5次。如果递归算法反复求解相同的子问题,就称为具有重叠子问题(overlapping subproblems)性质。在动态规划算法中使用数组来保存子问题的解,这样子问题多次求解的时候可以直接查表不用调用函数递归。

作业

这两节课的课后作业有三个,分别针对贪心算法,暴力算法和动态规划。首先是奶牛问题,需要讲一些牛运到对岸去,每头牛有各自的重量,每次运送有重量要求,求最少的运送次数。

贪心算法

  1. 首先对牛根据重量排序,降序排列。
  2. 只要船还能装,就从剩下的中间选最重的。
def greedy_cow_transport(cows,limit=10):
    """
    Uses a greedy heuristic to determine an allocation of cows that attempts to
    minimize the number of spaceship trips needed to transport all the cows. The
    returned allocation of cows may or may not be optimal.
    The greedy heuristic should follow the following method:

    1. As long as the current trip can fit another cow, add the largest cow that will fit
        to the trip
    2. Once the trip is full, begin a new trip to transport the remaining cows

    Does not mutate the given dictionary of cows.

    Parameters:
    cows - a dictionary of name (string), weight (int) pairs
    limit - weight limit of the spaceship (an int)
    
    Returns:
    A list of lists, with each inner list containing the names of cows
    transported on a particular trip and the overall list containing all the
    trips
    """
    cows_sorted = sorted(cows.items(), key  = lambda x:x[1], reverse = True)
    total_trips = 0
    weighs_per_trip = 0
    list_per_trip= []
    total_list = []
    transported = []
    while len(transported) < len(cows):
        for cow in cows_sorted:
            if cow[0] not in transported:
                weight = int(cow[1])
                if(weighs_per_trip + weight <= limit):
                    weighs_per_trip += weight
                    list_per_trip.append(cow[0])
                    transported.append(cow[0])
        total_list.append(list_per_trip)
        weighs_per_trip = 0
        list_per_trip = []
        total_trips += 1
    return total_list

暴力算法

def brute_force_cow_transport(cows,limit=10):
    """
    Finds the allocation of cows that minimizes the number of spaceship trips
    via brute force.  The brute force algorithm should follow the following method:

    1. Enumerate all possible ways that the cows can be divided into separate trips 
        Use the given get_partitions function in ps1_partition.py to help you!
    2. Select the allocation that minimizes the number of trips without making any trip
        that does not obey the weight limitation
            
    Does not mutate the given dictionary of cows.

    Parameters:
    cows - a dictionary of name (string), weight (int) pairs
    limit - weight limit of the spaceship (an int)
    
    Returns:
    A list of lists, with each inner list containing the names of cows
    transported on a particular trip and the overall list containing all the
    trips
    """
    # TODO: Your code here
    #pass
    cows_name = list(cows.keys())
    valid_allocation = []
    count = 0
    for allocation in get_partitions(cows_name):
        count += 1
        valid = True
        for trip in allocation:
            weighs_per_trip = 0
            for cow in trip:
                weighs_per_trip += cows[cow]
            if weighs_per_trip > limit:
                valid = False
                break
        if valid == True:
            valid_allocation.append(allocation)
    print('the number of allocations is', count)    
    return min(valid_allocation, key=len)

动态规划

def dp_make_weight(egg_weights, target_weight, memo = {}):
    """
    Find number of eggs to bring back, using the smallest number of eggs. Assumes there is
    an infinite supply of eggs of each weight, and there is always a egg of value 1.
    
    Parameters:
    egg_weights - tuple of integers, available egg weights sorted from smallest to largest value (1 = d1 < d2 < ... < dk)
    target_weight - int, amount of weight we want to find eggs to fit
    memo - dictionary, OPTIONAL parameter for memoization (you may not need to use this parameter depending on your implementation)
    
    Returns: int, smallest number of eggs needed to make target weight
    """
    # TODO: Your code here
#    avail_weight = target_weight
#    egg_list = []
#    length = len(egg_weights)
#    for i in range(length-1,-1,-1):
#        while avail_weight >= egg_weights[i]:
#            egg_list.append(egg_weights[i])
#            avail_weight -= egg_weights[i]
#    return len(egg_list)
       
            
    #pass
    if target_weight <0:
        return -1
    elif target_weight == 0:
        return 0
    elif target_weight in egg_weights:
        memo[target_weight-1] = 1
        return 1
    
    if target_weight-1 in memo:
        return memo[target_weight-1]

    for egg_weight in egg_weights:
        egg_numbers = dp_make_weight(egg_weights, target_weight-egg_weight, memo)
        ## if the return is valid
        if egg_numbers >0:
        ## if corresponding value not in memo is not available, set it
            if target_weight-1 not in memo:    
                memo[target_weight-1] = egg_numbers+1
        ## if corresponding value in memo, keep the minimum value
        ## there more than one combinations happens, keep the minimum value
            elif (target_weight-1 in memo):
                memo[target_weight-1] = min(memo[target_weight-1],egg_numbers+1)
              
    return memo[target_weight-1]

猜你喜欢

转载自blog.csdn.net/s09094031/article/details/88812825