欧拉计划
欧拉计划
Problem 1Multiples of 3 and 5
1.题干
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.q
2.求解
/*3或5的倍数
在小于10的自然数中,或的倍数有3、5、6和9,这些数之和是23。
求小于1000的自然数中所有3或5的倍数之和。
* */
public class _01Multiples_of_3_and_5 {
public static void main(String[] args) {
// System.out.println(countSum(10));
System.out.println(countSum(1000));
int res = getSum(1000);
System.out.println(res);
int res2 = sumDivisibleBy(3,999)+sumDivisibleBy(5,999)-sumDivisibleBy(15,999);
System.out.println(res2);
}
//暴力破解1
public static int countSum(int n){
int sum = 0;
for (int i = 3; i < n; i++) {
if(i%3 == 0){
sum=sum+i;
}
if(i%5 ==0){
sum=sum+i;
if (i%3 == 0){
sum=sum-i;
}
}
}
return sum;
}
// 暴力破解2
public static int getSum(int n){
int sum = 0;
for (int i = 0; i < n; i++) {
if (i%3 == 0||i%5==0){
sum = sum + i;
}
}
return sum;
}
//观察规律可以看出来,单独计算1000%3的+1000%5的,这样我们会计算两遍能整除15的,所以需要减掉
//3+6+9+12+...+996+999 = 3*(1+2+3+...+333) = 3*1/2*(333+1)*333
public static int sumDivisibleBy(int n,int target){
int p = target / n;
return n*(p*(p+1))/2;
}
}
答案 = {所有三的倍数的数的和} + {所有5的倍数的数的和} - {所有15的倍数的数的和}
3.总结
1.等差数列的前n项和
Problem 2Even Fibonacci numbers
1.题干
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1,2,3,5,8,13,21,34,55,89,…
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
偶斐波那契数
斐波那契数列中的每一项都是前两项的和。由1和2开始生成的斐波那契数列的前10项为:
1,2,3,5,8,13,21,34,55,89,…
考虑该斐波那契数列中不超过四百万的项,求其中为偶数的项之和。
2.求解
1.直接求解:
// 1.直接法:
public static void solution1() {
int limit = 4000000;
int sum = 0;
int a = 1;
int b = 1;
int temp = 0;
while (b < limit) {
if (b % 2 == 0) {
sum = sum + b;
}
temp = a + b;
a = b;
b = temp;
}
System.out.println(sum);
}
2.观察规律
观察一下,发现这里斐波那契数列中偶数项都是三的倍数
所以可以直接每三个加一次
//2.每三项加一下
public static void solution2() {
int limit = 4000000;
int a = 1;
int b = 1;
int c = a + b;
int sum = 0;
while (c < limit) {
sum = sum + c;
a = b + c;
b = c + a;
c = a + b;
}
System.out.println(sum);
}
3.这里还有另外一种规律:
如果我们只写偶数:
可以看出他们似乎遵守一个递归关系
E(n) = 4*E(n-1)+E(n-2)
我们可以证明斐波那契数列遵循F(n) = 4F(n-3)+F(n-6)
E(n) = 4*En+E(n-2),E1 = 2,E2 = 8
//3.F(n) = 4F(n-3)+F(n-6)
public static void solution3() {
int limit = 4000000;
int a = 2;
int b = 8;
int c = 4*a + b;
int sum = 0;
while (c < limit) {
sum = sum +c ;
a = b;
b = c;
}
System.out.println(sum);
}
3.总结:
斐波那契数列求解:
1.递归求解
// 斐波那契数列:除了第一项和第二项,所有的数列的值都是前一项和前一项的前一项的加和 1,1,2,3,5,8,13
/*
* 求第n项斐波那契数的值(递归法)
* */
public static int f(int n) {
if (n < 1) {
return 0;
} else if (n == 1 || n == 2) {
return 1;
} else {
return f(n - 1) + f(n - 2);
}
}
2.动态规划
对于 fib(10000),我们从 fib(1), fib(2), fib(3) ...... fib(10000),
从最小的值开始求解,并且把每次求解的值保存在“记忆”(可以是一个数组,下标正好用来对应 n 的解答值)里,下面的值都由记忆中的值直接得出。
这样等到算到 10000 的时候,我们想要求解的值自然也就得到了,直接从记忆[10000]
里取到值返回即可。
那么这种接法其实只需要一个 for 循环,而不需要任何递归的逻辑。
/*
* 求第n项斐波那契数的值(动态规划法法)
* */
public static int f2(int n) {
int[] dp = new int[n + 1];
//初始条件
dp[0] = 0;
dp[1] = 1;
//从2开始,不断地用之前记忆的值
//填充数组
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
动态规划:
recursion(递归) + memoization(记忆化)
递归有些值他会重复计算,会导致时间复杂度是指数级的。
记忆化就是在递归的基础上,我们记住或者缓存住一些数据
Problem 3Largest prime factor
1.题干
The prime factors of 13195 are 5, 7, 13 and 29.
What is the largest prime factor of the number 600851475143?
2.求解
首先给出百度百科中质因数的一些例子:
- 1没有质因子。
- 5只有1个质因子,5本身。(5是质数。)
- 6的质因子是2和3。(6 = 2 × 3)
- 2、4、8、16等只有1个质因子:2(2是质数,4 = 2,8 = 2,如此类推。)
- 10有2个质因子:2和5。(10 = 2 × 5)
在我们理解了质因数的定义后求质因数的算法如下:
(1) i = 2,如果i < n,并且n能被K整除,则 n = n/k;
(2)如果n不能被i整除,则k = k+1,继续执行步骤1
(3)直到k = n,循环结束
我的实现Java代码:
因为一个数的任何质因数都小于这个数,如果我们从2开始,然后按比例放大它,我们可以假设第一个模数为0的数是这个数的质因数。
然后,我们可以用原始数除以它的第一个质因数,留下一个更小的数,然后检查它是否还能被相同的因数整除。当它停止时,我们增加数字,直到找到下一个因子,以此类推。
最后,退出条件是当我们将数字除以它本身时,这意味着它是最后一个质数因子。
通过这样做,我们只输出最后一个因子
public static void solution4() {
Long num = 600851475143L;
Long aux = 2L;
while (num != 1) {
if (num % aux == 0) {
num /= aux;
} else {
aux += 1;
}
}
System.out.println(aux);
}
3.总结:
1.输出Map的方法
HashMap hm = new HashMap();
hm.put(1, "chen");
hm.put(2, "chen");
hm.put(3, "zhang");
hm.put(4, "wang");
hm.put(2, "sun");
//1、
System.out.println(hm);//默认调用toString
System.out.println("\n----------------------");
//2、 低效
Set keys = hm.keySet();
for (Object key : keys) {
System.out.print(key+"="+hm.get(key));
}
System.out.println("\n----------------------");
//3\*** 高效
Set<Map.Entry> ms =hm.entrySet();
for (Map.Entry entry : ms) {
System.out.print(entry.getKey()+"="+entry.getValue());
}
2.质数和合数:
一、性质不同
1、质数:是在大于1的自然数中,除了1和它本身以外不再有其他因数。
2、合数:是自然数中除了能被1和本身整除外,还能被其他数(0除外)整除的数。
二、特点不同
1、质数:质数的个数是无穷的;在一个大于1的数a和它的2倍之间(即区间(a,
2a]中)必存在至少一个素数。
2、合数:所有大于2的偶数都是合数;所有大于5的奇数中,个位为5的都是合数;除0以外,所有个位为0的自然数都是合数;所有个位为4,6,8的自然数都是合数。
扩展资料:
质数的特点:
(1)质数p的约数只有两个:1和p。
(2)初等数学基本定理:任一大于1的自然数,要么本身是质数,要么可以分解为几个质数之积,且这种分解是唯一的。
(3)若n为大于或等于2的正整数,在n到n!之间至少有一个质数。
3.判断素数:
//判断是否是素数
public static boolean isPrime(int n) {
boolean flag = true;
if (n < 2) {
return false;
}
for (int i = 2; i < Math.sqrt(n); i++) {
if (n % i == 0) {
return false;
}
}
return flag;
}
Problem 4Largest palindrome product
1、题干
A palindromic number reads the same both ways. The largest palindrome made from the product of two 2-digit numbers is 9009=91×99.
Find the largest palindrome made from the product of two 3-digit numbers.
最大回文乘积
回文数就是从前往后读和从后往前读都一样的数。由两个2位数相乘得到的最大的回文数是 9009=91×99。
求由两个3位数相乘得到的最大的回文数。
2.求解
官方直接求解1:
//官方解答:
//1.求反
public static int reverse(int n){
int reversed = 0;
while (n>0){
reversed = 10*reversed+n%10;
// System.out.println("reversed = "+reversed);
n = n/10;
// System.out.println("n = "+n);
}
return reversed;
}
//2.判断是否是回文
public static boolean isPalindrome(int n){
if (n == reverse(n)) {
return true;
}
return false;
}
//3.求解
public static void solution2(){
int largestPalindrome = 0;
int product = 0;
//遍历找出最大值
for (int a = 100; a < 1000; a++ ){
for (int b = 100; b < 1000; b++ ){
product = a * b;
if (product > largestPalindrome && isPalindrome(product)) {
largestPalindrome = product;
}
}
}
System.out.println(largestPalindrome);
}
对于这个问题这已经够快了,但是还可以改进。这个方法会重复检查许多数字多次,比如说69696在 a = 132 或者 b= 528的时候或者当 a = 528或者 b = 132的时候已经被检查过了。为了防止这种重复检查,我们可以约定 a<=b;粗略的可以减少一半的计算。
这里Now b=a instead of 100
//3.求解
public static void solution2(){
int largestPalindrome = 0;
int product = 0;
//遍历找出最大值
for (int a = 100; a < 1000; a++ ){
for (int b = a; b < 1000; b++ ){//Now b=a instead of 100
product = a * b;
if (product > largestPalindrome && isPalindrome(product)) {
largestPalindrome = product;
}
}
}
System.out.println(largestPalindrome);
}
下一步我们应该考虑从999计数,而不是从100开始计数。
这样可以让程序更早的找到最大值。
如果乘积比我们现在能找到的最大回文数都小,我们可以跳过检查a和b是否乘积是回文数。
public static void solution3(){
int max = 0;
for (int a = 999;a>=100;a--){
for (int b = 999; b >=a ; b--) {
if (a*b<=max){
break;//因为这时太小了不用判断了
}
if (isPalindrome(a*b)){
max = a*b;
}
}
}
System.out.println(max);
}
我们可以通过进一步分析进一步提升效率,我们可以把这个P 的每一位设为 x , y ,z;
P肯定是一个至少六位数长的数字。
P = XYZZYX
P = 100000*x+10000y+1000z+100z+10y+x
= 100001x + 10010y+ 1100z
= 11(9091 +910 +100)
因为 11 是一个指数,并且a或者b之间肯定有有一个11的质因子,所以如果我们知道a如果不能被11整除,那么b肯定能被11整除。使用这个信息我们可以按照b的值来检查啊
public static void solution4(){
int max = 0;
int b = 0;
int db = 0;
for (int a = 999; a >=100 ; a--) {
if(a %11==0){
b = 999;
db= 1;//那么b就不能被11整除
}else {
b = 990;//最大的能整除11并且小于999的数字
db = 11;//那么b能被11整除
}
for ( ; b >=a ; b = b-db) {
if (a*b<=max){//数比我们已经找到的回文数还小不判断
break;
}
if (isPalindrome(a*b)){
max = a*b;
}
}
}
System.out.println(max);
}
这里特别巧妙的就是更改b的检测间隔,如果a能整除11,那么b就不能整除,那么b就老老实实的按照1的间隔去检验,但是如果a不能整除11,那么b肯定就能整除11,那么就按照11的间隔去变化,减少无效项数的检验,但是要注意这里b的初始值不同,因为如果要按照11的间隔来变化的话,我们需要从990开始变化,因为990是第一个小于999的最大的能被11整除的数字。
我的暴力求解:
//暴力直接求解
public static void solution1() {
int sum = 0;
// 1.初始化二维数组求最大值
int[][] result = new int[1000][1000];
for (int i = 0; i <= 999; i++) {
for (int j = 0; j <= 999; j++) {
// 遍历所有乘积
sum = i * j;
String sumstr = Integer.toString(sum);
char[] str = sumstr.toCharArray();
//2.判断保存最大值
if (str.length == 5) {
//判断对称最小100*100 = 10000五位数字
if (str[0] == str[4] && str[1] == str[3]) {
result[i][j] = sum;
}
}
if (str.length == 6) {
//判断对称最大999*999 = 998001六位数字
if (str[0] == str[5] && str[1] == str[4] && str[2] == str[3]) {
result[i][j] = sum;
}
}
}
}
// 3.遍历数组,找到保存的最大值
int max = 0;
for (int i = 1; i <= 999; i++) {
for (int j = 1; j <= 999; j++) {
if (max < result[i][j]) {
max = result[i][j];
System.out.println(i);
System.out.println(j);
System.out.println(max);
}
}
}
}
3.总结:
1.求回文
public static int reverse(int n){
int reversed = 0;
while (n>0){
reversed = 10*reversed+n%10;
// System.out.println("reversed = "+reversed);
n = n/10;
// System.out.println("n = "+n);
}
return reversed;
}
2.判断回文,因为这个题肯定是6位数字,所以直接判断求回文之前和之后的值就可以了
如果用这种方法,是奇数个数字需要reversed最后会比x高一位,只需要reversed / 10 == x即可。 reversed / 10 == x。
比如x = 101,reversed = 0*10+101%10 = 1
x = 10,reversed = 1*10 + 10%10 = 0;
x = 0;
reversed= 10;
原来的数字x = 101/10 = 10 判断与reverse的是否相等。
/*
* 接着如何通过翻转后的数字来判断回文呢?
* 对于偶数位原数字,判断reversed == x即可。
* 对于奇数数字,reversed最后会比x高一位,只需要reversed / 10 == x即可。
* 因为中间的一位数字只有一个,不会影响回文序列。
* */
public static boolean isReverse(int x){
int reversed = 0;
while (reversed < x) {
// 具体做法是,不断地对原数字x进行取余,得到的结果保存在变量reversed中。
// 当在reversed < x时,继续循环,reversed * 10作为高一位,同时继续对x取余,加上余数作为低位。
// 同时对x / 10。
reversed = reversed * 10 + x % 10;
System.out.println("reversed"+reversed);
x /= 10;
System.out.println("x"+x);
}
return reversed == x || reversed / 10 == x;
}
分析:
法1:如果将整数转换为字符串来解决这个问题,会非常简单,只需要对转换后的字符串进行一次遍历就可以了。而且只需要遍历一半的元素。
法2:如果根据进阶的要求。我们可以对数字进行一半的翻转。我们求取数字的后一半的翻转。具体做法是,不断地对原数字x进行取余,得到的结果保存在变量reversed中。当在reversed < x时,继续循环,reversed * 10作为高一位,同时继续对x取余,加上余数作为低位。同时对x / 10。
例如:对121进行上述操作,
-
reversed = 1
x = 12
-
reversed = 1 * 10 + 2
x = 1
注意事项:
有一些数我们是可以直接判断不能构成回文序列的。
- 负数,由于存在负号。
- 个位为0,且不为0的数。高位无法存在0。
Problem 5Smallest multiple
1.题干
2520 is the smallest number that can be divided by each of the numbers from 1 to 10 without any remainder.
What is the smallest positive number that is evenly divisible by all of the numbers from 1 to 20?
最小公倍数
2520是最小的能够被1到10整除的数。
最小的能够被1到20整除的正数是多少?
2.求解
1.不需要任何编程就可以解决,直接从1×2...×19×20
2.暴力求解:
public static void solution1() {
Long num = 1L;
while (!((num % 1 == 0) && (num % 2 == 0) && (num % 3 == 0) & (num % 4 == 0) && (num % 5 == 0) && (num % 6 == 0) && (num % 7 == 0) && (num % 8 == 0) && (num % 9 == 0) && (num % 10 == 0) && (num % 11 == 0) && (num % 12 == 0) && (num % 13 == 0) && (num % 14 == 0) && (num % 15 == 0) && (num % 16 == 0) && (num % 17 == 0) && (num % 18 == 0) && (num % 19 == 0) && (num % 20 == 0))) {
num = num + 1;
}
System.out.println(num);
}
因为N是能被从2到k的所有整数整除的最小数,具有这个性质的最小值N,我们必须保证在质因数分解时只包含绝对必要的素因子。
比如k的三种情况:
k = 2; N = 2
k =3 ;N = 6
k = 4;N = 2*3*2 = 12
我们可以看到,当k = 4时,我们不需要计算2*3*4,因为有一个2在里面4 = 2*2的质因数分解已经包括在内。
如果我们现在考虑下一个
两种情况:
k = 5, N = 2*2*3*5 = 60
k = 6, N = 2*2*3*5 = 60
我们可以看到,对于k = 5和k = 6, N = 60,因为如果N包含因子2,3它已经包含了能被6整除的所有必要条件。
将这一原则应用于k = 20的情况:
N = 2 * 3 * 2 * 5 * 7 * 2 * 3 * 11 * 13 * 2 * 17 * 19 = 232792560
能被1至n整除的最小数:
1.首先筛选出[1,n]的所有素数,记为primeList[i]。
2.根据算术基本定理:“任何一个合数都可以分解为几个素数的积”,求出每一个Pn的kn值,如下所示:
-
假设Pn表示第n个素数,那么任意正整数可以通过下面的式子获得:
-
Kn 的取值:要保证最终值可以被所有含Pn约数的数整除(如下面例子,要保证Num既能被2整除,也能被4,8整除),因此公式为:primeList[i]^k<=n。
-
例如:一个整数要能被1-10的所有整数整除,那么就等同于他能被1-10之间的所有素数整除。那么此时:
3.求出各素数次幂的积,即求出Num。
Problem 6Sum square difference
The sum of the squares of the first ten natural numbers is,
12+22+…+102=385
The square of the sum of the first ten natural numbers is,
(1+2+…+10)2=552=3025
Hence the difference between the sum of the squares of the first ten natural numbers and the square of the sum is 3025−385=2640.
Find the difference between the sum of the squares of the first one hundred natural numbers and the square of the sum.
平方的和与和的平方之差
前十个自然数的平方的和是
12+22+…+102=385
前十个自然数的和的平方是
(1+2+…+10)2=552=3025
因此,前十个数的平方的和与和的平方之差是3025−385=2640。
求前一百个数的平方的和与和的平方之差。
2.求解
1.直接暴力求解:
因为100这个限制不算一个很高的上限。我们可以这样
我们都知道的我们可以求数的和用这个公式
//法一:暴力破解
public static void solution1(){
int n1= (100+1)*100/2;
int sum1 = n1*n1;
int sum2 = 0;
for (int i = 1; i <= 100; i++) {
sum2 = i*i+sum2;
System.out.println(sum2);
}
System.out.println(sum1-sum2);
}
2用平方的和:
找一个函数f(n),它对任意n都是12到n2的和
假设它是f(n) = an3 + bn2 + cn + d的形式,我们需要确定a b c d的常数。
因为我们可以证明f(0) = 0 f(1) = 1 f(2) = 5 f(3) = 14。这样就得到了四个方程
变量,即
解这个方程组,我们得到a =1/3,b = 1/2;c = 1/6; d= 0;这样就能求出来
f(n) = 1/6(2*n3+3*n2+n) = n/6*(2n+1)(n+1)
为了检验这个公式,我们算一下
f(n+1) = (n+1)/6*(2n+3)(n+2) = 1/3*n3+3/2*n2+13/6*n+1
f(n+1) = f(n) + (n+1)n
我们可以证明f是一个正确的公式,我们可以简化求平方的和
//法二:用两个公式
public static void solution2(){
int limit = 100;
int sum = limit*(limit+1)/2;
int sum2 = (2*limit+1)*(limit+1)*limit/6;
System.out.println(sum*sum-sum2);
}
3.总结
1.整数除法的漏洞
特别注意这里做除法的时候要把除数放在最后试试
int sum2 = limit/6*(2*limit+1)*(limit+1);
正确:
想保留小数,先定义一个双精度浮点型的变量double num;然后用num接收结果,需要相除的时候进行一下强转,因为这两数本来是整型数,要事先把他们变成浮点型再相除答案才正确.最后输出.
double num=0.0;
num=(double) = limit/6*(2*limit+1)*(limit+1);;
Syste,.out.println("两数相除的结果是:"+num);
Problem 710001st prime
一、题干
By listing the first six prime numbers: 2, 3, 5, 7, 11, and 13, we can see that the 6th prime is 13.
What is the 10 001st prime number?
第10001个质数
前6个质数分别是2、3、5、7、11和13。
第10 001个质数是多少?