D o C P 学习笔记(4 - 2)Dealing with complexity through search - Problem Set 4

备注1:每个视频的英文字幕,都翻译成中文,太消耗时间了,为了加快学习进度,我将暂停这个工作,仅对英文字幕做少量注释。
备注2:将.flv视频文件与Subtitles文件夹中的.srt字幕文件放到同1个文件夹中,然后在迅雷看看中打开播放,即可自动加载字幕。

你可以学到什么:

  • 搜索:利用手电筒或船,找到你的方法。
  • 分析算法的效率。
  • 递归关系,匹配数据类型与算法。

Problem Set 4

视频链接:
Problem Set 4 - Udacity

Course Syllabus

Lesson 4: Dealing with Complexity Through Search

1. 练习:Refactoring Bsuccessors

This homework is about refactoring the bridge successor function. I want you to write a new function. We’ll call it bsuccessors3, but we’re going to represent the states in a different way. We’re going to represent them not as a set that included the light, because in the old bridge successor function–bridge successor 2–it just seemed kind of complicated that I had to deal with the special case of are the elements of the sets on the here and there side–are they people or are they the light. It just got a little bit overly verbose(冗长的,累赘的).

I think maybe there’s a better representation, so we’re going to try a different representation. We’re going to try one where there is a set of people on the here side, a set of people on the there side, and then the light is going to be an index. It’s going to be 0 if the flash light, the torch, is on the here side and 1 if it’s on the there side. Then the tuple for the actions is going to be a little bit different. It’s just going to be a set of people–the travelers that are going–then followed by the arrow.

1. 练习:Refactoring Bsuccessors Solution

Here is my solution. I think it is a little bit simpler. The total number of lines of code is not that much different, but I think it’s easier to follow this way.

def bsuccessors3(state):
    _, _, light = state
    return dict(bsuccessor3(state, set([a, b]))
                for a in state[light]
                for b in state[light])  

What I said is let’s pick out the light from the state, and let’s take all the travelers a and b from within the state indexed by that light with 0 being here and 1 being there. Let’s find the individual successor states that results from those two travelers, or they might be one and the same–a and b traveling from that state.

def bsuccessor3(state, travelers):
    _, _, light = state
    start = state[light] - travelers
    dest = state[1-light] | travelers
    if light == 0:
        return (start, dest, 1), (travelers, '->')
    else:
        return (dest, start, 0), (travelers, '<-')

I broke out this single successor function. That just says we start where the light is, and we go to where the light isn’t. If the light was on side 0, then we go from the start to the destination, and the light now is on side 1, and we’re going in that direction. Otherwise, we’re going in the other direction.

2. 练习:More Pour Problem

This homework I call “More Pour.” We did the glass-pouring example where we had two glasses, and we could pour between them. We’re going to do the same thing here, except we’re going to allow an arbitrary(随意的) number of glasses.

For example, here I’ve drawn four of them. Let’s say they’re capacities are 1, 2, 4, and 8, and let’s say we set ourselves the goal of having 5 in one of the glass. How could we achieve that? One solution is we could fill up this glass with 8. That would be the first action. Then we could empty out or pour 1 into here. Now we’d be down 1 and be at that level. Then the second action would be to pour into here. Now we’d be down 2 more, and we find we’re at 5 here. That would solve the problem. An alternative solution would be to fill these two glasses with 4 and 1 and then pour both of them into 8 to achieve 5.

Your task will be to define more pour_problem, which takes a tuple of capacities of glass–any number of glasses–and then a goal, which is a single integer that you’re trying to achieve. Alternately, you can have a start state of what is the current level of the glasses are. If the start state is None, that means zero for all of the glasses. Your task is to return a path which achieves the goal or return the empty path or failure if there is none. The paths, like before, are an alternation between states and actions where the state is just a tuple of current level, not of capacities but of current levels. Then the actions can be either fill, empty, or pour, and we’re going to describe the glasses by their index numbers. In this list of capacities the first number in the list–we’ll call that glass number 0, the next one glass number 1, and so on. When we filled up the tallest glass that was the last glass out of four–glass number 3–so the action would have been fill 3.

Here we have a test that says the problem I just solved for you by drawing it out. The actions are fill glass number 3. Pour glass number 3 into glass number number 0. Then pour glass number 3 into glass number 1, and then you’re left with 5 in glass number 3. Here are some more test cases to check things out for you, see if you can solve those by writing your program.

Use what we have seen before. You’ll probably want to use one of the search procedures we’ve already defined, and you’ll probably need to write a custom successor function, which understands the current state and the capacities.

Let me just say I’ve seen some discussion in the forums about, oh, can I find a one-line solution or two-line solution ? Don’t worry about that. The solution will be whatever it is. The set up to solve the problem should be a call to a function we’ve already defined. That will be one line, but the successor function–that might be a dozen lines, maybe a little bit more or a little bit less. Don’t worry about minimizing it. Worry about making it clear.

2. 练习:More Pour Problem Solution

Here’s my solution. I got rid of the doc.string just so that the whole function would fit on the page. In general, I should bring that back. The doc.string is an important part of the solution.

Here is what I do. The heart of the function i just a call to shortest_path_search. Start at the start state. We have successor function and a goal function.

That goal function just asks is the goal number, that integer that we’re trying to achieve, anywhere in the state. Is any one of the current levels of the glasses equal to the goal ?

Here is a successor function, and I’m going to build up this dictionary of results, which I call “succ” for “successor. It’s a dictionary of state-action pairs. I go through all the indices. That’s equivalent to the glass numbers–0, 1, 2, 3. I’ve invented this function called “replace,” and replace takes a tuple, an index into the tuple, and a value that we want that index to be. Now, why did I need that? Because tuples are immutable. Mostly this is saying replace state sub i with capacities of I, but I can’t say that for a tuple, so I needed a function that’s going to do it for me. Take that tuple with state sub i, replace by the capacity. That’s the same as the action of filling an i. Replace it with 0. That’s the action of emptying. Now, the action of pouring, which we have over here, is a little bit more complicated. We have to go through all the glasses twice, pouring from i into j. We have to make sure that we’re not pouring into yourself. Then the amount that we pour is the minimum of how much we have. We can’t pour more than we have in the glass that we’re pouring. We also can’t pour more than would fill up the glass that we’re pouring into. That’s the amount we’re going to transfer, and then we subtract that amount from the glass we’re pouring from, and we add that amount from the glass we’re pouring into, and we make that a pouring action. Then we just return the result.

Then there’s one little statement here that says if you didn’t specify the start state, make that be the tuple that contains all zeros and whose length is the same as the number of glass we have.

def replace(sequence, i, val):
    s = list(sequence)
    s[i] = val
    return type(sequence)(s)

So that’s the whole solution, except here is my replace function. Replace in general takes any sequence–but it’s really most applicable for tuples that are immutable, makes that sequences into a list, mutates it–because we are allowed to mutate lists, and then puts it back together again. So what am I doing here? This might look a little bit unusual. But what I’m doing is I’m saying tell me the type of the original sequence–a tuple, could be a string, could be a list– and then call that because types are functions. In this case it’s going to be tuple. Then call tuple on this list s and make s back into a tuple. I made it general like this so that it would also apply to lists and string.

Finally, here are the test cases, and if we run them it turns out it works.

3. 练习:Subway Planning

This next exercise involves planning routes on a subway system. This is a map of the subway system for my hometown of Boston. We’re going to have a representation of this map, and then we’re going to be able to ask questions of what’s the shortest path to get from A to B.

Now I’m going to show you what I decided as the format for how I represent the subway line and the functions I want you to write. I’m going represent it as–we call this function “subway,” which builds up a data structures and assigns that–we’re going to assign it to the variable “boston.” And the arguments to this function are a bunch of keyword argument. Blue is the list of stations separated by spaces–just regular words separated by spaces. The orange line, red line, and green line are similar.

Now, here is the signature for this function subway. It takes this double-asterisk parameter name notation. If you haven’t seen that before, what that means is that these are the keyword arguments. Two asterisks means everything that’s left is going to be a keyword argument. I’m not going to tell you what they are. They can be anything. Collect them into this parameter called lines and make that be a dictionary–a dictionary where in this case the keys of the dictionary would be the strings blue, orange, green, and red, and the values of the dictionary would be these corresponding strings. That’s what lines is. Then you do whatever you want to lines and return the result. If you want, you just return the lines dictionary as-is. That would be fine. But if you did that you’d probably need to do more complicated processing on the values later one, so you might want to do some of the processing up front and return some other type of data structure for subway, because you’re going to need it down here.

Down here we’re going to write the ride function that says how do we get from here to there. Here and there are going to be stops. For example, how do we get from Oak Grove to Downtown Station or stop? Those are just strings that represent the stops. The system, which in this case defaults to Boston–the subway system that we previously defined. You write your code there. Presumably, you’re going to have some sort of call to a search function. You’re going to need to define a successor function that’ll be appropriate. So you need to decide what is the representation you’ll come up with for subway that will be useful to the successor function that you’re going to use here. Then just for fun, let’s also write the function longest_ride, which takes as input a subway system such as Boston and tells you what’s the longest ride you can get. From all the pairs of stations going from one to the other, what’s the longest one? And I helped you out a little bit here by writing just a small number of test cases. You can try that out. You can also write more test cases on your own.

3. 练习:Subway Planning Solution

Here is my solution. For subway I decided I’m going to return something that looks just like the successor function, only it’s going to have all possible successors. It’s going to be the dictionary of this form for any station that you ask me for. Stations are states in this problem. All you need to represent a state in this problem is what station am I currently at. Each of those I’m going to give you a dictionary that looks like a successor function. That is it has a combination of state-action pairs. The state is just going to be the neighboring station. Then the action is the line that you take. For example, if I’m at Bowdoin, then one of the states I can arrive at is government. The action I take to get there is follow the blue line. How do I do that? Well, one thing is I import the collections module. I use collections.defaultdict. I’m saying successors is a dictionary, and by default if there’s a value that isn’t there, the default is going to be a dictionary. Here’s what successors look like, and the default value–if I ask for the value of a station and there’s nothing there, make it be an empty dictionary. The line items are a set of line stops pairs. I’m going to go over those–line, name, and stops. Then I’m going to look at all the overlapping pairs–Bowdoin-government, government-state, state-aquarium–that I get by splitting up the stops into a list on spaces. Then I’m saying a successor of going from a to b, from Bowdoin to government–you can do that along a linename–and similarly going from government to Bowdoin. You can do that along the linename here, blue. Then return the successors. Overlapping_pairs takes any sequence and just gives me those pairs. If you give me this sequence of this turned into a list, the overlapping_pairs are first bowdoin-government, then government and state, then state and aquarium. This is useful here, and it’s useful in general. Here’s my ride function. It’s now very easy. It’s the shortest path search. That makes sense. I don’t need lowest cost search, because there are no costs here. I’m just going from one station to the next. I start here. Where am I trying to get to? Well, the goal is to get to there. I’m trying to get to someplace where–and I just put the function in-line here rather than defining it separately. You could have done it either way. Because the functions were short, I decided to put them inline. But naming the function would also be fine. Lambda s–“s” stands for state or for station or for stop, and we ask “are we there?” Then the successor function. Well, we’ve already built up the successor function in this subway system–Boston–so we just say looking into boston, that’s a dictionary and look for the station that I’m currently at, and that will give me a list of successors. Then shortest_path_search does the rest. What does longest_ride do? First, I have to find all the stops, and that’s a little bit complicated, because I’ve hidden them away in this dictionary for the system, but I can go through with this generator expression to come up with a set of stops. Then I just iterate through all possible stations a and b, look for a in all possible stops and for b in all possible stops, generate the ride–the shortest_path_search between them, and then take the longest out of all those shortest paths, and that’s my longest ride.

参考文献:

  1. Design of Computer Programs - 英文介绍 - Udacity
  2. Design of Computer Programs - 中文介绍 - Udacity
  3. Design of Computer Programs - 中文介绍 - 果壳
  4. Design of Computer Programs - 视频列表 - Udacity
  5. Design of Computer Programs - 视频列表 - YouTube
  6. Design of Computer Programs - class wiki - Udacity
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值