我在学校里遇到一个人的名字,叫多雷尔(Dorel),他在生日那天收到一个数字n
。
他想用一种颜色为从1到n的所有自然数着色,以使他自己的所有除数都与数字具有相同的颜色。
问题要求找出可以使用的最大颜色数量。
例如,使用n = 5
,您拥有:
因此在此示例中,我们需要4种颜色。
可以找到练习here,但是它是罗马尼亚语的。
质数出现了问题,所以我在考虑一种方法来计算从1
到n
有多少个质数,然后将其添加到所需的颜色数量中。
我尝试了复杂的解决方案,例如实施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");
}
答案 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