phsads's blog

By phsads, history, 14 months ago, In English

GIven this problem:

Given $$$n$$$ integer pairs $$$a_i$$$, $$$b_i$$$, pick $$$k$$$ of them so that $$$\frac{\sum_{}^{}a_i}{\sum_{}^{}b_i}$$$ is maximized, output the maximum value.

Constraints:

$$$1 \le k \le n \le 200000$$$

$$$1 \le a_i, b_i \le 10^{6}$$$

I have this scuffed greedy algorithm which does the following:

  1. Assume the result is $$$\ge x$$$, then let $$$u_i = a_i - xb_i$$$

  2. Greedily pick the largest $$$k$$$ values of $$$u_i$$$.

  3. Update $$$x$$$ to the new optimal value, if the new value is not better then stop.

After a lot of random tests, the worst case only made it perform $$$7$$$ iterations.

Are there any tests that makes it TLE, or at least run a lot more than $$$7$$$ iterations? If not, is there a proof? What is the runtime in the worst case?

Code
  • Vote: I like it
  • +64
  • Vote: I do not like it

»
14 months ago, hide # |
 
Vote: I like it +92 Vote: I do not like it

Actually, this algorithm is known as Dinkelbach algorithm. If you invented it by yourself you must be a genius.

I think I can't prove it, but there are already some proof on the Internet. According to some informations, the worst-case number of iterations of the Dinkelbach algorithm is $$$O(\log(n C D)) \leq O(\log(n M))$$$, where $$$M = \max(C, D)$$$, where $$$C=\max(\max|c_i|,1),D=\max(\max|d_i|,1)$$$. (In this article, the pairs are indicated as $$$c_i, d_i$$$.)

Some other proof, I translated it into English using AI

I am only responsible for bringing these contents over, unfortunately I do not have the ability to verify the correctness of the content.

»
14 months ago, hide # |
 
Vote: I like it +37 Vote: I do not like it

That's a cool algorithm.

Basically, you have $$$\binom{n}{k}$$$ options for the set of pairs you choose, each set $$$S$$$ will give some fraction $$$\frac{A_S}{B_S}$$$, which you then transform into a function $$$f_S(x) = A_S - B_S \cdot x$$$ which is linear. Then you construct a lower envelope (convex hull) of those lines, and then apply iteration to find a 0 of that lower envelope.

It can actually be shown that the lower envelope has at most $$$O(n^2)$$$ segments (maybe even some better bound is possible, but I don't know how), and it can be constructed in $$$O(n^2)$$$ time, but it is not really relevant to the iteration approach.

The iteration works as follows: we take $$$x_0 = 0$$$, then for $$$x_i$$$ we find the largest $$$f_S(x_i)$$$ and find $$$x_{i+1}$$$ as a 0 of $$$f_S(x)$$$. Let's denote the function we found for $$$x_i$$$ as $$$A_i - B_i \cdot x$$$, and $$$y_i = A_i - B_i \cdot x_i$$$. By the definition of $$$x_{i+1}$$$, $$$A_i - B_i \cdot x_{i+1} = 0$$$. Therefore, $$$y_i = B_i \cdot (x_{i+1} - x_i)$$$. $$$y_i$$$ is the largest value of our linear functions at $$$x_i$$$, so $$$A_{i+1} - B_{i+1} \cdot x_i \le y_i$$$. $$$y_{i+1} = A_{i+1} - B_{i+1} \cdot x_{i+1}$$$, which means $$$y_{i+1} + B_{i+1} \cdot (x_{i+1} - x_i) \le y_i$$$ and then $$$y_{i+1} \le \left( 1 - \frac{B_{i+1}}{B_i} \right) y_i$$$.

From this we can conclude that $$$B_{i+1} \le \frac{1}{2} B_i$$$ or $$$y_{i+1} \le \frac{1}{2} y_i$$$ (or both). $$$y_{i+1} = A_{i+1} - B_{i+1} \cdot x_{i+1} = A_{i+1} - \frac{B_{i+1}}{B_i} A_{i}$$$. $$$B_i$$$ are positive integers, $$$y_i$$$ are non-negative rational numbers with both numerators and denominators bounded by $$$(\max A_i) \cdot (\max B_i)$$$. Therefore, after $$$O(\log ((\max A_i) \cdot (\max B_i)))$$$ iterations we must have $$$y_i = 0$$$.

  • »
    »
    14 months ago, hide # ^ |
     
    Vote: I like it +19 Vote: I do not like it

    By the way I think the frac-programming of the binary version is better than the Dinkelbach version. So we should stop learning useless algorithms, go and solve some problems, learn how to use binary search.

    • »
      »
      »
      14 months ago, hide # ^ |
       
      Vote: I like it 0 Vote: I do not like it

      What's a frac-programming?

      • »
        »
        »
        »
        14 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it

        The problem described by the poster in the CNOI community is a template question for "01 分数规划", but I don't know what the English for this thing is, and there is no reasonable dictionary for me to use. So I used the English title of the Chinese OI wiki section.

        • »
          »
          »
          »
          »
          14 months ago, hide # ^ |
           
          Vote: I like it +1 Vote: I do not like it

          Well, you could have explained the concept.

          Is it the idea that when we want to find the maximum of some fraction (like the weighted average of something), we want to apply binary search, multiply by the denominator and then consider $$$\sum (p_i - \lambda q_i)$$$?

          • »
            »
            »
            »
            »
            »
            14 months ago, hide # ^ |
            ← Rev. 2  
            Vote: I like it 0 Vote: I do not like it

            Binary search is just a joke (first mentioned by you several years ago), it should actually be a binary answer to the lambda you mentioned, so that's it.

            The Chinese OI Wiki website about this kind of problems is there.

            I'm afraid that even a translator may not be able to handle the formulas on the page well, so it's better to ask an AI to translate.

      • »
        »
        »
        »
        14 months ago, hide # ^ |
         
        Vote: I like it 0 Vote: I do not like it
  • »
    »
    14 months ago, hide # ^ |
     
    Vote: I like it +22 Vote: I do not like it

    I guess it's kind of an application of Newton's iteration to a piecewise linear convex function. I am not proficient in this area, so I can't tell you if it's insightful or not.

    Also regarding your code: you don't need to sort the values to find the sum of $$$k$$$ maximums. Use nth_element, it's $$$O(n)$$$.

    • »
      »
      »
      14 months ago, hide # ^ |
      ← Rev. 2  
      Vote: I like it +13 Vote: I do not like it

      I just wanted to remark that $$$O(n)$$$ is only guaranteed in the average case (page 905); the worst case of gcc's implementation is $$$O(n\log n)$$$. It uses the quickselect and heapselect combination mentioned on the wiki article for Introselect.

      Of course, it is still a better choice over sorting (where possible) runtime-wise in most cases.