【问题描述】
古希腊数学家毕达哥拉斯在自然数研究中发现,220的所有真约数(即不是自身 的约数)之和为: 1+2+4+5+10+11+20+22+44+55+110=284。而284的所有真约数为1、2、4、71、142,加起来恰好为220。人 们对这样的数感到很惊奇,并称之为亲和数。一般地讲,如果两个数中任何一个数都是另一个数的真约数 之和,则这两个数就是亲和数。
220和284是人类最早发现,又是最小的一对亲和数。第二对亲和数(17296,18416)直到2000多年后的1636年才由法国数学家费马发现。1638年,法国数学家笛 卡儿发现了第三对亲和数,而大数学家欧拉在1747年一下子给出了30对亲和数, 1750年又增加到60对。到目前为止,人类已经发现了近千对亲和数。然而,令人惊 奇的是,第二对最小的亲和数(1184,1210)竟然被数学家们遗漏了,直到1886年才 由意大利的一位16岁男孩发现。
这里不要求你找到一对亲和数,而是对于每个正整数n,求出这个数的真约数之和。
比如,n=220,答案是284,如果n=284,答案是220。
【输入形式】
有多组测试数据,每组测试数据一行,是一个正整数n, n=0意味着输入结束并且不需要处理。
40%的测试数据1 ≤ n≤ 10(3);30%的测试数据1 ≤ n≤ 10(4);
20%的测试数据1 ≤ n≤ 10(5);10%的测试数据1 ≤ n≤ 10(6);
【输出形式】
对于每组测试数据,输出一行,包含一个正整数,表示n的真约数之和。
【样例输入】
220
284
0
【样例输出】
284
220
解题思路
问题关键:如何高效地找到一个数的所有真约数之和。一个直观的方法是遍历从1到n-1的所有数,检查它们是否是n的约数,并计算它们的和。然而,这种方法在n非常大时会非常慢。更高效的方法是只遍历到,因为大于的约数可以通过n / 约数 得到。这样可以显著减少计算量。
-
对于每个输入的正整数n,初始化一个变量sum来存储真约数之和,初始值为1(因为1是所有正整数的真约数)。
-
从2遍历到,对于每个i,检查它是否是n的约数。
-
如果i是n的约数,检查i是否等于 n / i(即检查是否是平方根)。
-
如果是,只加i到sum(因为在这种情况下,i和 n / i实际上是同一个数)。
-
如果不是,将 i 和 n / i 都加到sum。
-
-
-
特别注意,如果n为1,其真约数之和应为0,因为1没有真约数。
-
输出sum。
Java代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (true) {
int n = scanner.nextInt();
if (n == 0) break; // 输入结束
System.out.println(sumOfDivisors(n));
}
scanner.close();
}
// 计算并返回n的真约数之和
private static int sumOfDivisors(int n) {
if (n == 1) return 0; // 1没有真约数
int sum = 1; // 所有数都至少有一个真约数1
// 只需要检查到sqrt(n),因为大于sqrt(n)的约数可以通过n / 约数得到
for (int i = 2; i <= Math.sqrt(n); i++) {
if (n % i == 0) {
sum += i;
if (i != n / i) { // 避免平方根被加两次
sum += n / i;
}
}
}
return sum;
}
}