如何找到整数 M 的最大奇数分解?

How to find a maximal odd decomposition of integer M?

令M为范围[1; 1,000,000,000].

M的分解是一组唯一的整数,其总和等于M。

如果分解只包含奇数,则分解为奇数

M 的分解是最大 如果没有其他 M 的分解在集合的大小上更大。

写一个函数:

int[] maxOddDecomposition(int M)

return是一个数组,其中 M 的最大奇数分解。数组中的数字应按升序排列。如果 M 没有任何奇数分解,则数组应该为空。如果有多个正确答案,该函数可能 return 其中任何一个。

例如M = 6有四次分解:

6 = 1 + 2 + 3
  = 1 + 5
  = 2 + 4
  = 6

只有1 + 5是奇分解,因此是最大奇分解。我们应该 return 它在数组中使得 array[0] = 1array[1] = 5.

预期的最坏情况时间和 space 复杂度为 O(sqrt(M))。


我尝试过的:

由于时间复杂度必须为 sqrt(M),这让我想起了 M 算法的朴素分解,我们从 1 迭代到 sqrt(M)。不过没有进一步的想法出现。只是它必须非常快,只有 sqrt(M) 步。

所以,我做了一些例子。例如,如何找到 20 的答案? 20以内的奇数有哪些? 1 + 3 + 5 + 7 + ... 我们已经有了 16。所以,我们可以加 4,但 4 是偶数。

所以,让我们用 (7 + 4) = 11 替换 7,我们就完成了:1 + 3 + 5 + 11。我注意到初始序列总是有 floor(sqrt(M)) 元素,完美的。让我们用伪代码编写代码:

int len = floor(sqrt(M));
int result[] = new int[len];
int sum = 0;
for (i = 0; i < len - 1; i++) {
    result[i] = 1 + 2*i;
    sum += result[i];
}
result[len - 1] = M - sum;
return result;

我为 M = 2 做了一个特例,returning 一个空数组。我以为就这样了,finito。

我没有注意到它会中断 3,因为它给出 1 + 2 而不是 3。对于 5,给出 1 + 3 + 1,而不是 5。还有更多。


你会怎么解决?

这是一个可行的想法。它需要从你构造的贪心集合中净删除最多 1 个数。

像往常一样构造你的 O(sqrt(M)) 列表(没有 result[len - 1] = M - sum;)。如果总和 不是 平方数(即精确):

  • 加上下一个最大的奇数
  • 计算你现在的总和和你的目标数字之间的差 -> N

    • 若N为奇数,则去掉对应的数
    • 如果N是偶数但不是2,去掉比N小的最大奇数,1
    • 如果 N 为 2,删除您刚刚添加的 倒数第二个 数字。这将使差异 0

证明:

  • 如果您按顺序添加连续的奇数,则列表应尽可能密集 - 即您不能有更大的列表
  • 此外,差值始终以您在第 2 步中添加的奇数为界,因此 net 删除最多两个数字始终是奇差或偶差的帐户。

示例:

  • 假设你想要 42:构建 [1, 3, 5, 7, 9, 11],添加 13 => sum = 49,删除 7(没有净删除)
  • 你想要 39:构建 [1, 3, 5, 7, 9, 11],添加 13,删除 1 和 9(净删除 1)
  • 您想要 38:构建 [1, 3, 5, 7, 9, 11],添加 13,删除 11(无净删除)

编辑:最后一个案例中的错误由@user1952500 更正

这是问题的确定性解决方案。假设 M = {1, 3, 5, ..., 2*k-3, 2*k-1, r} 其中 r <= 2*k + 1。最大分解为 'obvious'数字不会超过 (k+1).

对于k > 3,我们有以下情况(前面的情况的推理和处理稍后介绍):

情况 1。如果 r 是奇数且等于 2*k+1:将 r 添加到列表中,从而给出 (k+1) 个元素的分解。

情况 2. 如果 r 是偶数:将 {(2*k-1), r} 替换为 {2*k-1+r} 给出 k 个元素的分解。

情况3.若r为奇数且不等于2*k+1:将数列{1, 2*k-1, r}中的第一个和最后两个元素替换为{2*k+ r} 给出 (k-1) 个元素的分解。

请注意,当输入的形式为 n^2 +(奇数 < 2*k+1)时,将出现 (k-1) 个元素的最坏情况。

另请注意,如果元素数量小于 3,(情况 3)将中断。例如,分解 5 和 7。我们将不得不对这些数字进行特殊处理。同样(情况 2)将中断 3 并且必须是特殊情况。 M=2 无解。因此,上面的限制 k > 3。其他一切都应该工作正常。

这需要 O(sqrt(M)) 个步骤。

一些C/C++代码:

#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;
}

示例:

Enter M:24
The array is:
1
3
5
15

Enter M:23
The array is:
3
5
15

我不明白为什么人们要把它搞得这么复杂。一个奇数分解就像一个 self-conjugate 的分区打开它的一侧并展开,例如 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

奇数分解越大,对应的自共轭"bounding-square"越大。 "bounding-square" 是指左上角的正方形,它在所有类似大小的奇数分解中都是一个常数。例如,我们可以将 13 写成自共轭 {5,3,3,1,1},而 9 格 "bounding square" 将保持不变,相应的奇分解 {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

要得到基数最大的奇数分解,找到最大的"bounding square",余数为偶数。

示例:

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

我认为这个Haskell代码输出了所有的可能性:

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)

输出:

*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]]