Блог пользователя kllp

Автор kllp, 11 лет назад, По-английски

Today I was having troubles remembering how to code ternary search for integers. Through the ages, people have been taught to divide the interval into three equal length parts. Fortunately Sisu told me that I should not... I believe this is a new thing for some people.

while(lo < hi) {
    int mid = (lo + hi) >> 1;
    if(f(mid) > f(mid+1)) {
        hi = mid;
    }
    else {
        lo = mid+1;
    }
}
  • Проголосовать: нравится
  • +53
  • Проголосовать: не нравится

»
11 лет назад, # |
  Проголосовать: нравится -27 Проголосовать: не нравится

Have you ever heard about binary search?

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится +40 Проголосовать: не нравится

    You'd normally use binary search for finding an element from an increasing sequence of values, and ternary search for finding the maximum when the values first increase and then start decreasing at some point.

    The observation here is that you can replace ternary search with a binary search for the point when the sign between adjacent values change. This is of course a simple thing, but in many places you can find the recommended solution being ternary search, which is harder to get right and less efficient.

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится -6 Проголосовать: не нравится

      Thanks a lot. This comment helped me to understand much more than the whole op post.

»
11 лет назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

Does it work correctly in case f(mid) == f(mid + 1) ?

For example: f = {0, 1, 0, 0, 0}

  • »
    »
    11 лет назад, # ^ |
    Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

    No, the function should be first strictly increasing and then strictly decreasing or vice versa.

    EDIT: increasing/decreasing --> strictly increasing/decreasing

    • »
      »
      »
      11 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Ok, ternary search also requires strict increase/decrease. So you algorithm is preferable.

»
11 лет назад, # |
  Проголосовать: нравится +8 Проголосовать: не нравится

Btw, if you search for some "double" answer, there is another intresting way to do that: just divide the segment by golden ratio, for example: AB → ACDB, where C divides AB in the way that AC < CB and D divides AB and AD > DB. The feature is unaware of the sub-segment you choose (AD or CB), one of the points you need on the next step is C or D; it's easy to prove from the definition of golden ratio.

So, you have to calculate only one next func value instead of two, but that can be really important in some cases, for example ones the function I minimized contained Dijkstra algorithm, so that was a cool cheat and my code easily passed TL without optimizing anything else.

  • »
    »
    10 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    Could you please explain the algorithm in more detail? Maybe a sample code will be useful. Thanks in advance.

»
11 лет назад, # |
  Проголосовать: нравится +16 Проголосовать: не нравится

What you're doing is very similar to binary search on continuous derivative (which may be useful for some floating-point problems, because calculating derivative may be performed with more precision sometimes). If it is below zero until some point and is above zero after that point, then local optima is in the point where derivative is zero. You've calculated discrete derivative and look for a point where it changes the sign.

»
11 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

So, what you are saying is that in some cases you can just use binary instead of ternary? I couldn't understand it well. Would you mind linking me to a problem where this applies?

Thanks in advance!

  • »
    »
    11 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    no, he's not saying that. what he means is that it is sufficient to divide the interval [lo,hi] into two parts, instead of three, while doing ternery search.
    the benefit is the reduction in complexity from to . this is usually not significant, but it's a reduction nonetheless and may help for problems with tight time limits.

»
9 лет назад, # |
Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

I don't need to take any special care with the f(mid+1)? If the search goes to the end of the data in my array for example. Actually i just got Accepted with this approach for ternary search by changing it to:

int ternary(int lo, int hi){
	while(lo <= hi){
		int mid = (lo+hi) >> 1;
		if(f(mid) > f(mid+1)){
			hi = mid-1;
		}
		else{
			lo = mid+1;
		}
	}
}
  • »
    »
    7 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

    If we look at it from a divide and conquer perspective, you are discarding mid at every iteration, I don't understand how this got accepted.

    The safe thing to to do with this approach is to keep a variable which keeps the maximum value of the mid.

    If f(mid)>f(mid+1), maybe the mid value found at first iteration is the actual maxima. Now you've reduced search space to [lo,mid-1] regardless. You can just add a line max_val = max(max_val,f(mid)) above it, to make it absolutely correct.

    You don't need to worry about mid+1 exceeding high in the author's code, here you've added an extra equal-to lo<**=**high , so you should take care of it.

    • »
      »
      »
      6 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      You're completely right. But it has been such a long time and i don't remember for which problem i have used this code. Thank you.

»
9 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

Hi . I have got a great help from this Blog about Ternary Search . And I have applied in some problems also and got a predicted result . Now My Question is how can I use this Ternary Search for Floating Point Data ??

Thanks in advance .

»
6 лет назад, # |
  Проголосовать: нравится +9 Проголосовать: не нравится

There's a better way: Golden-section search. It reduces hi - lo by a factor of φ ≈ 1.618 by only probing one new point. For example, it can reduce 106 to 1 in around 29 calls to f, instead of 40.

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится -7 Проголосовать: не нравится

    This post is about ternary search on integers. We can't choose integer points in the same way as we do it in golden-section search.

    • »
      »
      »
      6 лет назад, # ^ |
        Проголосовать: нравится +8 Проголосовать: не нравится

      Why not? Just round it to the nearest integer each time.

      • »
        »
        »
        »
        6 лет назад, # ^ |
          Проголосовать: нравится 0 Проголосовать: не нравится

        Yes, the length will be reduced by φ, but the main benefit of this search is that we calculate the function at one point on each iteration after the first. I don't think this property remains if we round the points on each iteration.

»
6 лет назад, # |
  Проголосовать: нравится 0 Проголосовать: не нравится

But it doesn't work for the case f(mid)=f(mid+1)

  • »
    »
    6 лет назад, # ^ |
      Проголосовать: нравится +9 Проголосовать: не нравится

    Ternary search doesn't work either.

    • »
      »
      »
      6 лет назад, # ^ |
        Проголосовать: нравится 0 Проголосовать: не нравится

      Yeah you're right, didn't noticed that, but at least there are some special cases which ternary works, but this doesn't... Doesn't really matter, as I said, I was wrong...

      • »
        »
        »
        »
        6 лет назад, # ^ |
        Rev. 2   Проголосовать: нравится 0 Проголосовать: не нравится

        Can you give such special cases? Pretty sure this works as good or better in almost any case.

        UPD: Okay apparently it is horrible for problems with real numbers. Example: http://codeforces.net/contest/1059/problem/D But for these problems ternary search is easy to code anyways (because don't have to deal with annoying cases on the integers), so this method really has no drawbacks.

        A good problem you can try this on is USACO angry cows (gold). Similar idea to the above problem, but on integers (so you can use this "trick").