Цвета натуральных делителей чисел

У меня есть школьная задачка о человеке по имени Дорел, который на день рождения получил число n.

Он решил покрасить все натуральные числа от 1 до n в один цвет так, чтобы все его собственные делители числа были одного цвета с этим числом.

Задача состоит в том, чтобы выяснить, какое максимальное количество цветов можно использовать.

Например, с n = 5 у вас есть:

  • 1 красный
  • 2 зеленый
  • 3 синие
  • 4 зеленый
  • 5 желтый

Итак, в этом примере нам нужно 4 цвета.

Упражнение можно найти под номером здесь, но оно на румынском языке.

Проблема возникает с простыми числами, поэтому я подумал о том, как вычислить, сколько простых чисел существует от 1 до n, а затем добавить это к количеству необходимых цветов.

Я пробовал сложные решения, такие как реализация теста простоты Миллера-Рабина и Эратосфена, но для автоматических тестов на веб-сайте это все еще слишком медленно.

Я что-то упустил или единственный способ решить эту проблему — найти каждое простое число от 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");
}

Это немного неясно. В чем проблема? Ваш код работает? Если да, то достаточно ли быстро?

klutt 07.04.2019 22:54

Я не уверен, что вопрос здесь...

Borgleader 07.04.2019 22:54

Во-первых, вы, вероятно, не хотите ceil(sqrt(n)) напрямую в качестве условия продолжения цикла for. Возможно, используемая вами stdlib не помечает их как чистые, что приведет к O(n) вызовам, вычисляющим верхнюю границу.

Cruz Jean 07.04.2019 22:55

@Broman работает, компилируется и запускается, я думаю, это неправильно, потому что некоторые тесты терпят неудачу (на веб-сайте вы можете загрузить код и протестировать его, он дает много сбоев, и большинство из них терпят неудачу, потому что он слишком медленный)

genesisxyz 07.04.2019 22:56

@Borgleader Я пытаюсь выполнить упражнение, я думаю, чтобы решить эту проблему, мне нужно найти способ подсчитать, сколько простых чисел находится между 1 и n, проблема в том, что каждый подход, который я пробовал, слишком медленный.

genesisxyz 07.04.2019 22:58

Вы искали алгоритм? Не похоже, чтобы поиск простых чисел был новой проблемой. Очень легко найти быстрый код, который это делает.

klutt 07.04.2019 23:05

Хотя считая количество простых чисел < n — это отдельная проблема от перечисление простых чисел < n, я считаю, что самый быстрый алгоритм для их подсчета — сначала перечислить их.

President James K. Polk 08.04.2019 00:49

@Broman Я выполнил поиск и реализовал также тест на простоту Миллера Рабина, но он все еще был слишком медленным для тестов, я попробую библиотеки по этой ссылке, но я не думаю, что это решение, это упражнение для 9-го класса, я думаю решение намного проще

genesisxyz 08.04.2019 01:28

Возможно это: en.m.wikipedia.org/wiki/Sieve_of_Eratosthenes

drescherjm 08.04.2019 04:51
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
10
92
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема в том, что вы используете алгоритм для одного числа снова и снова. Если вы хотите проверить много чисел, большую часть работы можно использовать повторно.

Я бы сделал что-то вроде этого:

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] указывает, является ли я простым числом или нет.

Если вы хотите сделать его более удобным для кэширования, вы можете использовать битовые поля вместо логических переменных.

Я попытался скомпилировать его, и он застрял в цикле while, g++ без каких-либо аргументов.

genesisxyz 08.04.2019 13:19

@genesisxyz Пропустил строчку. Починил это.

klutt 08.04.2019 13:21

хорошо, спасибо, я скоро представлю полное решение

genesisxyz 08.04.2019 13:27
Ответ принят как подходящий

Используя ответ @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;
}

Как было предложено, самый быстрый подход — создать массив, который используется в качестве кеша для всех чисел от 0 до n.

Другие вопросы по теме