简体   繁体   English

寻找汉明数 - 不是代码或距离

[英]Finding Hamming Numbers - not code or distance

I'm currently learning C++.我目前正在学习 C++。

I am looking for Hamming numbers ( numbers whose prime divisors are less or equal to 5).我要找海明号码号码,其素因子是小于或等于5)。

When I input a number n , the program should output the n -th Hamming number.当我输入一个数字n 时,程序应该输出第n个汉明数。

Following numbers are input, and output:以下数字是输入和输出:

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 ...

Finding Hamming numbers looks easy, but increasing the input number increases run time cost exponentially.查找汉明数看起来很容易,但增加输入数会成倍增加运行时间成本。

If I input over 1000 , it almost costs over 1 second, and over 1200 , it almost costs over 5 seconds.如果我输入超过1000 ,它几乎花费超过1秒,超过1200 ,它几乎花费超过5秒。

This is the code I wrote:这是我写的代码:

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

So I would like to know how I can find the answer faster.所以我想知道如何更快地找到答案。 This algorithm doesn't seem to be very good.这个算法好像不太好。

Thanks in advance.提前致谢。

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.您可以使用自下而上的方法:从 1 开始,然后将其与 2、3 和 5 递归地相乘,以使所有汉明数达到某个限制。 You have to take care of duplicates, because you can get to 6 by way of 2·3 and 3·2.您必须处理重复项,因为您可以通过 2·3 和 3·2 达到 6。 A set can take care of that.一个集合可以解决这个问题。

The code below will generate all hamming numbers that fit into a 32-bit unsigned int.下面的代码将生成适合 32 位无符号整数的所有汉明数。 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:每个汉明数都可以写成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;
}

The strange break syntax for the loops is required, because we have to check the size before the overflow.循环需要奇怪的break语法,因为我们必须在溢出之前检查大小。 If umax*5 were guananteed not to overflow, these conditions could be written in the condition part of the loop.如果保证 umax umax*5不会溢出,则可以将这些条件写入循环的条件部分。

The code examples in the Rosetta Code link Koshinae posted use similar strategies, but I'm surprised how lengthy some of them are. Koshinae 发布的 Rosetta Code 链接中的代码示例使用了类似的策略,但我很惊讶其中一些代码的长度如此之长。

In this link you can find two different solutions for finding the nth hamming number.在此链接中,您可以找到用于查找第n 个汉明数的两种不同解决方案。 The second method is the optimized one which can get the result in a few seconds.第二种方法是优化的方法,可以在几秒钟内得到结果。

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM