项目Euler#5(最小的正数除以1到20之间的所有数字):优化方法? 〜Java的

时间:2012-12-05 09:19:21

标签: java optimization lcm

问题5: 2520是可以除以1到10之间的每个数字而没有任何余数的最小数字。 可以被1到20的所有数字整除的最小正数是多少?

我已经解决了Project Euler

的问题5

这是Java代码:

 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);
 }

如何优化这个?

11 个答案:

答案 0 :(得分:6)

您的FindMultiple()方法不错,

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

它实现了一个相当不错的算法。您的问题是您的FindLcm()包含令人讨厌的性能错误。

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;
}

您正在循环,直到到达两个参数的更大。由于累积LCM增长相当快,这需要花费大量时间。但是两个(正)数字的GCD(或HCF,如果你愿意)不能大于两个中的较小者。因此,只有在达到两个参数中较小的一个时才进行循环,这里迭代次数最多为20次,这样做19次(对于i = 2, ..., 20),这是一个微不足道的计算量。

更改为

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

给了我(添加时间码,而不是测量打印):

17705 nanoseconds
Lcm=232792560

计算时小于20 微秒。如果我们使用欧几里得算法找到最大公约数,我们可以很容易地将其推到6微秒以下,

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

如果我们直接使用GCD

,则低于5
lcm *= i/gcd(lcm,i);
FindMultiple()中的

答案 1 :(得分:4)

你的解决方案或多或少是蛮力,这就是为什么它需要这么长时间。我们知道2520是(1,2,...,9,10)的lcm,这意味着两个有用的东西:1。)我们可以在11和2开始检查因子。)答案是2520的倍数。< / p>

您正在搜索答案的最大公约数(gcd)和序列中的下一个数字(类似于冒泡排序)。您可以检查当前答案是否可被下一个因子整除,如果没有,则将当前答案添加到自身,直到答案可被下一个因子整除。例如:

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

由于我们从lcm = a开始,我们知道只要我们向lcm添加一个,那么lcm将始终被a整除。现在,我们只需要对b进行整除即可。这个过程应该删除许多步骤,首先找到gcd以及从2到10迭代。

答案 2 :(得分:2)

我是这样做的,这是我能想到的最简单的方式。它也比你的快一点。

    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;
        }
    }

答案 3 :(得分:2)

以下是 4种不同的方法来获得结果(获得GCD的4种不同方式)+ 总时间。所有这些都基于以下观察:

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

其中:

LCM =最不常见的多个
GCD =最大公约数

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);
    }
}

<强>输出

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。:我使用反射来更轻松地调用这些方法,但您可以直接调用该方法以获得更好的性能+更好的可读性。

答案 4 :(得分:1)

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);

答案 5 :(得分:1)

C ++程序,迭代次数最少......非常类似于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";
}

答案 6 :(得分:1)

此方法使用蛮力,但一旦数字失败就跳过,而不是继续比较余数。哎呀,除非19已经过去,否则它永远不会检查20,这实际上使它非常有效。

#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);
}

答案 7 :(得分:0)

非强力方法

这是瞬间的!甚至不需要一秒钟。运行代码以了解逻辑。它是用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;
    }

现在,如果一个数字可被X整除,它也可以被其素数因子整除。因此,如果一个数字可被20整除,它将被其素因子整除。并且在20下有8个主要因子。我将每个数字低于20并找到其主要因素,也看到素数因子的功率并保持最高功率的计数。

一旦你完成了。将所有主要因素乘以其最高权力。

答案 8 :(得分:0)

我在python中的解决方案。这很简单,使用纯数学规则

获得最低公倍数

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

找到前两个数字和列表中下一个数字的最小公倍数。

LCM(前两个数字的LCM,列表中的下一个数字)

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

完整的Python代码

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)

答案 9 :(得分:0)

我们创建了一个可以被分割的数组,例如:如果任何数字可以被20整除,那么就不需要被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;
?>

答案 10 :(得分:-1)

1. How the messages are received/consumed from two topics(one common group id, same schema both key/value) in one consumer.
Let say the consumer reads data every second. Producer1 produces 50 messages to Topic1 and Producer 2 produces 1000 messages to Topic2.
2. Is it going to read all msgs(1000+50) in one batch and process together in the workflow, OR is it going to read 50 msgs first, process them and then read 1000 msgs and process them.
3. What parameter should i use to control the number of messages being read in one batch per second.
4. Will same group id create any issue while consuming.