0

I'm working on a problem that asks to generate a sequence using prime numbers 2, 3, and 5, and then displaying then nth number in the sequence. So, if I ask the program to display the 1000th number, it should display it.

I can't be using arrays or anything like that, just basic decisions and loops.

I started working on it and hit a wall... here's what I got:

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

Unfortunately, that code doesn't do what's required. It displays numbers such as 14, which includes a prime number 7.... The numbers can only be divided by the 3 specified primes (2,3,5).

I found some information that I'm trying to understand, and so far not sure how to implement it... maybe using lots of for() loops? So, it appears I have to use the concept of 2^n * 3^m * 5^k where n+m+k>0.

I guess I have to run a number through a test where it checks to see first if it's fully divisible by 2^1 * 3^0 * 5^0, then 2^0 * 3^1 * 5^0, then 2^0 * 3^0 * 5^1, and so on... Just not sure where to begin.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
B.K.
  • 9,982
  • 10
  • 73
  • 105
  • Are there any other requirements? Do you care *what* sequence it produces? – Beta Jan 24 '13 at 03:24
  • Your codes doesn't ask anything about the `nth` number and please be more specific with the requirements. – Mark Jan 24 '13 at 03:27
  • It took me less than 30 seconds to find the answer on google. – Edward Strange Jan 24 '13 at 03:30
  • I tried looking for two days now, can't find anything... ehh Maybe it's due to the fact that I'm sleep deprived (full time job and school). So the problem is this: Generate the following sequence and display the nth term in the sequence. There's nothing else mentioned. 2,3,4,5,6,8,9,10,12,15, etc..... Sequence only has Prime numbers 2,3,5 Must generate the 1500th term in less than 1 minute. – B.K. Jan 24 '13 at 03:32

4 Answers4

3

This is a famous problem, called Hamming's problem after Richard Hamming, and it's covered in the famous book A Discipline of Programming by Dijkstra. Mathematicians call these numbers (if you include 1) 5-smooth numbers, since their prime factorisations only contain primes less than or equal to 5.

What you're supposed to notice is that you can generate the numbers from each other. Here's one way to think about the problem:

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

This takes O(n log n) time to print n numbers. It's possible to do it in O(n) time using a similar algorithm, by merging lazy streams. My solution used boost::transform_iterator and boost::iterator_facade, so I wouldn't recommend that for a beginner.

Pseudonym
  • 2,571
  • 1
  • 13
  • 19
  • Unfortunately, I'm not that advance in c++ yet. I have to be able to accomplish this program without using arrays or creating my own functions (I do know how to create my own functions... but I don't think one would be necessary). – B.K. Jan 24 '13 at 04:07
  • you can just as easily do O(n) with arrays as with lazy streams -- even easier. Instead of inserting the three multiples always, as you do, insert only one of them - the smallest. Maintain three back-pointers (indices) into the array you're building (instead of overproducing it as you do). That's it. The size of the array portion's in use is the same as your set's: it's O(n^(2/3)). So you need a deque with O(1) `at`, `pop_front`, `push_back` for this ; or roll your own as list of arrays (growing in size as n^(2/3)) ; or just ignore the space and use one flat array. Dijkstra wrote it that way. – Will Ness Jan 01 '19 at 13:19
0

This code will do it. Breaking a problem down into smaller problems is often a good plan.

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

Now you only have to write this function.

bool isNotDivisibleByAPrimeGreaterThan5( unsigned int i ) {
  // return true if i is not divisable by a prime greater than 5.
}
Drew Dormann
  • 59,987
  • 13
  • 123
  • 180
  • Interestingly enough, what I was writing right now (reworking the code) looks very much like yours, but I'm obviously missing the prime part. Except I was using counter as the answer as well. – B.K. Jan 24 '13 at 03:58
  • Excellent. Have some coffee and see if you can implement the function that I left empty for you. It should be easier since it's only investigating one, specific int. Try making it `isNotDivisibleByAPrime` first. Then see if you can modify it to `isNotDivisibleByAPrimeGreaterThan5`. – Drew Dormann Jan 24 '13 at 04:01
  • See, but my problem is not that I can't put out the result, my problem is that I can't figure out how to check if it's only divisible by those 3 primes and nothing else. – B.K. Jan 24 '13 at 04:13
  • @user2006048 can you figure out how to check if it's not divisible by any prime? I'm still only breaking down the problem into smaller problems. – Drew Dormann Jan 24 '13 at 04:15
  • Here I figured a way to check which primes then number can be divided by: http://codepad.org/hK0W8NsA – B.K. Jan 24 '13 at 05:18
  • 1
    Drew, thank you very much for your help as well. It seems you were heading in the same directions as Toms. :) – B.K. Jan 24 '13 at 08:25
  • FYI to produce *n* Hamming numbers with a testing code like this takes *O( **exp(** n^(1/3) ))* time. A price for simplicity. :) Not just for *O(1)* space - [you can have *O( **n^(5/3)** )* time](https://stackoverflow.com/a/37346440/849891) with *O(1)* space. – Will Ness Jan 01 '19 at 13:41
0
#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";
}

Once you compile the above code, it will execute in far less than 1 minute.

The downside is that it will blow the compile time recursion limit of most compilers. (this was your daily understatement)

To improve this, nth_element needs to be rewritten to do an exponential blow up search and a divide and conquer within that range. You may also have to modify the code to work with 64 bit values, as the 1500th element of the mentioned sequence might be bigger than 2^32.

Or is having it compile fast also a requirement? :)

And here is a first pass at a Hamming implementation. Not compiled yet:

#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";
};
Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • The requirement is that I have to be able to understand the code, heh and I honestly don't know what half of that means. I need to find a simple beginner way to solve it. I'll be trying to solve this for another hour and then call it a night... I've only had 2 hours of sleep in the past 48 hours. – B.K. Jan 24 '13 at 04:15
  • If that's not an abuse of templates, then I don't know what is. I can just tell this from "it will blow the compile time recursion limit of most compilers". – Joker_vD Jan 24 '13 at 06:56
  • @Joker_vD Well, the second one (Hamming) crashed [LiveWorkSpace](http://liveworkspace.org/code/1ntmeJ$3) before it blew the recursion stack. Sigh. – Yakk - Adam Nevraumont Jan 24 '13 at 15:12
  • Well, tasks like this are better handled with LISP or Haskell, in my opinion. If you multiply a Hamming number by 2,3, or 5, you'll get another Hamming number. And you can get every Hamming number in such a way, if you start from 1... so basically we just need to merge three infinite streams. It can be done with LISP in 5 minutes, and it generates 1500th element momentarily. But with C/C++... hm. I'll try it and maybe answer here a bit later. – Joker_vD Jan 24 '13 at 17:24
  • @Joker_vD Dijkstra wrote it with one extendable array. std::deque fits the job perfectly, AFAICT. testing algos are exponential BTW, but merging are linear. – Will Ness Jan 01 '19 at 13:44
-1

Check this.

#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;
}
999k
  • 6,257
  • 2
  • 29
  • 32
  • Then check in the function GetFactors itself whether each factor is CheckifPrimeGreaterThaFive. Then update – 999k Jan 24 '13 at 06:18
  • Code modified without arrays – 999k Jan 24 '13 at 06:26
  • Cool, thanks, let me look over it really quick. It looks simple, but I want to fully understand it. I'm planning on doing this as a career, so I don't want to just use code without knowing what it does, heh – B.K. Jan 24 '13 at 06:40
  • Ok. Its simple. Ask if you have any doubts. Now its not showing nth number of sequence, but showing numbers upto n. you have to modify code to work according to you requirement – 999k Jan 24 '13 at 06:49
  • I think I got it to work to where it's what I want: http://codepad.org/g0FkspaN I understand how IsPrime works, and I fully understand how CheckifPrimeGreaterThaFive works. However, I'm not understanding how GetFactors works.... – B.K. Jan 24 '13 at 07:11
  • Also, what's the purpose of FactorsCount variable in main()? – B.K. Jan 24 '13 at 07:16
  • FactorsCount from my old code. No use now. GetFactors will find all the factors of a given number by check modulo with each number upto itself. – 999k Jan 24 '13 at 07:25
  • Ah. So, I'm pretty much getting everything that code except the GetFactors() function. I'm not sure what's is being used for... having a hard time understanding it. So, the user sends the parameter to the main() function, then for loop is ran to run through every number in the specified parameter. Inside that for loop is the GetFactors(), which uses two other functions to determine is the dividing number is a prime less or equal to 5. However, I'm not sure about the specifics of GetFactors(). – B.K. Jan 24 '13 at 07:31
  • Do I even need that GetFactors() function? Or is there a way to get it to work without it? Seems like it was used for that FactorsCount variable. – B.K. Jan 24 '13 at 07:45
  • I think I'm starting to get it, although, not fully, but that GetFactors() is needed to check to see if the number can be factored by 2, 3, and 5... and the CheckifPrimeGreaterThaFive is what insures that it stays as 2, 3, and 5 only – B.K. Jan 24 '13 at 07:58
  • GetFactors function returns 0 if given input (argument Num) is a number in not your series and 1 if argument Num is in your series. i am calling GetFactors with all the numbers upto 98 from Main. And print the number if function GetFactors return 1. In function GetFactors i am finding all divisors of given number first (ie for Num 14- factors are 2,7 and 14). Then i am checking whether there are primenumbers in divisors other than 2 3 5. Understand program using debug prints. Also go and get some sleep – 999k Jan 24 '13 at 08:01
  • AHH! Makes sense, I'm going to print it out tomorrow too and go through each step by pen and paper. Last question before I head off to bed :) Here's what I came up with to get the nth position to be displayed from the user: http://codepad.org/NPF7BbGZ and it works; however, is there a better way than just shoving a predefined variable with 9999999 (I just put an integer there for now). I was thinking of a way to find out if the program can predict the needed number there based on requested position... but I don't think that' possible, since calculations were not completed before that. – B.K. Jan 24 '13 at 08:21
  • I'll check your response tomorrow, but for now I'm heading off to bed, heh I appreciate your help so very very much. I've learned a lot from you tonight and I'm very thankful. – B.K. Jan 24 '13 at 08:23
  • Did you mean this http://codepad.org/E2QEdPuK Use for(unsigned int i=2; (disp_pos – 999k Jan 24 '13 at 08:52
  • My only issue now is that I can't generate 1500th position in under a minute... Takes like 10 minutes. Could be because my pc is old? – B.K. Jan 24 '13 at 22:04
  • I figured out an easier way that generated that position in 25 seconds, heh – B.K. Jan 24 '13 at 22:51