查找多个数字的最小公倍数

时间:2019-05-22 21:47:47

标签: c optimization

该程序的目标是找到最小的数字,该数字可以除以1到20,而没有任何余数。该代码有效,但是需要33秒。我可以改善它以便更快吗?怎么样?

#include <stdio.h>

int main(){
  int number = 19, i, k;

  label:
  number++;
  k = 0;
  for (i = 1; i <= 20; i++){
    if (number%i == 0){
      k++;
    }
  }

  if (k != 20){
    goto label;
  }

  printf("%d\n", number);
  return 0;
}

4 个答案:

答案 0 :(得分:2)

#include <stdio.h>


/*  GCD returns the greatest common divisor of a and b (which must be non-zero).

    This algorithm comes from Euclid, Elements, circa 300 BCE, book (chapter)
    VII, propositions 1 and 2.
*/
static unsigned GCD(unsigned a, unsigned b)
{
    while (0 < b)
    {
        unsigned c = a % b;
        a = b;
        b = c;
    }

    return a;
}


int main(void)
{
    static const unsigned Limit = 20;

    unsigned LCM = 1;

    /*  Update LCM to the least common multiple of the LCM so far and the next
        i.  The least common multiple is obtained by multiplying the numbers
        and removing the duplicated common factors by dividing by the GCD.
    */
    for (unsigned i = 1; i <= Limit; ++i)
        LCM *= i / GCD(LCM, i);

    printf("The least common multiple of numbers from 1 to %u is %u.\n",
        Limit, LCM);
}

答案 1 :(得分:1)

更改

int number = 19 ;

int number = 0 ;

然后:

number++;

number += 20 ;

是一个明显的改进,即使它仍然是一种幼稚的蛮力方法,也会产生重大影响。

在onlinegdb.com上,您的算法运行了102秒,而此更改运行的时间不到一秒钟,并且产生了相同的答案。

评论中建议的素数初始值将提供进一步的改进。

答案 2 :(得分:1)

您需要将所有最小公倍数相乘,但忽略可以相乘以得到其他任何数的数。这表示将所有小于N的素数相乘,并且每个素数均提高到最高幂<= N。

const unsigned primes[] = {
    2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47
};

unsigned long long answer(unsigned n){ //for your example n=20
    if (n>46) return 0;  //will overflow 64 bit unsigned long long
    unsigned long long tmp, ret = 1;
    for (unsigned i = 0; primes[i]<=n;++i){ //each prime less than n
        tmp = primes[i];
        while ((tmp*primes[i])<=n) //highest power less than n
            tmp *= primes[i];
        ret *= tmp;
    }
    return ret;
}

用法:printf("%llu", answer(20));

如果我的数学/代码正确,这应该很快,并且覆盖数字最多为46。如果编译器支持无符号__int128,则可以将其修改为88。

说明: TLDR版本:所有数字都是质数,也可以乘以质数来得出。

  

要获得一组数字的最小公倍数,请破坏每个数字   乘以它的质数乘以最大数   每个素数在一起。

     

少于20个主题:   2,3,5,7,11,13,17,19

     

20岁以下的非素数:   4 = 2 * 2    6 = 2 * 3    8 = 2 * 2 * 2    9 = 3 * 3    10 = 2 * 5    12 = 2 * 2 * 3    14 = 2 * 2 * 7    15 = 3 * 5    16 = 2 * 2 * 2 * 2    18 = 2 * 3 * 3    20 = 2 * 2 * 5

     

由此我们可以看到2s的最大值为4,最大为   3的个数是2。

     

2到4th <= 20   3平方<= 20   所有力量大于剩余的1   素数大于20。

     

因此您得到:

     

2 * 2 * 2 * 2 * 3 * 3 * 5 * 7 * 11 * 13 * 17 * 19

     

如果您在电视中观看了tmp变量,将会看到什么   调试器。

     

另一个更快的原因是它避免了模数和除法   (在许多系统上价格昂贵)

答案 3 :(得分:0)

这是一种使用Sieve of Eratosthenes(大约公元前200年)而无需定义素数或除数(单个sqrt除外)的方法。

我用1标记复合材料,并用-1标记质数^ x。然后,我只需遍历从sqrt(n)到n的数字数组,然后取出剩余的素数和最大幂素数。

#include <stdio.h>
#include <math.h>

#define n 20

int main()
{
    int prime [100]={0};
    int rootN = sqrt(n);
    unsigned long long inc,oldInc;
    int i;
    for (i=2; i<rootN; i++)
    {
        if (prime[i] == 1) continue;
        //Classic Sieve
        inc = i*i;
        while (inc < n)
        {
         prime[inc] = 1;
         inc += i;
        }
        //Max power of prime
        oldInc = i;
        inc = i * i;
        while (inc < n)
        {
         prime[inc] = 1;
         oldInc=inc;
         inc *= i;
        }
        prime[oldInc] = -1;
        prime[i] = 1;

    }
    inc = 1;
    for(i=rootN; i<n; i++)
    {
        if (prime[i] == 0 || prime[i] == -1)
        {
            inc = inc * i;
        }
    }

    printf("%llu",inc);

    return 0;
}