Генерация последовательности с использованием только простых чисел 2, 3 и 5, а затем отображение n-го члена (C++)

Я работаю над проблемой, которая просит создать последовательность, используя простые числа 2, 3 и 5, а затем отображая затем n-е число в последовательности. Итак, если я попрошу программу отобразить 1000-е число, она должна его отобразить.

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

Я начал работать над этим и врезался в стену... вот что я получил:

#include <iostream>

using namespace std;
int main() {
    unsigned int n=23;
    for(int i=2; i<n; i++){
        if(i%2==0){
            cout<<i<<", ";
        }else if(i%3==0){
            cout<<i<<", ";
        }else if(i%5==0){
            cout<<i<<", ";
        }
    }

    return 0;
}

К сожалению, этот код не делает того, что требуется. Он отображает такие числа, как 14, которое включает в себя простое число 7... Числа могут делиться только на 3 указанных простых числа (2,3,5).

Я нашел некоторую информацию, которую пытаюсь понять, и пока не знаю, как ее реализовать... возможно, используя множество циклов for()? Итак, похоже, я должен использовать концепцию 2^n * 3^m * 5^k, где n+m+k>0.

Я думаю, мне нужно запустить число через тест, где он сначала проверяет, полностью ли оно делится на 2 ^ 1 * 3 ^ 0 * 5 ^ 0, затем 2 ^ 0 * 3 ^ 1 * 5 ^ 0, затем 2 ^ 0 * 3^0 * 5^1 и так далее... Просто не знаю, с чего начать.


person B.K.    schedule 24.01.2013    source источник
comment
Есть ли другие требования? Вам не все равно, какая последовательность получается?   -  person Beta    schedule 24.01.2013
comment
Ваши коды ничего не говорят о номере nth, и, пожалуйста, укажите более конкретные требования.   -  person Christian Mark    schedule 24.01.2013
comment
Мне потребовалось менее 30 секунд, чтобы найти ответ в Google.   -  person Edward Strange    schedule 24.01.2013
comment
Я пытался искать уже два дня, ничего не нашел... эх Может быть, это из-за того, что я лишен сна (полный рабочий день и учеба). Итак, проблема заключается в следующем: сгенерируйте следующую последовательность и отобразите n-й член в последовательности. Больше ничего не упоминается. 2,3,4,5,6,8,9,10,12,15 и т. д. Последовательность содержит только простые числа 2,3,5 Должен сгенерировать 1500-й член менее чем за 1 минуту.   -  person B.K.    schedule 24.01.2013


Ответы (4)


Проверь это.

#include <iostream>
using namespace std;

int IsPrime(int var);
int CheckifPrimeGreaterThaFive(int Num);
int GetFactors(int Num)
{
    int i =0,j=0;
    for (i =2,j=0; i <= Num; i++)
    {
        if (Num%i == 0)
        {
           if (1 == CheckifPrimeGreaterThaFive(i))
           {
                 return 1;
              }
        }
    }
    return 0;
}

int CheckifPrimeGreaterThaFive(int Num)
{
   if ((Num != 2 && Num != 3 && Num != 5) && IsPrime(Num))
   {

           return 1;
   }

    return 0;
}

int IsPrime(int var)
{
    for (int i = 2; i <= var/2; i++)
    {
        if (var % i == 0)
           return 0;
    }
    return 1;
}


int main() {
    int n=98;
    int i, FactorsCount=0;

    for(i=2; i<n; i++)
    {
        if (0 == GetFactors(i))
        {  
           cout<<" "<<i;
        }   
    }
    return 0;
}
person 999k    schedule 24.01.2013
comment
Затем проверьте в самой функции GetFactors, является ли каждый фактор CheckifPrimeGreaterThaFive. Затем обновите - person 999k; 24.01.2013
comment
Код изменен без массивов - person 999k; 24.01.2013
comment
Круто, спасибо, позвольте мне просмотреть это очень быстро. Это выглядит просто, но я хочу полностью понять это. Я планирую сделать это своей карьерой, поэтому я не хочу просто использовать код, не зная, что он делает, хех - person B.K.; 24.01.2013
comment
Ok. Это просто. Спросите, есть ли у вас сомнения. Теперь он не показывает n-й номер последовательности, но показывает числа до n. вам нужно изменить код для работы в соответствии с вашими требованиями - person 999k; 24.01.2013
comment
Думаю, я заставил его работать там, где это нужно: codepad.org/g0FkspaN Я понимаю, как работает IsPrime. , и я полностью понимаю, как работает CheckifPrimeGreaterThaFive. Однако я не понимаю, как работает GetFactors.... - person B.K.; 24.01.2013
comment
Кроме того, какова цель переменной FactorsCount в main()? - person B.K.; 24.01.2013
comment
FactorsCount из моего старого кода. Теперь бесполезно. GetFactors найдет все множители заданного числа, проверив по модулю каждое число до самого себя. - person 999k; 24.01.2013
comment
Ах. Итак, я получаю почти весь этот код, кроме функции GetFactors(). Я не уверен, для чего он используется... мне трудно это понять. Итак, пользователь отправляет параметр в функцию main(), затем запускается цикл for для прогона каждого числа в указанном параметре. Внутри этого цикла for находится функция GetFactors(), которая использует две другие функции, чтобы определить, является ли делимое число простым числом, меньшим или равным 5. Однако я не уверен в особенностях GetFactors(). - person B.K.; 24.01.2013
comment
Нужна ли мне эта функция GetFactors()? Или есть способ заставить его работать без него? Похоже, он использовался для этой переменной FactorsCount. - person B.K.; 24.01.2013
comment
Я думаю, что начинаю понимать, хотя и не полностью, но что GetFactors() необходим, чтобы проверить, можно ли разложить число на 2, 3 и 5... и CheckifPrimeGreaterThaFive - это то, что гарантирует, что оно остается только как 2, 3 и 5 - person B.K.; 24.01.2013
comment
Функция GetFactors возвращает 0, если данный вход (аргумент Num) является числом не из вашей серии, и 1, если аргумент Num входит в вашу серию. я звоню в GetFactors со всеми номерами до 98 из Main. И выведите число, если функция GetFactors возвращает 1. В функции GetFactors я сначала нахожу все делители данного числа (т.е. для Num 14-множители 2,7 и 14). Затем я проверяю, есть ли простые числа в делителях, отличных от 2 3 5. Поймите программу, используя отладочные отпечатки. Также иди и поспи - person 999k; 24.01.2013
comment
АХ! Логично, завтра тоже распечатаю и пройдусь по каждому шагу ручкой и бумагой. Последний вопрос, прежде чем я отправлюсь спать :) Вот что я придумал, чтобы получить n-ю позицию, которая будет отображаться от пользователя: codepad.org/NPF7BbGZ и это работает; однако есть ли лучший способ, чем просто вставить предопределенную переменную с 9999999 (пока я просто поместил туда целое число). Я думал о том, как узнать, может ли программа предсказать нужное число там на основе запрошенной позиции ... но я не думаю, что это возможно, поскольку до этого расчеты не были завершены. - person B.K.; 24.01.2013
comment
Я проверю ваш ответ завтра, а пока я отправляюсь спать, хех, я очень ценю вашу помощь. Я многому научился у вас сегодня вечером, и я очень благодарен. - person B.K.; 24.01.2013
comment
Вы имели в виду это codepad.org/E2QEdPuK Use for(unsigned int i=2; (disp_pos‹n); i++){ в вашем основном - person 999k; 24.01.2013
comment
Вы, сэр, удивительны! Это точно! Большое вам спасибо! - person B.K.; 25.01.2013
comment
Моя единственная проблема сейчас в том, что я не могу сгенерировать 1500-ю позицию менее чем за минуту... Занимает около 10 минут. Может быть, потому что мой компьютер старый? - person B.K.; 25.01.2013
comment
Я нашел более простой способ, который генерировал эту позицию за 25 секунд, хех - person B.K.; 25.01.2013

Это известная проблема, названная в честь Ричарда Хэмминга проблемой Хэмминга. Она описана в известной книге Дейкстры A Discipline of Programming. Математики называют эти числа (если вы включаете 1) 5-гладкими числами, поскольку их простые факторизации содержат только простые числа, меньшие или равные 5.

Что вы должны заметить, так это то, что вы можете генерировать числа друг из друга. Вот один из способов подумать о проблеме:

#include <set>
#include <iostream>

using namespace std;

int
main()
{
    const unsigned n = 23;

    set<unsigned> s;
    s.insert(2);
    s.insert(3);
    s.insert(5);

    for (unsigned i = 0; i < n; ++i)
    {
        // This returns the smallest element in the set.
        unsigned x = *s.begin();
        cout << x << '\n';

        // Erase the smallest element.
        s.erase(s.begin());

        // Insert the multiples of x.
        s.insert(2*x);
        s.insert(3*x);
        s.insert(5*x);
    }
}

Для печати n чисел требуется время O(n log n). Это можно сделать за O(n) раз, используя аналогичный алгоритм, объединяя ленивые потоки. В моем решении использовались boost::transform_iterator и boost::iterator_facade, поэтому я бы не рекомендовал его новичку.

person Pseudonym    schedule 24.01.2013
comment
К сожалению, я еще не настолько продвинулся в С++. Я должен иметь возможность выполнить эту программу без использования массивов или создания собственных функций (я знаю, как создавать свои собственные функции... но я не думаю, что это было бы необходимо). - person B.K.; 24.01.2013
comment
вы можете так же легко сделать O(n) с массивами, как и с ленивыми потоками - даже проще. Вместо того, чтобы всегда вставлять три кратных, как это делаете вы, вставляйте только одно из них — наименьшее. Поддерживайте три обратных указателя (индекса) в массиве, который вы строите (вместо того, чтобы перепроизводить его, как вы это делаете). Вот и все. Размер используемой части массива такой же, как у вашего набора: это O (n ^ (2/3)). Итак, вам нужна двухсторонняя очередь с O(1) at, pop_front, push_back для этого; или сверните свой собственный список массивов (растущий в размере n ^ (2/3)); или просто игнорируйте пробел и используйте один плоский массив. Дийкстра так написал. - person Will Ness; 01.01.2019

Этот код сделает это. Разбиение проблемы на более мелкие часто является хорошим планом.

int main() {
    unsigned int n=23;

    unsigned int counter=0;
    unsigned int answer;
    for ( answer = 2; counter < n; ++answer ) {
        if ( isNotDivisibleByAPrimeGreaterThan5( i ) {
           ++counter;
        }
    }
    cout << answer;
    return 0;
}

Теперь вам нужно только написать эту функцию.

bool isNotDivisibleByAPrimeGreaterThan5( unsigned int i ) {
  // return true if i is not divisable by a prime greater than 5.
}
person Drew Dormann    schedule 24.01.2013
comment
Интересно, что то, что я писал прямо сейчас (переделывая код), очень похоже на ваше, но я явно упускаю главную часть. За исключением того, что я также использовал счетчик в качестве ответа. - person B.K.; 24.01.2013
comment
Превосходно. Выпейте кофе и посмотрите, сможете ли вы реализовать функцию, которую я оставил для вас пустой. Это должно быть проще, так как он исследует только один конкретный int. Попробуйте сначала сделать это isNotDivisibleByAPrime. Затем посмотрите, сможете ли вы изменить его на isNotDivisibleByAPrimeGreaterThan5. - person Drew Dormann; 24.01.2013
comment
Видите ли, но моя проблема не в том, что я не могу вывести результат, моя проблема в том, что я не могу понять, как проверить, делится ли он только на эти 3 простых числа и ни на что другое. - person B.K.; 24.01.2013
comment
@user2006048 user2006048 можете ли вы понять, как проверить, не делится ли оно ни на какое простое число? Я все еще только разбиваю проблему на более мелкие проблемы. - person Drew Dormann; 24.01.2013
comment
Здесь я нашел способ проверить, на какие простые числа можно разделить число: codepad.org/hK0W8NsA - person B.K.; 24.01.2013
comment
Дрю, спасибо большое за помощь. Кажется, вы шли в том же направлении, что и Томс. :) - person B.K.; 24.01.2013
comment
К вашему сведению, для получения n чисел Хэмминга с помощью такого тестового кода требуется O( exp( n^(1/3)) времени. Цена за простоту. :) Не только для пространства O(1) - у вас может быть O(n ^(5/3) ) время с пространством O(1). - person Will Ness; 01.01.2019

#include <type_traits>
#include <utility>
#include <iostream>

template<int... s>
struct seq {};

template<int n, typename seq, typename=void>
struct can_be_factored_into;

template<int n, int first, int... rest>
struct can_be_factored_into< n, seq<first, rest...>, typename std::enable_if< (n > 1) && (n%first) >::type >: can_be_factored_into< n, seq<rest...> > {};

template<int n, int first, int... rest>
struct can_be_factored_into< n, seq<first, rest...>, typename std::enable_if< (n > 1) && !(n%first) >::type >: can_be_factored_into< n/first, seq<first, rest...> > {};

template<int n, int... rest>
struct can_be_factored_into< n, seq<rest...>, typename std::enable_if< n == 1 >::type: std::true_type {};

template<int n>
struct can_be_factored_into< n, seq<>, typename std::enable_if< n != 1 >::type: std::false_type {};

template<int n>
using my_test = can_be_factored_into< n, seq<2,3,5> >;

template<template<int n>class test, int cnt, int start=1, typename=void>
struct nth_element;

template<template<int n>class test, int cnt, int start>
struct nth_element<test, cnt, start, typename std::enable_if< (cnt>1)&&test<start>::value >::type >:
  nth_element<test, cnt-1, start+1 > {};

template<template<int n>class test, int cnt, int start>
struct nth_element<test, cnt, start, typename std::enable_if< (cnt==1)&&test<start>::value >::type >
  { enum { value = start }; };

template<template<int n>class test, int cnt, int start>
struct nth_element<test, cnt, start, typename std::enable_if< !test<start>::value >::type >:
  nth_element<test, cnt, start+1 > {};

int main() {
  std::cout << nth_element< my_test, 1500 >::value << "\n";
}

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

Недостатком является то, что это превысит предел рекурсии времени компиляции большинства компиляторов. (это было ваше ежедневное преуменьшение)

Чтобы улучшить это, nth_element необходимо переписать, чтобы он выполнял экспоненциальный поиск с увеличением и разделяй и властвуй в этом диапазоне. Возможно, вам также придется изменить код для работы с 64-битными значениями, так как 1500-й элемент указанной последовательности может быть больше, чем 2^32.

Или быстрая компиляция также является требованием? :)

И вот первый проход в реализации Хэмминга. Еще не скомпилировано:

#include <iostream>
#include <utility>

template<long long... s>
struct seq;

template<long long cnt, typename seq, typename=void>
struct Hamming;

template<long long cnt, long long first, long long... rest>
struct Hamming<cnt, seq<first, rest...>, typename std::enable_if< cnt == 0 >::type> {
  static const long long value = first;
};

template<long long x, typename seq>
struct prepend;
template<long long x, long long... s>
struct prepend<x, seq<s...>>
{
  typedef seq<x, s...> type;
};

template<typename s1, typename s2, typename=void>
struct merge;

template<long long begin_s1, long long... s1, long long begin_s2, long long... s2>
struct merge< seq< begin_s1, s1... >, seq< begin_s2, s2... >, typename std::enable_if< (begin_s1 < begin_s2) >::type > {
  typedef typename prepend< begin_s1, typename merge< seq< s1... >, seq< begin_s2, s2... > >::type >::type type;
};

template<long long begin_s1, long long... s1, long long begin_s2, long long... s2>
struct merge< seq< begin_s1, s1... >, seq< begin_s2, s2... >, typename std::enable_if< (begin_s1 >= begin_s2) >::type > {
  typedef typename prepend< begin_s2, typename merge< seq< begin_s1, s1... >, seq<  s2... > >::type >::type type;
};
template<long long begin_s1, long long... s1>
struct merge< seq< begin_s1, s1... >, seq<>, void > {
  typedef seq< begin_s1, s1... > type;
};
template<long long... s2>
struct merge< seq<>, seq<s2...>, void > {
  typedef seq< s2... > type;
};

template<long long cnt, long long first, long long... rest>
struct Hamming<cnt, seq<first, rest...>, typename std::enable_if< cnt != 0 >::type>:
  Hamming<cnt-1, typename merge< seq<first*2, first*3, first*5>, seq<rest...> >::type >
{};

int main() {
  std::cout << Hamming<1500, seq<1>>::value << "\n";
};
person Yakk - Adam Nevraumont    schedule 24.01.2013
comment
Требование состоит в том, чтобы я мог понимать код, хех, и я, честно говоря, не знаю, что половина из этого означает. Мне нужно найти простой способ для начинающих, чтобы решить эту проблему. Я буду пытаться решить это еще час, а затем начну ночь... Я спал всего 2 часа за последние 48 часов. - person B.K.; 24.01.2013
comment
Если это не злоупотребление шаблонами, то я не знаю, что это такое. Я могу просто сказать, что это превысит предел рекурсии времени компиляции большинства компиляторов. - person Joker_vD; 24.01.2013
comment
@Joker_vD Ну, второй (Хэмминг) разбил LiveWorkSpace до того, как он взорвал стек рекурсии. Вздох. - person Yakk - Adam Nevraumont; 24.01.2013
comment
Что ж, на мой взгляд, такие задачи лучше решаются с помощью LISP или Haskell. Если вы умножите число Хэмминга на 2,3 или 5, вы получите другое число Хэмминга. И вы можете получить каждое число Хэмминга таким образом, если вы начнете с 1... так что в основном нам просто нужно объединить три бесконечных потока. Это можно сделать с помощью LISP за 5 минут, и он моментально генерирует 1500-й элемент. Но с C/C++... хм. Я попробую и, возможно, отвечу здесь чуть позже. - person Joker_vD; 24.01.2013
comment
@Joker_vD Дейкстра написал это с помощью одного расширяемого массива. std::deque идеально подходит для этой работы, AFAICT. Алгоритмы тестирования являются экспоненциальными, кстати, но слияние линейными. - person Will Ness; 01.01.2019