140 likes | 216 Views
Introductory Example. Suppose we meet a woman and want to determine her age (+/- a year) We can’t possibly just ask her, because women tend to be coy about such things. We’re sure that 18 <= age <= 50.
E N D
Introductory Example Suppose we meet a woman and want to determine her age (+/- a year) We can’t possibly just ask her, because women tend to be coy about such things. We’re sure that 18 <= age <= 50. We pick the middle of this range ((18 + 50) / 2 = 34) and artfully work a reference to being 34 into the conversation. She blushes. We now know that 35 <= age <= 50. We pick the middle of this new range ((35 + 50) / 2 = 42)) and get in something about being 42. She frowns. We now know that 35 <= age <= 41. We pick the middle once more ((35 + 41) / 2 = 38) and try again. She looks angry (we’re too high again). We now know that 35 <= age <= 37, and have got close enough Her age is 36 +/- 1 year. P.S. If one of our picks were right on, she’d have giggled, so giving the game away.
Age Algorithm low = lowest possible age high = highest possible age for (;;) { middle = (low + high) / 2; if ((high - low) < (2 * tolerance)) { we’re done: age is middle +/- tolerance } if (mentioning middle gets a giggle) { we’re done: age is middle } else if (reaction is negative) { high = middle - 1; } else { // reaction must be positive low = middle + 1; } }
Initially: low guess high answer is somewhere in here Guess too low: low high answer is somewhere in here low high Guess too high: answer lies somewhere in here Our algorithm is an example of a “bisection search”. We start off by knowing that the answer lies somewhere in a range. We then pick the middle of this range (“bisect” it) and see whether this guess is correct (in which case we’re all done), too low, or too high. If the guess is too low, we know that the answer lies somewhere in the upper half of the range, and if it’s too high, we know that it lies somewhere in the lower half of the range. Bisection Search Principles We repeat this until we either hit the answer or low and high get close enough to each other.
Pulley Example (Introduction) distance apart = C length of belt = L pulley diameter = D2 pulley diameter = D1 Problem I: Given D1, D2, and C, what is L? Deriving the formula is not difficult, though the diagram is tricky to draw in PowerPoint… Let R1 = the radius of the larger pulley R2 = the radius of the other pulley A = belt angle = asin ((R1 - R2) / C) Then the belt length L is 2*C*cos(A) + (pi + 2*A)*R1 + (pi - 2*A)*R2 This formula can easily be made into a C++ function.
Pulley Example (Basic Function) // returns -1 if the values supplied are not reasonable. double find_belt_length(double C, double D1, double D2) { double R1, R2, angle; // make R1 the radius of the larger pulley // and R2 the radius of the smaller pulley if (D1 >= D2) { R1 = D1 / 2; R2 = D2 / 2; } else { R1 = D2 / 2; R2 = D1 / 2; } // if the pulleys overlap we've a problem if ((R1 + R2) > C ) { return -1.0; } angle = asin ((R1 - R2) / C); return (2 * C * cos(angle)) + ((M_PI + (2 * angle)) * R1) + ((M_PI - (2 * angle)) * R2); }
C = (D1 + D2)/2 C = L/2 teeny pulley teeny pulley Problem II: Given D1, D2, and L, what is C? We could try and obtain a formula by manipluating the formula for L, but this is not easy …. L = 2*C*cos(asin((R1 - R2)/C)) + (pi + 2 * asin((R1 - R2)/C))*R1 + (pi - 2 * asin((R1 - R2)/C))*R2 C = ??? an “exercise for the student” An alternative is to use a bisection search, and keep making “guesses” for C until we either hit the correct answer or get close enough for government work. Presuming reasonable input values, C must be somewhere between (D1 + D2) / 2 and L/2. Pulley Example (The Issue)
Pulley Example (Pseudo Code) low = (D1 + D2) / 2 // closest possible distance high = L / 2 // furthest possible distance for (;;) { // make a “guess” as to what the distance is trial_C = (low + high) / 2; if ((high - low) < (2 * tolerance)) { // low and high are so close that we can call it a day. we’re done: C is trial_C +/- tolerance } use the function from Problem I to compute how long the belt would be if the pulleys were trial_C apart if (computed belt length == L) { we’re done: C is trial_C } else if (computed belt length > L) { high = trial_C; } else if { // computed belt length < L low = trial_C; } }
Pulley Example (C++ Code) double find_distance (double L, double D1, double D2) { double low, high, trial_C, trial_L; const double epsilon = 1e-6; low = (D1 + D2) / 2; // closest possible distance high = L / 2; // furthest possible distance for (;;) { trial_C = (low + high) / 2; if ((high - low) < (2 * tolerance)) { return trial_C; } trial_L = find_belt_length (trial_C, D1, D2); if (trial_L == L) { // unlikely (remember fuzziness!!) return trial_C; } else if (trial_L > L) { high = trial_C; } else if { // computed belt length < L low = trial_C; } } }
Pulley Example (Refinement) What will happen is the given belt length L isn’t long enough to go around the two pulleys, even if they are touching? We can protect against this possibility by inserting a “sanity check” at the start of our function. low = (D1 + D2) / 2; high = L / 2; // make sure that inputs are reasonable if (find_belt_length (low, D1, D2) > L) { // belt isn’t long enough to go around pulleys return -1; // return special value } for (;;) { . . .
Root Finding In our problem, we were given D1, D2, and L, and wanted to find C. This boiled down to finding C such that find_belt_length (C, D1, D2) = L Or alternately, to finding C such that find_belt_length (C, D1, D2) - L = 0 Or, more generally, to finding C such that F(C) = 0, where F(C) is “find_belt_length(C,D1,D2) - L” Problems of the form “find x such that F(x) = 0” are known as “root finding” problems (and the values of x which satisty the equation are called “roots”). The bisection search is one way of solving such problems (there are others).
Generalized Bisection Search Want to find x such that F(x) = 0. low = some value lower than the answer high = some value higher than the answer for (;;) { middle = (low + high) / 2 if (high - low < 2 * tolerance for x) { // middle is very close to the correct answer return middle } value = F(middle) if (|value| < tolerance for F(x)) { // a “direct hit” (more or less) return middle } else if (value * F(low) > 0) { // value and F(low) are both negative or both positive low = middle } else { // sign of value != sign of F(low) high = middle } }
F(x) low middle high tolerance x correct x Generalized Search Notes (1) middle = (low + high) / 2 if (high - low < 2 * tolerance for x) { // middle is very close to the correct answer return middle } Stopping when low and high are close to each other guarantees that the calculated x (middle) is within the selected tolerance of the correct answer. It does not guarantee, on the other hand, that F(x) is correspondingly small.
F(x) |F(middle)| < tolerance x correct x middle Generalized Search Notes (2) value = F(middle) if (|value| < tolerance for F(x)) { // a “direct hit” (more or less) return middle } . . . In many applications, it is reasonable to stop when F(x) is close to zero. In the pulley case, for example, we might consider a distance to be good enough if the actual and calculated belt lengths are within 0.001 of each other. The calculated x (middle) may be some way from the correct value.
F(x) low middle high x Generalized Search Notes (3) The rule for whether low or high should be moved allows for both increasing and decreasing functions. F(low) and F(middle) both negative:move up low (low = middle) F(x) F(low) and F(middle) both positive: move up low (low = middle) low middle high x