-1

Imagine that a needle in the speedometer, which shows the speed, has fallen off.

It was reattached, but not at the right angle. So, although the speedometer showed the value of the current speed v, its actual value was v+k, where k is an unknown constant (probably also negative). So we started keeping honest records of the trips we made to find out the value of the mysterious constant k.

Input:

The first line of the input contains two integers: n (1 ≤ n ≤ 1000), which represents the number of parts of a single run, and t (1 ≤ t ≤ 10^6), which represents the total run time.

This is followed by n lines, where each describes one part of the trip that we recorded. Each line contains two integers: s (1 ≤ s ≤ 1000) and v (|v| ≤ 1000), the distance and speed indicated by the speedometer with the needle stuck on during that part of the journey. Keep in mind that even though the speedometer needle on the glove box may have negative readings, its actual speed was always greater than 0 during every part of the trip. The time is given in hours, the distance in kilometres and the speed in kilometres per hour.

Output:

The problem is to find K. The mysterious constant k given in kilometers per hour.

Example of Input:

3 5
4 -1
4 0
10 3

Output:

3.000000000

Input:

4 10
5 3
2 2
3 6
3 1

Output:

-0.508653377

Well, I was told that this problem can be solved with Approximate algorithm.

Can someone write a pseudocode solution or explain how exactly I can solve this problem with this algorithm?

mkrieger1
  • 19,194
  • 5
  • 54
  • 65
redflag
  • 27
  • 5
  • 2
    First of all, do you understand the problem statement? Can you solve the first example on paper? Do you understand why the solution in the first example is 3.0? – mkrieger1 Apr 02 '22 at 11:26
  • 1
    [There is no algorithm widely called the Approximate algorithm.](https://xlinux.nist.gov/dads/) There is an [approximation algorithm](https://xlinux.nist.gov/dads/HTML/approximatin.html), but that is an unrelated algorithm for a different subject area. – Eric Postpischil Apr 02 '22 at 11:35
  • The quoted text in the post poorly states the problem. The first input numeral, n, is a number of trip segments, which comprise the entire trip. During each segment, the vehicle moved at a constant speed, changing instantly between segments (which is physically impossible). The second input numeral, t, is the total trip time. This is followed by n pairs of numerals, the first of which is the distance covered during a segment, and the second is the indicated speed during that segment. – Eric Postpischil Apr 02 '22 at 11:38
  • You have to solve for k: (d1 / (s1+k)) + (d2 / (s2+k)) + ... + (dn / (sn+k)) = T. You can do this by for example binary search. – Paul Hankin Apr 02 '22 at 11:40
  • @EricPostpischil the units _are_ given in the question: "The time is given in hours, the distance in kilometres and the speed in kilometres per hour." – Paul Hankin Apr 02 '22 at 11:41
  • These form an equation, t = sum of d[i] / (s[i]+k), where the sum is taken over the segments, and d[i] and s[i] are the distance and indicated speed described above. This equation can be multiplied by the product of the s[i]+k to form a polynomial equation in k, but there are no general algebraic solutions for polynomial of high degree. Solutions can be found by numerical methods. What numerical methods for solving equations has your class studied recently? – Eric Postpischil Apr 02 '22 at 11:41
  • Bisection will work. If k is the negative of the minimum s[i], the sum is infinity. If k is infinity, the sum is zero. Between these, the function is continuous and monotonically decreasing. Once a finite k is found that produces a sum less than t (easily found by, say, doubling sample values until the criterion is met), bisection can be applied. – Eric Postpischil Apr 02 '22 at 11:46

1 Answers1

4

All of this was mentioned in comments, but it's not clear...

If you guess a value for k, then you can determine how long the whole trip would have taken if that guess was correct:

Total time T = distance1/(speed1+k) + distance2/(speed2+k)...

If this total time is more than the actual total time given in the problem, then your guess is too small (you went faster than you guessed). If the guessed total time is less than the actual total time, then your guess is too big (you went slower than you guessed).

With the ability to make and test these guesses, you can play the higher/lower game to narrow down the range of possible k values until you get as close to the real value as you want.

You can't necessarily get to the exact value, which is why this could be called an approximate algorithm. But the numbers we work with also have limited precision, so they probably can't even represent the exact value. You can easily get one of the closest representable values, which is just as good as an "exact" calculation.

The algorithm for playing the higher/lower game is called "binary search". It's a little tricky with doubles, so I'll write it out. Given a function isTooHigh that tests a guess for k, you can do it like this:

double findk()
{
    double lo = -minOfAll(speeds);
    double hi = max(1.0, lo);
    while(!isTooHigh(hi)) {
        hi *= 2.0;
    }
    while(lo < hi) {
        double test = lo + (hi-lo)*0.5;
        if (isTooHigh(test)) {
            if (test >= hi) {
               // we've reached the limit of precision
               break;
            }
            hi = test;
        } else {
            if (test <= lo) {
               // we've reached the limit of precision
               break;
            }
            lo = test;
        }
    }
    return lo;
}

Note that, because of the way double-precision numbers are represented, this loop could iterate up to a 2000 times if k is 0 or very close to it. If you don't want that, then you can change the (lo < hi) test to (lo < hi - REALLY_SMALL). You will have to put up with a less accurate answer, but you can limit the maximum number of iterations.

There are also other approximate algorithms you could use that require fewer iterations, like Newton's method

Matt Timmermans
  • 53,709
  • 3
  • 46
  • 87
  • As Eric mentioned in the comments, `lo` has to be initialized to -min(speed), because there's a pole there. The termination condition would be better as "hi - low < tolerance" for some tolerance. I don't think your "test >= hi" condition will always cause the procedure to terminate, especially as I think `test` is incorrectly calculated as it's not always between lo and hi. – Paul Hankin Apr 02 '22 at 13:17
  • Ha. fixed the dumb mistake and the initial lo. The necessary guards are in place to ensure termination. If (test – Matt Timmermans Apr 02 '22 at 13:21
  • I see how your termination condition works now. I think your hi can be wrong because it may start below the singularity: `double hi = max(1.0, lo + 1);` works better. – Paul Hankin Apr 02 '22 at 13:51
  • That is better, even though it does assume that isTooHigh() doesn't do what it says. – Matt Timmermans Apr 02 '22 at 16:46
  • I assumed `isTooHigh` measured the function, and determined if it was above the target time. But there's values below the initial lo which are "too high" in that sense (the function has several poles, and goes from infinity to -infinity multiple times). Eric's final comment on the question was on point about this. – Paul Hankin Apr 02 '22 at 17:40
  • `isTooHigh` here returns true if a guess for `k` is too high, so it should return *false* if the time summation is too high or if the k guess is below any valid value. – Matt Timmermans Apr 02 '22 at 21:30