如果您想检查一个特定数字是否是汉明数,您的代码很好。当你想建立一个汉明数列表时,它是低效的。
您可以使用自下而上的方法:从 1 开始,然后将其与 2、3 和 5 递归地相乘,以使所有汉明数达到某个限制。您必须注意重复,因为您可以通过 2·3 和 3·2 达到 6。一套可以解决这个问题。
下面的代码将生成适合 32 位无符号整数的所有汉明数。它通过“传播”到所有汉明数来填充一个集合。然后它从该集合中构造一个排序向量,您可以使用它来查找某个索引处的汉明数:
#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;
}
即使您最终创建的汉明数超出您的需要,此代码也比您的线性方法更快。
如果您确保不构造一个数字两次,您甚至不需要一个集合。每个汉明数都可以写成h = 2^n2 + 3^n3 + 5^n5
,所以如果你找到一种方法来唯一地迭代这些,你就完成了:
#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;
}
循环的奇怪break
语法是必需的,因为我们必须在溢出之前检查大小。如果umax*5
保证不溢出,这些条件可以写在循环的条件部分。
罗塞塔代码链接 Koshinae 发布的代码示例使用了类似的策略,但我很惊讶其中一些是多么冗长。