Java 中的 Zumkeller 数

2025年4月10日 | 阅读 12 分钟

如果一个数 N 的所有因子可以被分成两个集合,使得第一个集合中因子的和等于第二个集合中因子的和,则称 N 为 Zumkeller 数。这两个集合都必须是非空集。让我们通过几个例子来理解一下。输入时会给出数字 N。

示例 1

输入: N = 84

输出: 是,84 是一个 Zumkeller 数。

解释: 84 的因子是:1, 2, 3, 4, 6, 7, 12, 14, 21, 28, 42, 84。现在,如果我们把这些因子分成两个集合,如下所示,我们得到:

(28, 84) 和 (1, 2, 3, 4, 6, 7, 12, 14, 21, 42)。现在,第一个集合元素的和为 28 + 84 = 112

第二个集合元素的和为:

1 + 2 + 3 + 4 + 6 + 7 + 12 + 14 + 21 + 42 = 112。由于这两个集合元素的和相等,所以数字 84 是一个 Zumkeller 数。

示例 2

输入: N = 108

输出: 是,108 是一个 Zumkeller 数。

解释: 108 的因子是:1, 2, 3, 4, 6, 9, 12, 18, 27, 36, 54, 108。现在,如果我们把这些因子分成两个集合,如下所示,我们得到:

(2, 3, 27, 108) 和 (1, 4, 6, 9, 12, 18, 36, 54)。现在,第一个集合元素的和为 2 + 3 + 27 + 108 = 140

第二个集合元素的和为:

1 + 4 + 6 + 9 + 12 + 18 + 36 + 54 = 140。由于这两个集合元素的和相等,所以数字 108 是一个 Zumkeller 数。

示例 3

输入: N = 200

输出: 否,200 不是一个 Zumkeller 数。

解释: 200 的因子是:1, 2, 4, 5, 8, 10, 20, 25, 40, 50, 100, 200。现在,如果我们把这些因子分成任何两个集合,我们都无法找到一对集合,使其元素的和相等。因此,200 不是一个 Zumkeller 数。

朴素方法

方法很简单。首先,我们将找到数字 N 的所有因子并将其存储在一个数组中。同时,计算数组中元素的总和并将其存储在一个名为 totalSum 的变量中。然后,我们将计算所有可能的集合,并将它们存储在二维数组中。迭代二维数组,并在每次迭代中逐个计算集合的总和。我们将其存储在一个名为 setSum 的变量中。然后,用 totalSum 减去 setSum,得到的差值如果与 setSum 匹配,则数字 N 是 Zumkeller 数。如果不匹配,请对其他迭代重复上述过程。如果所有迭代都已完成,并且没有一次迭代得到 totalSum - setSum 等于 setSum 的值,那么我们可以说 N 不是 Zumkeller 数。

文件名: ZumkellerNaive.java

输出

Yes, 84 is a Zumkeller number.
Yes, 108 is a Zumkeller number.
No, 200 is not a Zumkeller number.

复杂度分析: 上述程序使用了递归,使得程序的 time complexity 是指数级的,即 O(2f),space complexity 也是指数级的,即 O(f * 2f),其中 f 是输入数字 n 的总因子数。

上述程序的 space 和 time complexities 都非常高,不适用于因子数量很多的数字。对于这类数字,可能会出现 time limit exceeded 或 memory limit exceeded。因此,需要进行一些优化。

方法:使用动态规划

我们可以通过动态规划做得更好。观察使用动态规划解决问题的步骤。

步骤 1: 首先,检查数字 N 的因子之和是否为偶数。如果和不是偶数,我们可以得出该数字不是 Zumkeller 数。

步骤 2: 声明一个大小为 (sumofFactors / 2) + 1 * (size + 1) 的二维数组 dp[][],其中 size 是因子的总数。

步骤 3: 运行一个 for 循环,从 0 <= j <= size,并将 dp[0][j] 设置为 true。因为零和总是可行的。

步骤 4: 使用 for 循环,从 1 <= j <= sumofFactors / 2,并将 dp[j][0] 设置为零,因为任何和为零的组合都是不可能的。

步骤 5: 运行一个嵌套 for 循环,从 1 <= k <= sumofFactors / 2 和 1 <= k <= size,并将 dp[j][k] 设置为 dp[j][k - 1]

步骤 6: 如果 j 大于或等于 arr[k-1],并且 dp[j - arr[k - 1]][k - 1] 为 true,则将 dp[j][k] 赋值为 true。

现在,让我们来实现上述步骤。

文件名: ZumkellerDP.java

输出

Yes, 84 is a Zumkeller number.
Yes, 108 is a Zumkeller number.
No, 200 is not a Zumkeller number.

复杂度分析: 该程序使用了嵌套的 for 循环。外层循环运行 sumOfFactors / 2 次,内层循环从 1 运行到 size 次。因此,程序的 time complexity 为 O(size * sumOfFactors)。由于程序中使用了二维数组,space complexity 也为 O(size * sumOfFactors)。

我们仍然可以进行优化以减小 space complexity。以下程序说明了这一点。

文件名: ZumkellerDP.java

输出

Yes, 84 is a Zumkeller number.
Yes, 108 is a Zumkeller number.
No, 200 is not a Zumkeller number.

复杂度分析: 该程序的 time complexity 与前一个程序相同。该程序的 space complexity 为 O(sumOfFactor),其中 sumOfFactor 是输入数字 n 的因子总和。

在范围内查找 Zumkeller 数

以下程序说明了如何查找范围内的 Zumkeller 数。

文件名: ZumkellerNumbersRange.java

输出

Zumkeller Numbers from 1 to 100 are:
6 12 20 24 28 30 40 42 48 54 56 60 66 70 78 80 84 88 90 96