自然数除数的颜色

Colors of natural number divisors

我有一个关于名字 Dorel 的学校问题,他的生日收到一个数字 n

他想用一种颜色给从 1 到 n 的所有自然数着色,这样他自己的一个数的所有约数都与该数具有相同的颜色。

该题要求找出最多可以使用多少种颜色。

例如,n = 5 你有:

所以在这个例子中,我们需要 4 种颜色。

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

问题出在质数上,所以我在想办法计算从1n有多少个质数,然后把它加到需要的颜色数量上。

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

我是不是遗漏了什么,或者解决这个问题的唯一方法是找到 1 到 n 之间的每个素数?

我根据维基百科示例实现埃拉托色尼:

/* 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");
}

问题在于您一遍又一遍地对单个数字使用算法。如果要查很多号,很多工作可以重用。

我会这样做:

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

这returns一个布尔数组ret,其中ret[i]表示i是否为素数。

如果你想让它对缓存更友好,你可以使用位域而不是布尔变量。

使用@Bromax 的回答,我成功了,它在网站上的所有测试中都获得了 100 分:

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

按照建议,最快的方法是制作一个数组,用作从 0n

的所有数字的缓存