day14 HJ56 完全数计算(Java)

255 阅读2分钟

题目来源: HJ56 完全数计算

题目描述:

  • 描述: 完全数(Perfect number),又称完美数或完备数,是一些特殊的自然数。 它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。
    例如:28,它有约数1、2、4、7、14、28,除去它本身28外,其余5个数相加,1+2+4+7+14=28。
    输入n,请输出n以内(含n)完全数的个数。
    数据范围: 1n5×1051≤n≤5×105
  • 输入描述: 输入一个数字n
  • 输出描述: 输出不超过n的完全数的个数
示例1:
输入:1000
输出:3

思路

反其道而行之,题目虽然求的是完全数,但是其实就是求一个数的所有约数,然后将除自身外的约数相加,结果等于自身,则为完全数。所以,题目的核心就是求一个数的所有约数。

具体实现:

import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner scanner = new Scanner(System.in);
        int n = scanner.nextInt();
        int Sum = 0;
        for(int i = 1;i <= n;i++){
            if(Perfect(i)){
                Sum ++;
            }
        }
        System.out.println(Sum);
    }

    static boolean Perfect(int number){  //判断是否满足完全数
        int sum = 0;
        for(int i = 1;i <= Math.sqrt(number);i++){  
            if(number % i == 0){    //求每个数的约数
                if(number / i == i){   //如果除数和商相同,那么只保留一个作为约数,
                                       //达到去重的效果
                    sum += i;   
                }else{        //如果除数和商不同,那么两个都保留作约数
                   sum += i;
                   sum += number/i;
                }
            }
        }
        //上述for循环,会将数的所有约数相加,但是其中包括自身
        //所以要将自身从求得的总数中减去
        if(sum-number == number){    
            return true;
        }else{
            return false;
        }
    }
}

思路杂记

1.我们这道题的思路,其实就是用N除1到N的二次平方根,余数为0时,取除数及整除的结果,但是为什么除到二次平方根就可以了呢?

如果有非平方数N=a×b,其中a<b,那么一定有a<N\sqrt {N},b>N\sqrt {N},对于每一个小于N\sqrt {N}的N的约数a,都有一个对应的b>N\sqrt {N},b也是N的约数,因此我们只需要遍历2到N\sqrt {N}就可以了,每一次整除都对应了N的两个约数,最后只需要再考虑一下N是不是平方数就可以了。这样,时间复杂度就被优化到了N\sqrt {N}

  • 最后考虑N是不是平方数,是为了去重\color{red}{去重}
    if(number / i == i){   //如果除数和商相同,那么只保留一个作为约数
        sum += i;
    }
    

2.对于求一个数的约数,其实有很多种思路

  • 思路1:给定的数N依次除以1到N的数,余数为0则是约数
static boolean Perfect(int number){
    int sum = 0;
    for(int i = 1;i <= number;i++){
        if(number % i == 0){    
                sum += i;
        }
    }
    if(sum-number == number){ 
        return true;
    }else{
        return false;
    }
}
  • 思路2:对于一个数N,存在N/N==1,而N与1分别是数N的最大约数与最小约数,同理,次大约数就是N/2
    我们可以进一步减少循环的次数,N除以1到N/2, 余数为0为是约数
static boolean Perfect(int number){
    int sum = 0;
    for(int i = 1;i <= number/2;i++){
        if(number % i == 0){
                sum += i;
        }
    }
    if(sum == number){   //因为sum是从1加到次大约数,并没有+最大约数(数自身),所以无需减去自身
        return true;
    }else{
        return false;
    }
}