Project Eluer - 23

Non-abundant sums

Problem 23

A perfect number is a number for which the sum of its proper divisors is exactly equal to the number. For example, the sum of the proper divisors of 28 would be 1 + 2 + 4 + 7 + 14 = 28, which means that 28 is a perfect number.

A number n is called deficient if the sum of its proper divisors is less than
n and it is called abundant if this sum exceeds n.

As 12 is the smallest abundant number, 1 + 2 + 3 + 4 + 6 = 16, the smallest number that can be written as the sum of two abundant numbers is 24. By mathematical analysis, it can be shown that all integers greater than 28123 can be written as the sum of two
abundant numbers. However, this upper limit cannot be reduced any further by analysis even though it is known that the greatest number that cannot be expressed as the sum of two abundant numbers is less than this limit.

Find the sum of all the positive integers which cannot be written as the sum of two abundant numbers.

翻译(大意):

如果一个数 n,它的所有真因子之和如果等于n,n就被称为完全数(例如28,1+2+4+7+14=28,所以28为完全数)。小于n,n被称为不足数。大于n,能被称为过剩数。最小的过剩数为12,(1+2+3+4+6=16, 大于12),所以最小的两个过剩数之和为12+12=24。

经过数学分析,所有大于28123的数都可以写成两个过剩数之和。求出正整数中不能被写成两个过剩数之和的所有数之和。

解决方案:

根据题意,大于28123的数所有数都能被写成两个过剩数之和,所有我们只需要关注小于等于28123的数。我的思路是:step1:求出所有小于28124的过剩数, step2:写一个函数,输入参数为n,算所有过剩数中,是否有两个的和可以等于n, step3:根据step2的返回值,累加结果。思路还是很简单,我们来看一看代码。

代码:

package projectEuler;

import java.util.ArrayList;

public class Problem23 {
	private static ArrayList<Integer> list;
	private static final int SIZE = 28123;

	public static void main(String[] args) {
		long start = System.currentTimeMillis();
		list = initAbundantList();
		int result = 0;
		for (int i = 1; i <= SIZE; i++) {
			result += (ifCanBeTheSumOfTwoAbundantNumber(i) ? 0 : i);
		}
		System.out.println("result:" + result);
		long end = System.currentTimeMillis();
		System.out.println("time:" + (end - start));
	}

	/*
	 * 判断过剩数列表中是否有两个数之和等于num
	 */
	private static boolean ifCanBeTheSumOfTwoAbundantNumber(int num) {
		int duration = findFirstIndexGreaterThanNum(num);
		for (int i = 0; i <= duration; i++) {
			for (int j = 0; j <= duration; j++) {
				if (num == (list.get(i) + list.get(j))) {
					// System.out.print("" + list.get(i) + "," + list.get(j));
					return true;
				}
			}
		}
		return false;
	}

	/*
	 * 在过剩数列表中,找到小于等于num的最后一个索引
	 */
	private static int findFirstIndexGreaterThanNum(int num) {
		int lengh = list.size();
		int i = 0;
		while (i < (lengh - 1) && list.get(i) < num) {
			i++;
		}
		return i;
	}

	/*
	 * 初始化小于等于28123的过剩数列表
	 */
	private static ArrayList<Integer> initAbundantList() {
		ArrayList<Integer> list = new ArrayList<Integer>();
		for (int i = 1; i <= SIZE; i++) {
			if (countSumProperDivisor(i) > i) {
				list.add(i);
			}
		}
		return list;
	}

	/*
	 * 算num的所有真因子的和,方法一
	 */
	private static int countSumProperDivisor(int num) {
		int sum = 0;
		int sqrtNum = (int) Math.sqrt(num);
		for (int i = 2; i <= sqrtNum; i++) {
			if (0 == num % i) {
				if (i == num / i) {
					sum += i;
				} else {
					sum += (i + num / i);
				}
			}
		}
		return sum + 1;
	}

	/*
	 * 算num的所有真因子的和,方法二
	 */
	private static int countSumProperDivisor2(int num) {
		int sum = 0;
		int duration = (int) num / 2;
		for (int i = 1; i <= duration; i++) {
			if (0 == num % i) {
				sum += i;
			}
		}
		return sum;
	}
}

代码中有注释,就不多做解释。思路和上面的解题思路是一样的。我这里还分析几个函数。

求一个数的所有真因子之和这里提供了两种方法:

求真因子之和:

方法一:这种方法只需遍历 2到 sqrt(num) 之间的数,1是能被所有数整除,所以可以直接在后面加1,我们就可以直接从2开始算。 每求到一个数的时候,其实是一对,比如24, 24/2 = 12,所以这里我们求得的是2个数 2和12,4和6是一对,而且可以发现规律,较小的数都是小于 sqrt(num)的,所有较大的数都大于 sqrt(num) ,所以我们才能这样算。但是需要注意,比如25, 最后算到5*5=25的时候,我们不能加两个数,而是应该直接加一个5。好了,这里就分析完了这个思路。

	/*
	 * 算num的所有真因子的和,方法一
	 */
	private static int countSumProperDivisor(int num) {
		int sum = 0;
		int sqrtNum = (int) Math.sqrt(num);
		for (int i = 2; i <= sqrtNum; i++) {
			if (0 == num % i) {
				if (i == num / i) {
					sum += i;
				} else {
					sum += (i + num / i);
				}
			}
		}
		return sum + 1;
	}

方法二:第二种方法要费时一点,从2到num/2计算,因为 大于num/2的所有数都不可能是num的真因子(想一下就知道)。所以这里把小于等于num/2的部分都算一遍,判断是否能mod尽就OK了。

	/*
	 * 算num的所有真因子的和,方法二
	 */
	private static int countSumProperDivisor2(int num) {
		int sum = 0;
		int duration = (int) num / 2;
		for (int i = 1; i <= duration; i++) {
			if (0 == num % i) {
				sum += i;
			}
		}
		return sum;
	}

求过剩数列表很简单就不讲了,我们再着重讲一下怎么判断是否有两个过剩数之和等于输入参数。这里我可以先提供一种思路,就是我们可以把列表中的所有过剩数之和能组合成的和算出来保存在数组中,后面就只需要将每个数遍历一次这个列表就可以知道。但是这个方法难在要求所有过剩数列中任意两个数之和,太耗费时间。所以来讲一下我的思路:

每传入一个数num,我们就在现有的过剩数列表中遍历任意两个数之和,一旦等于num,结束循环,返回true。 这里遍历的范围是提高速度的关键,我们可以思考一下,其实要两个过剩数之和等于num,那么过剩数肯定都小于num(过剩数中没有0),所以后面写了一个函数findFirstIndexGreaterThanNum(int num) ,根据名字就知道,实在过剩数列中找到小于num的范围,然后我们在判断 ifCanBeTheSumOfTwoAbundantNumber(int num)就可以缩小范围。不要小瞧这点范围哦,可以节约将近一半的计算次数。下面是代码:

	/*
	 * 判断过剩数列表中是否有两个数之和等于num
	 */
	private static boolean ifCanBeTheSumOfTwoAbundantNumber(int num) {
		int duration = findFirstIndexGreaterThanNum(num);
		for (int i = 0; i <= duration; i++) {
			for (int j = 0; j <= duration; j++) {
				if (num == (list.get(i) + list.get(j))) {
					// System.out.print("" + list.get(i) + "," + list.get(j));
					return true;
				}
			}
		}
		return false;
	}

	/*
	 * 在过剩数列表中,找到小于等于num的最后一个索引
	 */
	private static int findFirstIndexGreaterThanNum(int num) {
		int lengh = list.size();
		int i = 0;
		while (i < (lengh - 1) && list.get(i) < num) {
			i++;
		}
		return i;
	}

好了,写完了,结果是:

result:4179871

time:14711

用时将近15秒,还是挺慢的,希望能和大家交流哦。

时间: 2024-11-05 16:05:08

Project Eluer - 23的相关文章

Project Eluer - 18

Maximum path sum I Problem 18 By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23. 3 7 4 2 4 6 8 5 9 3 That is, 3 + 7 + 4 + 9 = 23. Find the maximum total from top t

Project Eluer - 17

Number letter counts Problem 17 If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total. If all the numbers from 1 to 1000 (one thousand) inclusive were written out in w

Project Eluer - 21

Amicable numbers Problem 21 Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n). If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numb

Project Eluer - 22

题目: Names scores Problem 22 Using names.txt (right click and 'Save Link/Target As...'), a 46K text file containing over five-thousand first names, begin by sorting it into alphabetical order. Then working out the alphabetical value for each name, mul

PE1 Multiples of 3 and 5

false 7.8 磅 0 2 false false false EN-US ZH-CN X-NONE MicrosoftInternetExplorer4 /* Style Definitions */ table.MsoNormalTable {mso-style-name:普通表格; mso-tstyle-rowband-size:0; mso-tstyle-colband-size:0; mso-style-noshow:yes; mso-style-priority:99; mso-

如何用C代码生成二维码

当下因微信和支付宝等手机应用广泛使用,而基于二维码/一维条码的移动支付,也借助手机移动端席卷全国,使得越来越多的人知道有"二维码"这么一种东西. 对于普通用户而来,一般只知道将自己的二维码展示给别人,别人使用软件识别这个二维码即可完成一定的功能.比如,扫码二维码付款.扫码二维码加微信好友.扫码二维码访问网页.扫码二维码下载app等等.这些个功能,在日常行为中,已经很常见了,但作为程序猿的我们,我们怎么能不知道二维码是如何生成的呢?或者说,我要自己生成一个二维码,除了去网页上找二维码生成

如何在Visual Studio中开发自己的代码生成器插件

 Visual Studio是美国微软公司开发的一个基本完整的开发工具集,它包括了整个软件生命周期中所需要的大部分工具,如UML工具.代码管控工具.集成开发环境(IDE)等等,且所写的目标代码适用于微软支持的所有平台.可以说.NET开发人员离不开它,它可以极大的提高编写软件的效率. Visual Studio作为一个世界级开发工具,当然支持通过插件方式对其功能进行扩展,开发人员可以定制自己的插件来进一步提升Visual Studio的功能. 1 什么是Add In? 所谓的add-in就是一些被

Team Foundation Server 2013 with Update 3 Install LOG

[Info   @10:14:58.155] ====================================================================[Info   @10:14:58.163] Team Foundation Server Administration Log[Info   @10:14:58.175] Version  : 12.0.30723.0[Info   @10:14:58.175] DateTime : 10/03/2014 18:1

npm 打包 electron app 报错问题

在进行desktop打包过程中,遇到如下报错: 0 info it worked if it ends with ok 1 verbose cli [ 'C:\\Program Files\\nodejs\\node.exe', 1 verbose cli 'C:\\Users\\Administrator\\AppData\\Roaming\\npm\\node_modules\\npm\\bin\\npm-cli.js', 1 verbose cli 'start' ] 2 info usi