自然数除数的颜色

时间:2019-04-07 20:50:27

标签: c++ primes

我在学校里遇到一个人的名字,叫多雷尔(Dorel),他在生日那天收到一个数字n

他想用一种颜色为从1到n的所有自然数着色,以使他自己的所有除数都与数字具有相同的颜色。

问题要求找出可以使用的最大颜色数量。

例如,使用n = 5,您拥有:

  • 1红色
  • 2绿色
  • 3蓝色
  • 4个绿色
  • 5黄色

因此在此示例中,我们需要4种颜色。

可以找到练习here,但是它是罗马尼亚语的。

质数出现了问题,所以我在考虑一种方法来计算从1n有多少个质数,然后将其添加到所需的颜色数量中。

我尝试了复杂的解决方案,例如实施Miller-Rabin素数测试和Eratosthenes,但是对于网站上的自动化测试而言,它仍然太慢。

我是否缺少某些东西,或者解决此问题的唯一方法是找到1到n之间的每个素数?

我在Wikipedia示例中实现的Eratosthenes:

/* https://en.wikipedia.org/wiki/Sieve_of_Eratosthenes#Overview */
void prime(uint n) {
  if (n < 1) return;

  /* Create a list of consecutive integers from 2 through n: (2, 3, 4, ..., n). */
  uint size = n - 1;
  uint *list = (uint *)malloc(sizeof(uint) * size);
  for (uint i = 0; i < size; i++) {
    list[i] = i + 2;
  }

  /* Initially, let p equal 2, the smallest prime number. */
  uint p = 2;

  uint c = 1;

  while (c < n) {
    /*
     * Enumerate the multiples of p by counting in increments of p from 2p to n,
     * and mark them in the list (these will be 2p, 3p, 4p, ...; the p itself should not be marked).
     */
    for (uint i = c; i < size; i++) {
      if (list[i] % p == 0) {
        list[i] = 0;
      }
    }

    /*
     * Find the first number greater than p in the list that is not marked.
     * If there was no such number, stop.
     * Otherwise, let p now equal this new number (which is the next prime).
     */
    while (c < n) {
      if (list[c] > p) {
        p = list[c++];
        break;
      }
      c++;
    }
  }

  /* the numbers remaining not marked in the list are all the primes below n */
  for (uint i = 0; i < size; i++) {
    if (list[i] != 0) {
      printf("%d ", list[i]);
    }
  }
  printf("\n\n");
}

2 个答案:

答案 0 :(得分:1)

问题是您一次又一次地对单个数字使用算法。如果要检查很多数字,可以重复使用很多工作。

我会做这样的事情:

bool * calculatePrimeArray(int n) 
{
    bool * ret = malloc(n*sizeof(*ret)+1);
    for(int i=0; i<n*sizeof(*ret)+1; i++) ret[i]=true;
    ret[0]=ret[1]=false;
    int cur = 2;
    while(cur < n/2+1) {
        if(ret[cur])
            for(int i=cur*2; i<n; i+=cur) ret[i] = 0;
        cur++;
    }
    return ret;
}

这将返回布尔数组ret,其中ret [i]指示i是否为素数。

如果要使其对缓存更友好,可以使用位域而不是布尔变量。

答案 1 :(得分:0)

Using @Bromax answer I made it work and it scores 100 on all tests on the website:

#include <cstdlib>
#include <iostream>
#include <cmath>

#define PRIME 0
#define NOT_PRIME 1

bool *primes(int n) {
  bool *ret = (bool *)calloc(n + 1, sizeof(bool));
  ret[0] = ret[1] = NOT_PRIME;
  uint cur = 2;
  while (cur <= sqrt(n)) {
    if (ret[cur] == PRIME) {
      for (uint i = cur * cur; i <= n; i += cur) {
        ret[i] = NOT_PRIME;
      }
    }
    cur++;
  }
  return ret;
}

int main() {
  FILE *input = NULL;
  FILE *output = NULL;

  input = fopen("primcolor.in", "r");

  uint n;

  fscanf(input, "%d", &n);

  if (n < 1 || n > 50000000) {
    fclose(input);
    return 1;
  }

  output = fopen("primcolor.out", "w");

  if (n <= 3) {
    fprintf(output, "%d\n", n);
    fclose(input);
    fclose(output);
    return 0;
  }

  uint colors = 2;

  bool *a = primes(n);

  for (uint i = (n / 2 + 1); i <= n; i++) {
    if (a[i] == PRIME) {
      colors++;
    }
  }

  fprintf(output, "%d\n", colors);

  fclose(input);
  fclose(output);

  return 0;
}

As suggested, the fastest approach is to make an array that is used as cache for all numbers from 0 to n