Your code is good if you want to check whether one particular number is a hamming number. When you want to build a list of hamming numbers, it is inefficient.
You can use a bottom-up approach: Start with 1 and then recursively multiply that with 2, 3, and 5 to get all hamming numbers up to a certain limit. You have to take care of duplicates, because you can get to 6 by way of 2·3 and 3·2. A set can take care of that.
The code below will generate all hamming numbers that fit into a 32-bit unsigned int. It fills a set by "spreading" to all hamming numbers. Then it constructs a sorted vector from the set, which you can use to find a hamming number at a certain index:
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
typedef unsigned int uint;
const uint umax = 0xffffffff;
void spread(std::set<uint> &hamming, uint n)
{
if (hamming.find(n) == hamming.end()) {
hamming.insert(n);
if (n < umax / 2) spread(hamming, n * 2);
if (n < umax / 3) spread(hamming, n * 3);
if (n < umax / 5) spread(hamming, n * 5);
}
}
int main()
{
std::set<uint> hamming;
spread(hamming, 1);
std::vector<uint> ordered(hamming.begin(), hamming.end());
for (size_t i = 0; i < ordered.size(); i++) {
std::cout << i << ' ' << ordered[i] << '\n';
}
return 0;
}
This code is faster than your linear method even if you end up creating more hamming numbers than you need.
You don't even need a set if you make sure that you don't construct a number twice. Every hamming number can be written as h = 2^n2 + 3^n3 + 5^n5
, so if you find a means to iterate through these uniquely, you're done:
#include <iostream>
#include <algorithm>
#include <set>
#include <vector>
typedef unsigned int uint;
int main()
{
const uint umax = 0xffffffff;
std::vector<uint> hamming;
for (uint k = 1;; k *= 2) {
for (uint l = k;; l *= 3) {
for (uint m = l;; m *= 5) {
hamming.push_back(m);
if (m > umax / 5) break;
}
if (l > umax / 3) break;
}
if (k > umax / 2) break;
}
std::sort(hamming.begin(), hamming.end());
for (size_t i = 0; i < hamming.size(); i++) {
std::cout << i << ' ' << hamming[i] << '\n';
}
return 0;
}
The strange break
syntax for the loops is required, because we have to check the size before the overflow. If umax*5
were guananteed not to overflow, these conditions could be written in the condition part of the loop.
The code examples in the Rosetta Code link Koshinae posted use similar strategies, but I'm surprised how lengthy some of them are.