Description: In this lecture, Dr. Bell discusses string manipulation, guess-and-check algorithms, approximate solution methods, and bisection search.
Instructor: Dr. Ana Bell
The following content is provided under a Creative Commons license. Your support will help MIT OpenCourseWare continue to offer high quality educational resources for free. To make a donation or view additional materials from hundreds of MIT courses, visit MIT OpenCourseWare at ocw.mit.edu.
ANA BELL: All right everyone, let's get started. So good afternoon. So this is the 3rd lecture of 6.0001 and 600. As always, please download slides and code to follow along.
So a quick recap of what we did last time. Last time, we talked about strings as a new object type, as sequences of characters. And then we introduced two new concepts that allowed us to write slightly more complicated programs.
So we introduced branching, with these keywords, if, elif, else. And branching allowed us to write programs that, us, as programmers, could introduce decisions into our programs. And then we introduced two different kinds of loops, while loops and for loops. And those also added a little bit of complexity to our programs.
Today, we're going to talk a little bit more about strings. So we're going to see a couple of more operations that you can do on strings and string objects. And then we're going to talk about three different algorithms, a guess and check algorithm, an approximate solution algorithm, and a bisection method algorithm.
So let's dive right in. We'll talk a little bit about strings, first. So strings, we thought of them as sequences of characters, case sensitive, as we saw in programs we wrote last lecture. And strings are objects. And we can do all of these operations on string objects, like test if they're equal, less than, greater than, and so on.
It turns out, we can do more than just concatenate two strings together or do these little tests on them. So we're going to start introducing the idea of a function or a procedure. And we're going to see more about functions and how you can write your own functions next lecture.
But for today, you can think of a function as sort of a procedure that does something for you. Someone already wrote this. So the first one we're going to look at is a pretty popular function.
And when applied on a string, this function, called len, will tell you the length of a string. So that's going to tell you how many characters are in the string. And characters are going to be letters, digits, special characters, spaces, and so on. So it's just going to count how many characters are in a string.
So if I have the string s is equal to "abc"-- remember a string is in quotation marks-- then, if I do this, if I write this expression, len s, here, since it's an expression, it has a value. So it evaluates to a certain value. And by definition, it's going to tell me what the length of the string, which is 3 characters long.
Another thing that we can do on strings is, since they're a sequence of characters, I might want to get what character is at a certain position. So we do this using this fancy word called indexing. But pretty much what indexing into a string means is you're going to tell Python, I want to know the character, at this certain position or at this certain index, inside my sting.
So once again, let's use this string, s is equal to "abc." And let's index into it. So in computer science, we start from 0, counting by convention. Notice, we had a problem set 0 in this class. Python is no different.
So in Python, you start indexing at position 0. Or you start indexing at 0. So the first character, in your string, we say is at position 0 or at index 0. The next character in the string is at index 1. And the next character in the string is at index 2.
In Python, it turns out, you can also use negative numbers to index. And if you index into the string with negative 1, for example, that means that you want the last character in the string. So the last character in your string is always going to be at position negative 1, the second-to-last character is at negative 2, third-to-last character is at negative 3, and so on and so on.
So the way you index into a string is with these square brackets, here. And this is the notation. So if I want the character at position 0 or at index 0, I say s, which is the string I want to index into. And then, inside the square brackets, I say what index I want.
So s at index 0 is going to be the value "a." s at index 1 is going to be the value "b," and so on and so on. And we can also do negative indexing, as well.
I added this in here. If you do try to index into a string beyond the limits of the string-- and we can even try this out, just to show you that it's not the end of the world if we do that. If we have s is equal to "abc," and we have s at position 20, for example, obviously, my string is only length 3, so what's at position 20?
I get an error. I call this angry text, here, in Python. But really, the most relevant thing to note is these last couple of lines here. This tells you what line is problematic. So s at position 20 has an issue. And this last line here tells me what actual error I have. So it's an index error, which means I'm trying to index too far into the string, because it only has three characters.
So it's nice to be able to get a single character out of my string. But sometimes, I might want to get a substring. So I want to start at the first character and go halfway into the string, or I want to take a few characters in between, or I want to skip every other letter or something like that in my string.
So if I want to do this slightly more complicated interaction with strings, we call that slicing, slicing into a string. And this notation here should seem a little bit familiar, because we saw it last lecture when we did it with range.
We had a start, stop, and a step. The notation was a little bit different, because, in range, we had open-close parentheses and commas in between. But except for that, this sort of works the same.
The start is the index, starting from 0, from where you want to slice into this string. The stop is the stop index. So you're going to go up until stop minus 1 and take that index. And then the step is how many letters you wish to take.
So this is the full notation here. But sometimes, you can not give it a third sort of number in here. So if you only give it two numbers, then, to Python, that represents just the start and the stop. And by default, step is going to be 1.
And there's a lot of other things you can do with strings. You can omit numbers and just leave colons in Python. By definition, the way that whoever wrote slicing had decided, if you omit numbers, then it's going to be equivalent to these things here.
So we slice using square brackets, just like indexing. Except now, we can give it two numbers. So with this string, s, if we slice into the string s, we start from index 3 and go up until index 6. So if we have abcdefgh, this is position 0, 1, 2, 3, 4, 5, 6, 7.
And you just count. So s, starting from 3 and going till 6, is going to start here, 3. So it's going to come up with-- sorry d. And then we're going to take e. And then we're going to take f. And since we're going until stop minus 1, we're not going to take g. Because this is position 6, and we're going until 6 minus 1.
The next one here, 3, 6, 2 is going every other one. So we start at 3, and then we skip every other one, so we go d but not e, and then f, and then stop.
If you do s and then nothing inside except colons, notice that you're going to have s, and then nothing, and then colon, nothing, colon, nothing. So nothing for start, nothing for stop, nothing for step. And that's just going to value it to the string, itself. It's the same as 0 to the length s going every step.
This one might actually be useful. It reverses the string automatically for you. So with this one little line here, you can get the inverse of your string. And that's equivalent to that. So the minus 1 represents starting from the end and going back every letter. And then this one's a little bit more complicated but also not too bad.
So as we're doing these string slices, again, if you're unsure what something does, just type it into Spider. And you might be surprised. You might not be. But it's a good way to check yourself, to make sure you're understanding what's happening.
One thing I want to mention, and it's good to keep this in the back of your mind. We're going to come back to this as we start talking about slightly more complicated object types. But strings are immutable. So just keep this word in the back of your mind as we go through this class.
And what I mean by this is that an actual string object, once it's created, cannot be modified. This might not mean anything right now. But let me just draw a little something. Let's say I have this string, s is equal to hello.
Remember, in the first lecture, we drew a diagram sort of like this. This is my memory. I have this object "hello." And this object, "hello" is bound to this variable s. So now I can access the object "hello" using this variable s.
Now you might think, well, since I could index into a string, I might be able to just say something like, s at position 0 is equal to y. And that will just change the little h into a y, and I'll have a new object.
Well strings are immutable, which means, in Python, you're not actually allowed to do this. And it gives you an error if you do try to do that. If you want the variable s to point to the string, Y-E-L-L-O, you could just say s is equal to Y-E-L-L-O.
Or you could do string operations like this. And this takes the y and it concatenates it to the string s, all of the elements starting from position 1, which is e, l, l, o. So this makes Y-E-L-L-O.
Now internally, what happens when I write this line is Python says, OK, I'm going to break my bond with this original object "hello." I'm going to bind my string variable s to the new object "yello." and this other, old object still is in memory somewhere. But it's an entirely different object that I've created here.
Again, it might not mean anything right now, but just keep this in the back of your mind, strings are immutable.
So the next thing I want to talk about is a little bit of recap on for loops. And we're going to see how we can apply for loops, very easily, to write very nice, readable code when dealing with strings.
So remember that for loops had a loop variable. My loop variable being this var, here, in this particular case. It can be anything you want. And this variable, in this particular case, iterates over this sequence of numbers, 0, 1, 2, 3, 4.
So the very first time through the loop, var has a value of 0. It does the expressions in the loop. As soon as they're done, var takes the value 1. It does all the expressions in the loop. And then var takes the value 2, and it does that all the way up until 0, 1, 2. And the last time it goes around is with var is equal to 3.
And remember, we said that we can customize our range in order to start from a custom value to end at a different value and to skip certain numbers.
So, so far, we've only been using for loops over a sequence of numbers. But actually, for loops are a lot more powerful than that. You can use them to iterate over any sequence of values not just numbers but also strings.
So here are two pieces of code, this one and this one here. These two pieces of code both do the exact same thing. To me, possibly to you, this one looks a lot more readable than this one, just at a first glance.
If I were to read this one, just using the keywords and variables here, it would sound like broken English. But you could decipher what I'm trying to say. For a char in a string s, if the char is equal to "i" or a char is equal to "u," print "There is an i or a u."
It sounds weird, but you could probably tell what I was trying to do here. Whereas up here, it's a little more complicated to tell what I'm doing. You have to sort of think about it a little bit.
For some index in this range of numbers, 0 through the length of the string s, if s, at position index, is an "i" or s at position index is a "u" print, "There is an i or a u." Both of these codes just go through the string s. And if it encounters a letter that's an i or a u, it's just going to print out this string here.
But this bottom one is a lot more pythonic. It's an actual word created by the Python community. And it just looks pretty, right? You can tell what this code's supposed to do. Whereas this one is a little bit harder to decipher.
So that's sort of an illustration of a for loop over a sequence of characters. So char is going to be a loop variable, still. And the loop variable, instead of iterating over a set of numbers, it's going to iterate over every character in s, directly. And char is going to be a character. It's going to be a letter.
So here's a more complicated example. I wrote this code a couple of years ago. And it was my attempt at creating robot cheerleaders , because I needed some motivation. And then I googled, last night, "robot cheerleaders," and was not disappointed. Created this GIF. It looks pretty cool. And it looks like they kind of stole my idea. But that's fine.
So let's look at what this code's supposed to do. I'm going to run it. I'm going to run it, and then we'll go through it. All right, it prints out, "I will cheer for you! Enter a word."
You know what, I like robots, so I'll put in "ROBOTS." How enthusiastic am I about robots? Let's say 6. So what this is going to print is-- it's a cheerleader, right? "Give me an r, r." "Give me an o, o." "Give me a b, b," and so on and so on.
"What does that spell? ROBOTS." And it's going to print it 6 times, because I'm 6 out of 10 enthusiastic about robots. So that's pretty much what that code's supposed to do. And you can write it using what we've learned so far.
Now let's go through it a little bit. And I'm going to show you just how easy it is to convert this code using a for loop over characters. Right now, what it does is it asks the user for input, so a word and a number.
And then it does this thing, here, right? First, it uses a while loop. And second, it uses indexing. And what tips you off that it's using indexing is it's using the square bracket, here, into the word.
And obviously, it's using a while loop. And it has to first create a counter, initialize it. And then, down here, it's going to increment it inside the while loop. If you remember, that's sort of what we need to do for while loops.
So it's going to start at 0, and it's just basically going to go through index i is equal to 0, 1, 2, 3 4, which is going to go all the way to the end of the word, whatever the user typed in, in this case "ROBOTS." It's going to get the character at that position. word at position i is going to be a character.
This line here is just for the cheerleading to make sense. It's just to take care of letters that make sense to use an, right? So give me a b, give me an b. So give me an b does not make sense, right? So that's just taking care of that.
And I'm using this in keyword to check whether the character-- so the character, r, for example, in robots-- is inside an letters. And an letters I've defined up here, which is these are all the letters that make sense to put an an before the letter. So give me an r for example, here, on the right.
And so if it makes sense to use an before the letter, use that, and otherwise use just an a. And after I'm done, I say, "What does that spell?" And then it's just a for loop that goes times many times and prints out the word and the exclamation mark.
So this code might have been a little bit more intuitive if I rewrote it or if I'd originally written it with a for loop. So this part here, the while loop and indexing and creating my original counter, we can get rid of that.
And we can replace it with this, for char in word. I'm originally using char, so I can use char as my loop variable again. And simply, I'm just going to iterate over the word, itself.
So now, instead of having this mess here, I have a one-liner that says, for every character in my word, do all this stuff here. So that remains the same. And then I don't even need to increment a counter variable, because I'm not using while loops anymore. I'm just using a for loop.
So the code becomes-- delete that-- for char in word. And then delete that. And that does the exact same thing. And it's a lot more readable.
So this was our toolbox at the beginning of this course. We are two and half, I guess, lectures in. These are the things we've added to it. We know integer, floats, Booleans. We know a bit of string manipulation, math operations. We added, recently, these conditionals and branching to write slightly more interesting programs.
And now we have loops, for and while loops to add interesting and more complicated programs. So with these, the second part of this lecture is going to be looking at three different algorithms. That's the sort of computer science part of this class, Introduction to Computer Science and Programming using Python.
Don't let the word algorithm scare you. They're not that complicated. You just have to sort of think a little bit about them. And you'll be able to get them.
So we're going to look at three algorithms, all in the context of solving one problem, which is finding the cube root. The first algorithm is guess and check, then we're going to look at an approximation algorithm, and then a bisection search.
So the first is the guess and check method. You might have done this, in math, in high school. The guess and check method is also sometimes called exhaustive enumeration. And you'll see why.
So given a problem, let's say, find the cube root of a number, let's say you can guess a starting value for a solution. The guess and check method works if you're able to check if your solution is correct.
So if your guess is originally 0, you can say, is 0 cubed equal to the cube of whatever I'm trying to find the cube root of? So if I'm trying to find the cube root of 8, is 0 cubed equal to 8? No. So the solution is not correct.
If it's not correct, guess another value. Do it systematically until you find a solution or you've guessed all the possible values, you've exhausted all of your search space.
So here's a very simple guess and check code that finds the cube root of a number. So I'm trying to find the cube root of 8. So my cube is 8. I'm going to have a for loop that says, I'm going to start from 0. And I'm going to go all the way up to--
So I'm going to start from 0 and go all the way up to 8. For every one of these numbers, I'm going to say, is my guess to the power of 3 equal to the cube 8? And if it is, I'm going to print out this message.
Pretty simple, however, this code is not very user friendly, right? If the user wants to find the cube root of 9, they're not going to get any output, because we never print anything in the case of the guess not being a perfect cube. or the cube not being a perfect cube.
So we can modify the code a little bit to add two extra features. The first is we're going to be able to deal with negative cubes, which is kind of cool.
And the second is we're going to tell the user, if the cube is not a perfect cube, hey, this cube is not a perfect cube. So we're not going to silently just fail, because then the user has some sort of feedback on their input.
So let's step through this code. We have, first of all, a for loop just like before. And we're going to go through 0 to 8 in this case. We're using the absolute value, because we might want to find the cube root of negative numbers.
First thing we're doing is doing this check here. Instead of guessing whether the guess to the power of 3 is equal to the cube, we're going to check if it's greater or equal to, and we're going to do that for the following reason.
So if we're trying to find the cube root of 8, for example, versus a cube root of 9-- this is 8 and this is 9-- what is this code going to do? It's going to first guess 0. 0 cubed is not greater or equal to 8. 1 cubed is not greater or equal to 8.
2 cubed is greater or equal to 8, so here, once we've guessed 2, we're going to break. Because we found a number that works. And there's no need to keep looking. Once we've found the cubed root of this number 8, there's no need to keep searching the remainder, 3, 4, 5, 6, 7, 8.
Sort of the same idea when we're trying to find the cube root of 9. We're going to start with 0. 0 to the power of 3 is less than 9. 1 to the power of 3 is less 9. 2 to the power of 3 is less than 9.
When we get to 3 to the power of 3, that's going to be greater than 9. So this code tells us, once we've picked a number that's beyond the reasonable number of our cubed root, of our cube, the cubed root of our cube, then we should stop.
Because, again, it doesn't make sense to keep searching. Because if 3 to the power of 3 is already greater than 9, 4 to the power of 3 is also going to be greater than 9 and so on. So once we break here, we either have guess being 2 or guess being 3 depending on what cube we're trying to find.
And if the guess to the power or 3 is not equal to the cube, then, obviously, the cube was not a perfect cube. So that's this case here, if we were looking at at the cube root of 9. And otherwise, this part here just looks at whether we should make it a positive or a negative cube. So if our original cube was less than 0, then, obviously, the cube root of a negative number is going to be a negative number, and, otherwise, it's just our guess.
So that's the guess and check method, slightly more feature-rich program for guessing the cube root. But that only tells us the cube root of perfect cubes and doesn't really give us anything else, any more information.
So sometimes, you might want to say, well, I don't care that 9 is not a perfect cube, just give me a close enough answer. So that's where approximate solutions come in. So this is where we're OK with having a good enough solution.
So in order to do that, we're going to start with a guess and then increment that guess by some small value. Start from 0 and start incrementing by 0.001 and just go upwards from there. And at some point, you might find a good enough solution.
In this program, we're going to keep guessing as long as we're not close enough. And close enough is going to be given by this epsilon value in the program. So as long as the guess cubed minus the cube-- so how far away are we from the actual answer-- is greater than some epsilon, keep guessing, because the solution is not good enough.
But once this is less than epsilon, then we've reached a good enough solution. So two things to note with approximate solutions. So you can get more accurate answers if your step size is really, really small. If you're incrementing by 0.0001, you're going to get a really good approximate solution, but your program will be a lot slower.
Same sort of idea with epsilon, you can change epsilon. If you change epsilon to be a bigger epsilon, you're sacrificing accuracy, but you're going to reach a solution a lot faster.
So here's the code for the approximate solution of a cube root. It might look intimidating, but, look, almost half this code is just initializing variables. So we're initializing, this is the cube we want to find the cube root of. We pick an epsilon of this. We start with a guess of 0. We start with an increment of 0.0001. And just for fun, let's keep track of the number of guesses that it takes us to get to the answer.
This is similar to the guess and check from before. It's not similar. Well this part is similar to the guess and check from before. So we're going to take the guess to the power of 3 minus the cube, right? So that's how far away are we from the actual answer?
And we're going to say, if that's not good enough-- so if we're still greater than or equal to the epsilon-- then keep guessing. So we're going to be stuck in this loop, where we keep guessing values, until we've reached a guess that's good enough, so until we're less than epsilon.
And way we keep guessing is just with this line, right here, which says, increment my guess by increment, and increment being this really small value. That make sense?
So I'm going to keep incrementing my guess by that small value. Before I go on, I'm going to run the code. And we're going to discover a small issue with it.
So with 27, we're going to run it. Perfect, it took me 300 guesses. But 2.99999 is close to the cube root of 27. We can find the cube root of this guy here. And it took me 20,000 guesses, but I figured out that 200.99999, so 201, is close to the cube root of that large number.
I should have done this. This is going to be a giveaway, you guys. Sorry. Then we're going to have-- let's say I want to try cube of 10,000. So 10,000 is not a perfect cube. So we can run the code. And with 8,120,601 I had already gotten an answer. But with 10,000, I'm not getting an answer yet, right?
So I'm thinking that there might be something wrong. So I'm going to stop my code. So I just hit Control C, because I feel like I've entered an infinite loop. And, in fact, I have. So what ended up happening is this problem here.
So I'm going to draw something. According to the code, I'm going to start from 0, and I'm going to increment my guesses, like that. With every little increment, I'm going to make a new guess. I'm going to take that guess to the power of 3. I'm going to subtract the cube, and I'm going to figure out if I'm less than epsilon.
This is the epsilon that I want to be in, this little bit here. So with every new guess, I might be, maybe-- so this is where I want to be, within this little boundary here. With every new guess, I might be here.
With the next guess, over here, I might be here. When I make another guess, I might be here. So I'm getting close to being within epsilon. But maybe with my next guess, I'm going to hop over my epsilon and have made too big of a guess.
So just because of the way the numbers were chosen in this example, just to illustrate this, using an increment of 0.01, a with finding the cube of 10,000 and epsilon of 0.1, it turns out that, as I'm doing all these calculations, I'm going to skip over this perfect sort of epsilon difference.
So first, I'm going to be too small. And then I'm going to be too large. And once I've become too large or too far away from epsilon, the guesses I continue to make are just going to be even farther away from epsilon. And I'm not going to get to my answer.
And that's why I've reached an infinite loop in this code. All I'm doing in this code is checking whether my guess cube minus cube is less than epsilon. The only thing I need to do here is sort of add this little clause, here, that says, oh, by the way, also check that I'm less than cube.
Because this is just like we did in the very first program, when I'm checking 0, 1, 2, 3, 4, 5, 6, 7, 8, when I'm trying to find the cube root of 8. Once I've reached 8, I'm going to stop. And it's the same thing here.
So I just added this little clause that says, well, while I'm greater than or equal to epsilon and I'm still less than the actual cube, just keep searching. But once I've reached the cube, then stop searching.
And with 10,000, you can see that I failed to actually find-- so that's what this part, here, does. It tells me I've failed to find the cube root with those particular parameters.
The last thing we're going to look at is bisection search. And to illustrate this, I'm going to need one volunteer. And you're going to play a game with me in front of the whole class. And there will be a prize. There go the hands. In the blue shirt, right there. Cool.
So the prize is going to be, once again, this. I promise I don't have millions of these, Google glasses. I also don't work for Google. I just happened to get a couple.
So the game is this. I'm going to ask you to pick a number, a whole number, between 0 and 100. And I'm going to try to guess it. And you need to make it hard for me. And you need to make it so hard for me that I cannot guess it within 10 guesses.
And if you can do that, if I cannot guess it within 10 guesses, you get this. And I'm going to draw out what I do as we go along. So do you have your number? Yes?
ANA BELL: Perfect. Let me erase that. Actually, I should've probably kept that, because I'll still use it. There's the numbers, 0 to 100. Is your number 50?
ANA BELL: 50 Was my guess. So I've made one guess. Is your number higher or lower than 50?
ANA BELL: Higher. Is your number-- my next guess is going to be 75. And the reason I'm guessing 75 is because-- what's your name?
ANA BELL: What's that?
ANA BELL: Sophie. Sophie said, 50 was too low. So I immediately know that it cannot be any less than 50. So I've already eliminated half of the numbers. So my next guess is 75. Is your number 75? Is your number lower or higher?
ANA BELL: Since it's higher, I'm eliminating this half here. Is your number-- so between 75 and 100. Oh, boy, you're putting me on the spot. What's that?
ANA BELL: 87, thank you. 87?
ANA BELL: Higher or lower?
ANA BELL: Lower. So since it's lower, I'm eliminating that half. Is your number 81? Higher or lower?
ANA BELL: So she said, lower, so I'm eliminating that half. Is your number 78? Oh, boy, that's really hard. 78, OK. Higher or lower?
ANA BELL: Is your number 76?
ANA BELL: Yay. All right, perfect, 76 was the number. So how many guesses have I made? One, two, three, four, five, six-- I made six guesses. So I did get it under 10. But you know what? The game was rigged. So you get the prize anyway, just because I rigged the game. Here you go. Pass it down.
AUDIENCE: Thank you.
ANA BELL: Thank you. So notice, in bisection search, what I did was I eliminated half the search space with every guess. I said, well, she said it's higher or lower, so I definitely cannot be in the other search space, right? If I was doing approximate solution or, in this case, guess and check, I would be asking Sophie, is your number 0, 1, 2, 3, 4, and so on?
So with guess and check, it would have taken me 76 guesses to get to the number, whereas, with this bisection search, that I just did, it only took me 6. Isn't that cool?
So that means that the larger the space actually is, that I need to search, the better it is to use bisection search, this bisection search method. So that's basically what I'm illustrating here. So we have our original search space. We're going to choose a guess halfway, eliminate half of the guesses. Then we're going look in the remaining interval, eliminate half the guesses, and so on and so on.
So then this is the code for bisection search. Also looks intimidating, but it's not so bad. So we're initializing a bunch of stuff up here. The most important couple of things we're initializing are, first of all, this high and this low boundaries.
So with the guessing game, the low boundary was 0, and the high boundary was 100. When we're looking at the cube root, the low boundary is going to be 0, and the high boundary is going to be just my cube, because a guess to the power of 3 cannot be any greater than cube.
And then, I'm just going to do the same procedure that I did with the guessing game, which is I'm going to make my guess, be halfway in between. So with this guessing game, I had to sort of choose, if there were four numbers in between, should I go higher or lower?
Well, when we're doing by bisection search, here, we don't care about that. We're just going to do floating point division, because we want decimal numbers. So I have a low boundary and a high boundary. And I figured out my halfway point.
Then I have this while loop here. A while loop is similar to the approximation method, where, as long as I don't have a guest that's good enough-- so this, depicted by this greater or equal to epsilon-- as long as my guess is not good enough, I'm going to keep guessing. That's what this while loop is saying.
So if the guess to the power of 3 minus cube is not good enough, keep guessing. And the way I keep guessing is this part, here, says, my guess was too low. So if my guess was too low, set the low boundary to be the guess. Because I don't care about all of the other numbers that are much lower than me.
So set the low to be the guess. That's what that line is doing. And otherwise, my guess was too high. That's what this else is doing. So set the high to be the guess, because I don't care about numbers any higher than my guess.
Once I have these new boundaries, I make another guess, again, halfway in between the new boundary points. So essentially, I'm just halving my interval with every single guess. And that's what the while loop is doing. And then I print out the remaining part.
So notice the search space originally being N, we're halving it with each guess. So the first guess divides it by 2, the second guess divides it by 4, and so on. So by the time we get to the k-th guess, N/2k, the k-th guess, let's say that's the actual answer we're interested in. There's only one value in that little interval. And that's the answer we want.
So 2 to the k is equal to N. And then how many guesses did we make? k is equal to log base 2 of N. So when we are playing the guessing game of 100, my end was 100. Log base 2 of 100 is 6.-something, I think.
So in fact, I could have said, if I don't guess it within seven guesses, you would have won as well. So that's why the game was rigged. So the guess, notice, it converges on the order of log base N instead of just linearly in terms of N. So that's why it's so powerful.
One last thing I want to mention is the code I showed only works for positive cubes. And that's because of the following. So I have this 0 and 1. Let's say I'm trying to find the cube root of 0.5.
When I first set my initial boundaries, my low is this one, and my high is this one. But what's the cube root of 0.5? Is it within this boundary or is it outside this boundary?
AUDIENCE: Outside the boundary.
ANA BELL: I heard, outside. It's like 0.7 something. So it's out here. So with this particular code, I'm going to be halving my interval in between those numbers, but I'll never get to an answer. Because the actual cube root of 0.5, or numbers less than 1, is going to be outside that boundary.
So there's a small change you can make to the program, which will fix that. And that's in the code. I didn't put it in, but it's a very small change, a small if statement. So that's it. All right, thank you.