1、题干
对于一个 正整数,如果它和除了它自身以外的所有 正因子 之和相等,我们称它为 「完美数」。
给定一个 整数 n
, 如果是完美数,返回 true
;否则返回 false
。
示例 1:
输入:num = 28
输出:true
解释:28 = 1 + 2 + 4 + 7 + 14
1, 2, 4, 7, 和 14 是 28 的所有正因子。
示例 2:
输入:num = 7
输出:false
提示:
1 <= num <= 108
2、解题思路
在区间 [ 1, ] 中找 的因子i
,是因子则累加i + num / i
(i=1
时只加1),最后判断因子之和是否等于 即可。
3、代码
var checkPerfectNumber = function (num) {
let sum = 1;
for (let i = 2; i * i <= num; i++) {
if (num % i === 0) sum += i + num / i;
}
return num === 1 ? false : sum === num;
};
4、困惑
题目比较简单,找一个正整数的所有正整数因子只需要遍历到 的结论也一直记着。如果用数学归纳法,举几个例子很容易推出这个结论。
官解这么说:如果 有一个大于 的因子 ,那么它一定有一个小于的因子。也就是说 的因子总是成对出现。
但是有两个关键问题始终让人很困惑:1、为什么遍历终点是 ,2、因子有没有大于 且未被找到的特例。实际上可以用高中数学做个推导,来解答这些困惑。过程有点繁琐,如果对过程不感兴趣可以直接看最后的结论。
5、推导
假设要找正整数 的所有正整数因子 和 。(注意:以下推导只考虑正整数范围内的情况)
必然有,移项得到 。
反比例函数
这个函数是不是很眼熟,没错它就是高中数学课本里的反比例函数。以 为例,其图形如下:
从上图可以很容易看出,反比例函数上的任意坐标 对应的整数 和 都是 的因子。这跟官解所说 的因子总是成对出现的意思一致。也就是找到一个因子 时,必然找到了另外一个因子 。
反比例函数的对称性
反比例函数图像是以 为对称轴的轴对称图形(反比例函数对称特性)。以 为例,其图形如下:
关于 对称的两个点,他们的坐标是 和 互换,即任意坐标 的对称点为 。例如:从上图很容易看出坐标 关于 对称的点是 。
由此可得 的任意因子对 具有关于 对称的性质。
而 与 的交点 的坐标是 。
最后可得,假设遍历因子时取数自 轴,在交点 左上侧出现过的任意点 (其中 且 ),都会在交点的右下侧以 的形式出现。
也就是说,遍历大于 的因子本质上是在重复遍历小于 的因子,只是把小于 时找到的因子对进行 、 互换位置而已。
因此遍历到 就能找到所有因子,而且没有大于 而未被找到的特例因子,这也就解释了前文提到的两个困惑。
举个例子
的因子对是
的因子对是
可以发现因子对具有回文特性,这也是反比例函数对称性的体现。另外很容易看出 和 这两个数都只要遍历到 即可找到所有因子。
6、小结
找因子之所以只需要遍历到 ,是因为:
- 因子之间存在反比例函数关系 ,所以因子总是成对出现
- 反比例函数关于 对称,所以所有因子对也关于 对称
- 而反比例函数 图形与对称轴 的交点是
- 所以在反比例函数的图形上,任意处于交点 左上侧的点 ,都会在交点的右下侧以 的形式出现(其中 且 )
- 所以遍历到 就能找到所有因子且不会出现大于 而未被找到的特例因子
也就是说,找因子只需要遍历到 即可,因为遍历大于 的因子本质上是在重复遍历小于 的因子,只是把小于 时找到的因子对进行 、 互换位置而已。 由于数轴上的数具有连续性(非离散),因此这个结论对于小数也成立。