How to find a maximal odd decomposition of integer M?

前端 未结 3 1279
遥遥无期
遥遥无期 2021-02-09 18:40

Let M be an integer in range [1; 1,000,000,000].

A decomposition of M is a set of unique integers whose sum is equal to M.

A decomposition is odd

相关标签:
3条回答
  • 2021-02-09 19:17

    Here is an idea which should work. It requires a net removal of at most 1 number from the greedy set you construct.

    Construct your O(sqrt(M)) list as usual (without result[len - 1] = M - sum;). If the sum is not a square number (i.e. exact):

    • Add the next largest odd number
    • Take the difference between your sum now and your target number -> N

      • If N is odd, remove the corresponding number
      • If N is even but not 2, remove the largest odd number smaller than N, and the 1
      • If N is 2, remove the penultimate number you just added. This will make the difference 0

    Proof:

    • If you add the consecutive odd numbers sequentially then the list should be as dense as possible - i.e. you cannot have a larger list
    • Also the difference is always bounded by the odd number you add in step 2, so a net removal of up to two numbers is always accounts of either odd or even differences.

    Examples:

    • Say you want 42: construct [1, 3, 5, 7, 9, 11], add 13 => sum = 49, remove 7 (no net removal)
    • You want 39: construct [1, 3, 5, 7, 9, 11], add 13, remove 1 and 9 (net removal of 1)
    • You want 38: construct [1, 3, 5, 7, 9, 11], add 13, remove 11 (no net removals)

    EDIT: error in last case corrected courtesy of @user1952500

    0 讨论(0)
  • 2021-02-09 19:32

    I don't see why people have to make it so complicated. An odd decomposition is like a self-conjugate partition turned on its side and unfolded out, for example, n = 13

    4 4 3 2     =>              =>            =>              7 5 1
    
    x x x x   rotate             x         unfold out      x x x x x x x
    x x x x   clockwise     ↖  x  x  ↗      each side       x x x x x
    x x x     45 degrees      x  x  x           =>               x
    x x                      x  x  x  x 
                              x  x  x
    

    The larger an odd decomposition is, the larger the "bounding-square" of the corresponding self-conjugate. By "bounding-square" I mean the top left corner square, which is a constant in all similar-sized odd decompositions. For example, we could have written 13 as the self-conjugate {5,3,3,1,1} and the 9-cell "bounding square" would remain the same, with corresponding odd decomposition {9,3,1}:

    5 3 3 1 1        =>            9 3 1
    
    x x x x x                x x x x x x x x x
    x x x                          x x x
    x x x                            x
    x
    x
    

    To get the odd decomposition with the largest cardinality, find the largest "bounding square" with even remainder.

    Example:

    M = 24
    
    Bounding square | remainder
    1                 23 
    4                 20
    9                 15
    16                8
    25...too large
    
    Place the remainder in any diagonally-symmetric way you like. The simplest way might be
    
    xxxx         xxxxxxxx
    xxxx   =>    xxxx
    xxxx         xxxx
    xxxx         xxxx
                 x
                 x
                 x
                 x
    
    Decompose: 15,5,3,1
    

    I think this Haskell code outputs all possibilities:

    f m = g [1,3..bestRoot*2 - 1] remainder 0 []
      where root = floor (sqrt (fromIntegral m))
            bestRoot = head $ dropWhile (\x -> odd (m - x^2)) [root,root - 1..1]
            remainder = m - bestRoot^2
            g (x:xs) r prev res
              | null xs   = [reverse ((x + r):res)]
              | otherwise = do r' <- takeWhile (<= div remainder bestRoot) [prev,prev + 2..]
                               g xs (r - r') r' ((x + r'):res)
    

    Output:

    *Main> f 24
    [[1,3,5,15],[1,3,7,13],[1,5,7,11],[3,5,7,9]]
    
    *Main> f 23
    [[1,3,19],[1,5,17],[1,7,15],[3,5,15],[3,7,13],[5,7,11]]
    
    *Main> f 38
    [[1,3,5,7,9,13]]
    
    *Main> f 37
    [[1,3,5,7,21],[1,3,5,9,19],[1,3,7,9,17],[1,5,7,9,15],[3,5,7,9,13]]
    
    *Main> f 100
    [[1,3,5,7,9,11,13,15,17,19]]
    
    0 讨论(0)
  • 2021-02-09 19:39

    Here is a deterministic solution to the problem. Suppose M = {1, 3, 5, ..., 2*k-3, 2*k-1, r} where r <= 2*k + 1. It is 'obvious' that the maximal decomposition is not going to have more numbers than (k+1).

    We have the following cases for k > 3 (the reasoning and handling of earlier cases is presented later):

    Case 1. If r is odd and equal to 2*k+1: add r into the list thereby giving a decomposition of (k+1) elements.

    Case 2. If r is even: replace {(2*k-1), r} by {2*k-1+r} giving a decomposition of k elements.

    Case 3. If r is odd and not equal to 2*k+1: replace the first and the last two elements in the series {1, 2*k-1, r} by {2*k+r} giving a decomposition of (k-1) elements.

    Note that the worst case of (k-1) elements will occur when the input is of the form n^2 + (odd number < 2*k+1).

    Also note that (Case 3) will break in case the number of elements is less than 3. For example, the decomposition of 5 and 7. We will have to special-case these numbers. Likewise (Case 2) will break for 3 and will have to be special-cased. There is no solution for M=2. Hence the restriction k > 3 above. Everything else should work fine.

    This takes O(sqrt(M)) steps.

    Some C/C++ code:

    #include <stdio.h>
    
    int main(int argc, char *argv[])
    {
        printf("Enter M:");
        int m = 0;
        scanf("%d", &m);
    
        int arr[100] = {0};
        printf("The array is:\n");
        switch(m) {
            case 2:
                printf("No solution\n");
                return 0;
            case 1:
            case 3:
            case 5:
            case 7:
                printf("%d\n", m);
                return 0;
        }
    
        int sum = 0;
        int count = 0;
        for (int i = 1; (sum + i) < m; i+= 2) {
            arr[count++] = i;
            sum += i;
        }
        int start = 0;
        int r = m - sum;
        if (r % 2 == 0) {
            arr[count - 1] += r;
        } else if (r > arr[count - 1]) {
            arr[count++] = r;
        } else {
            start = 1;
            arr[count - 1] += r + 1;
        }
    
        for (int i = start; i < count; i++) {
            printf("%d\n", arr[i]);
        }
    
        return 0;
    }
    

    Example:

    Enter M:24
    The array is:
    1
    3
    5
    15
    
    Enter M:23
    The array is:
    3
    5
    15
    
    0 讨论(0)
提交回复
热议问题