Project Euler #5(Smallest positive number divisible by all numbers from 1 to 20): Ways to Optimize? ~Java

百般思念 提交于 2021-02-05 20:38:08

问题


Problem 5: 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?

I have solved the problem 5 of Project Euler

Here is the Java code:

 static long FindLcm(long a,long b)
 {
     long lcm,hcf = 0;
     long i=1;
     long ger=a>b?a:b;
     while(i<ger)
     {
         if((a%i==0) && (b%i==0))
             hcf=i;
         i++;
     }
     lcm=(a*b)/hcf;
     return lcm;
 }
 static void FindMultiple()
 {
     long lcm=1;
     for(long i=2;i<=20;i++)
     {
         lcm=FindLcm(lcm,i);
     }   
     System.out.println("Lcm="+lcm);
 }

How can optimize this?


回答1:


Your FindMultiple() method is not bad,

static void FindMultiple()
{
    long lcm=1;
    for(long i=2;i<=20;i++)
    {
        lcm=FindLcm(lcm,i);
    }
    System.out.println("Lcm="+lcm);
}

it implements a fairly good algorithm. Your problem is that your FindLcm() contains a nasty performance bug.

static long FindLcm(long a,long b)
{
    long lcm,hcf = 0;
    long i=1;
    // This sets ger to max(a,b) - why?
    long ger=a>b?a:b;
    // This would return a wrong result if a == b
    // that never happens here, though
    while(i<ger)
    {
        if((a%i==0) && (b%i==0))
            hcf=i;
        i++;
    }
    lcm=(a*b)/hcf;
    return lcm;
}

You are looping until you reach the larger of the two arguments. Since the cumulative LCMs grow rather fast, that takes a lot of time. But the GCD (or HCF, if you prefer) of two (positive) numbers cannot be larger than the smaller of the two. So looping only until the smaller of the two arguments is reached makes the number of iterations at most 20 here, do that 19 times (for i = 2, ..., 20), it's a trivial amount of computation.

Changing to

long ger = a < b ? a : b;
while(i <= ger) {

gives me (adding timing code, not measuring the printing):

17705 nanoseconds
Lcm=232792560

So less than 20 microseconds for the computation. We can easily push that below 6 microseconds if we use the euclidean algorithm to find the greatest common divisor,

static long gcd(long a, long b) {
    while(b > 0) {
        a %= b;
        if (a == 0) return b;
        b %= a;
    }
    return a;
}

and below 5 if we directly use the GCD as

lcm *= i/gcd(lcm,i);

in FindMultiple().




回答2:


You're solution is more or less brute force which is why it's taking so long. We know that 2520 is the lcm of (1,2,...,9,10) which means two useful things: 1.) We can start checking factors at 11 and 2.) The answer is a multiple of 2520.

You're searching for the Greatest Common Divisor (gcd) of the answer and the next number in your sequence (similar to a bubble sort). You could just check to see if your current answer is divisible by the next factor and if not then add your current answer to itself until the answer is divisible by the next factor. For Example:

    static long findLCM(long a, long b) {
        long lcm = (a>b) ? a : b;
        while (lcm % b != 0) {
            lcm += a;
        }
        return lcm;
    }

Since we started with lcm = a, we know that as long as we add a's to lcm then lcm will always be divisible by a. Now, we just need to make some multiple of a divisible by b. This process should cut out many steps of first finding the gcd as well as iterating from 2 through 10.




回答3:


i did it like this, which was the easiest way i could think of. it's also a little faster than yours.

    for(int i = 190; ; i += 190) {
        if(i % 3 == 0 
                && i % 4 == 0
                && i % 6 == 0 
                && i % 7 == 0
                && i % 8 == 0 
                && i % 9 == 0
                && i % 11 == 0
                && i % 12 == 0 
                && i % 13 == 0 
                && i % 14 == 0 
                && i % 15 == 0
                && i % 16 == 0
                && i % 17 == 0
                && i % 18 == 0
                && i % 20 == 0) {
            System.out.println(i);
            break;
        }
    }



回答4:


Here are 4 different methods to obtain the result (4 different ways to obtain GCD) + the total time. All of them are based on the following observation:

              a*b
lcm(a,b) = ---------- 
            gcd(a,b)

where:

LCM = Least Common Multiple
GCD = Greatest Common Divisor

import java.lang.reflect.Method;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;

public class A {

    final static int N = 20;

    static Map<Integer, String> messages = new HashMap<>();

    static {
        messages.put(0, "Euler - difference");
        messages.put(1, "modulo - recursive");
        messages.put(2, "modulo - iterative");
        messages.put(3, "BigInteger implementation");
    }

    private static long GCD0(long x, long y) {
        while (x != y) {
            if (x > y) {
                x -= y;
            } else {
                y -= x;
            }
        }
        return x;
    }

    private static long GCD1(long x, long y) {
        if (x % y == 0) {
            return y;
        }
        return GCD1(y, x % y);
    }

    private static long GCD2(long x, long y) {
        long aux;
        while (x % y != 0) {
            aux = y;
            y = x % y;
            x = aux;
        }
        return y;
    }

    private static long GCD3(long x, long y) {
        BigInteger xx = BigInteger.valueOf(x);
        BigInteger yy = BigInteger.valueOf(y);
        return xx.gcd(yy).longValue();
    }

    private static void doIt(int pos) throws Exception {

        System.out.print("\n" + messages.get(pos));
        printSpaces(25, messages.get(pos).length());

        Class cls = Class.forName("A");
        Object obj = cls.newInstance();
        Method method = cls.getDeclaredMethod("GCD" + pos, long.class,
                long.class);

        long start = System.nanoTime();

        long p = 1;
        for (int i = 2; i <= N; i++) {
            p = (p * i) / (long) method.invoke(obj, p, i);
        }
        long stop = System.nanoTime();
        System.out.println("\tTime: " + (stop - start) / 1000 + " microseconds");
        System.out.println(p);
    }

    private static void printSpaces(int total, int actualLength) {
        for (int i = 0; i < total - actualLength; i++) {
            System.out.print(" ");
        }
    }

    public static void main(String[] args) throws Exception {
        doIt(0);
        doIt(1);
        doIt(2);
        doIt(3);
    }
}

Output:

Euler - difference          Time: 137205 microseconds
232792560

modulo - recursive          Time: 1240 microseconds
232792560

modulo - iterative          Time: 1228 microseconds
232792560

BigInteger implementation   Time: 2984 microseconds
232792560

P.S.: I used reflection to call those methods easier, but you can call the method directly to obtain a better performance + a better readability.




回答5:


int i = 20;
        while (true)
        {
        if (    
                    (i % 1 == 0) &&
                    (i % 2 == 0) &&
                    (i % 3 == 0) &&
                    (i % 5 == 0) &&
                    (i % 7 == 0) &&
                    (i % 9 == 0) &&
                    (i % 11 == 0) &&
                    (i % 13 == 0) &&
                    (i % 16 == 0) &&
                    (i % 17 == 0) &&
                    (i % 19 == 0) )
            {
                break;
            }
            i += 20;
        }
S.O.P(i);



回答6:


C++ Program with minimum iteration... very much resemble to Daniel Fischer

#include<iostream>

using namespace std;

int main()
{
    int num = 20;
    long lcm = 1L;
    for (int i = 2; i <= num; i++)
    {
        int hcf = 1;
        for (int j = 2; j <= i; j++)
        {
            if (i % j == 0 && lcm % j == 0)
            {
                hcf = j;
            }
        }
        lcm = (lcm * i) / hcf;
    }
    cout << lcm << "\n";
}



回答7:


This method uses brute force, but skips as soon as a number fails instead of continuing to compare the remainders. Heck, it never checks for 20 unless 19 has passed already, which actually makes it pretty efficient.

#include<stdio.h>
int a = 1, b = 1, rem;
int main() {
    while (b < 20){
        rem = a % b;
        if (rem != 0){
            a++;
            b = 1;
        }
        b++;
        }
    printf("%d is the smallest positive number divisible by all of the numbers from 1 to 20.", a);
}



回答8:


A Non Brute Force Method

This one is instantaneous! Doesn't even take a second. Run the code to understand the logic. It's written in C

#include <stdio.h>

int main()  {

int primes[8]={2,3,5,7,11,13,17,19};
int primes_count[8]={0,0,0,0,0,0,0,0};
int i,j,num,prime_point;
int largest_num=1;

printf("\nNUM");
for(j=0;j<8;j++)
    printf("\t%d",primes[j]);

for(i=2;i<=20;i++)  {
    num=i;
    int primes_count_temp[8]={0,0,0,0,0,0,0,0};
    for(j=0;j<8;j++)    {
        while(num%primes[j]==0) {
            num=num/primes[j];
            primes_count_temp[j]++;
        }
    }
    for(j=0;j<8;j++)
        if(primes_count_temp[j]>primes_count[j])
            primes_count[j]=primes_count_temp[j];

        printf("\n %d",i);
        for(j=0;j<8;j++)
            printf("\t %d",primes_count_temp[j]);
    }
    printf("\nNET");
    for(j=0;j<8;j++)
        printf("\t%d",primes_count[j]);
    printf("\n");
    for(i=0;i<8;i++)
        while(primes_count[i])  {
            largest_num*=primes[i];
            primes_count[i]--;
        }

        printf("The answer is %d \n",largest_num);
        return 0;
    }

Now if a number is divisible by X it will be divisible by its prime factors also. So if a number is divisible by 20 it will be divisible by its prime factors. And there are 8 prime factors under 20. I take each number under 20 and find its prime factors, also see the power of the prime factor and keep a count of the highest power.

Once you're done. Multiply all the prime factors raised to their highest power.




回答9:


my Solution for this in python. this is so simple and use pure Math rules

get the Least Common Multiple

def getLCM (x, y):
  return x*y/getGCD(x,y)

get the Greatest Common Divisor

def getGCD(a,b):
   while(True):
      if(a%b != 0):
          temp = b
          b = a%b
          a = temp
      else:
          return b
          break

Find the Least Common Multiple of, LCM of prev two numbers and next number in list.

LCM(LCM of prev two numbers,next number in list)

num_list = list(range(1,21))
finalLCM = 1
for i in num_list:
  finalLCM = getLCM(finalLCM,i)
print(finalLCM)

Full Python Code

def getLCM (x, y):
return x*y/getGCD(x,y)

def getGCD(a,b):
  while(True):
      if(a%b != 0):
         temp = b
         b = a%b
         a = temp
      else:
         return b
         break

num_list = list(range(1,21))
finalLCM = 1

for i in num_list:
  finalLCM = getLCM(finalLCM,i)
print(finalLCM)



回答10:


we have create an array which was common divisible eg: if any number is divisible by 20 then no need to divisible by 2,4,5,10

<?php
$chk=20;

$div=array(11,12,13,14,15,16,17,18,19,20);
for($number=1;1;$number++){
    $chk=$number;
    foreach($div as $value){        
        if($number%$value!=0){
            $chk=0;
            $number+=$value;
            break;
        }
    }
    if($chk!=0){break;}
}
echo $chk;
?>



回答11:


#Small Python code, you should have some Math knowledge

k=0
for j in range(233000000,33300000,-1):
# by intuition we know any number having 11,13,17,19,16,9,5 will be big so 33300000

  for i in range(2,21):
    if j%i==0:
      k=k+1
    else:
      k=0   
    if k==19:  #k=19 means it was evenly divided by all numbers from 2 to 20
      print(j) 


来源:https://stackoverflow.com/questions/13719768/project-euler-5smallest-positive-number-divisible-by-all-numbers-from-1-to-20

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!