【问题标题】:Finding Hamming Numbers - not code or distance寻找汉明数 - 不是代码或距离
【发布时间】:2015-05-30 23:08:37
【问题描述】:

我目前正在学习 C++。

我正在寻找Hamming numbersnumbers,其主要除数小于或等于 5)。

当我输入一个数字n时,程序应该输出第n个汉明数。

输入和输出以下数字:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 ...

1 2 3 4 5 6 8 9 10 12 15 16 18 20 24 ...

找到汉明数看起来很容易,但增加输入数会成倍增加运行时间成本。

如果我输入超过1000,它几乎花费超过1秒, 超过1200,它几乎花费超过5秒。

这是我写的代码:

while (th > 1)
{
    h++;
    x = h;

    while (x % 2 == 0)
        x /= 2;
    while (x % 3 == 0)
        x /= 3;
    while (x % 5 == 0)
        x /= 5;

    if (x == 1)
        th--;
}

所以我想知道如何更快地找到答案。 这个算法好像不是很好。

提前致谢。

【问题讨论】:

标签: c++ algorithm performance time-complexity hamming-numbers


【解决方案1】:

如果您想检查一个特定的数字是否是汉明数,您的代码是很好的。当你想建立一个汉明数列表时,它是低效的。

您可以使用自下而上的方法:从 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 不会溢出,则可以将这些条件写入循环的条件部分。

the Rosetta Code linkKoshinaeposted 中的代码示例使用了类似的策略,但我很惊讶其中一些代码的长度。

【讨论】:

    【解决方案2】:

    在这个link 中,您可以找到两种不同的解决方案来查找第 n 个 汉明数。第二种方法是优化后的方法,几秒钟就可以出结果。

    /* Function to get the nth ugly number*/
    unsigned getNthUglyNo(unsigned n) 
    { 
        unsigned ugly[n]; // To store ugly numbers 
        unsigned i2 = 0, i3 = 0, i5 = 0; 
        unsigned next_multiple_of_2 = 2; 
        unsigned next_multiple_of_3 = 3; 
        unsigned next_multiple_of_5 = 5; 
        unsigned next_ugly_no = 1; 
    
        ugly[0] = 1; 
        for (int i=1; i<n; i++) 
        { 
            next_ugly_no = min(next_multiple_of_2, 
                               min(next_multiple_of_3, 
                                   next_multiple_of_5)); 
            ugly[i] = next_ugly_no; 
            if (next_ugly_no == next_multiple_of_2) 
            { 
                i2 = i2+1; 
                next_multiple_of_2 = ugly[i2]*2; 
            } 
            if (next_ugly_no == next_multiple_of_3) 
            { 
                i3 = i3+1; 
                next_multiple_of_3 = ugly[i3]*3; 
            } 
            if (next_ugly_no == next_multiple_of_5) 
            { 
               i5 = i5+1; 
               next_multiple_of_5 = ugly[i5]*5; 
            } 
        } /*End of for loop (i=1; i<n; i++) */
    
    return next_ugly_no; 
    } 
    

    【讨论】:

      猜你喜欢
      • 2012-09-26
      • 1970-01-01
      • 2017-09-10
      • 2018-11-17
      • 1970-01-01
      • 1970-01-01
      • 2015-03-21
      • 2012-03-10
      • 2014-01-28
      相关资源
      最近更新 更多