【问题标题】:Get numbers that only divide by 2,3 and/or 5, but not by any other prime number获取仅除以 2,3 和/或 5,但不能除以任何其他素数的数字
【发布时间】:2012-09-09 04:10:30
【问题描述】:

给定一个整数 N,我必须找到前 N 个只能被 2,3 和/或 5 整除的元素,而不能被任何其他素数整除。

例如:

N = 3
Results: 2,3,4
N = 5
Results: 2,3,4,5,6

错误数 = 55..55/5 = 11..11,这是一个素数。由于 55..55 可被不同于 2,3 和 5 的素数整除,因此不算数。

我想我需要一个递归函数,但我无法想象算法会是什么样子

【问题讨论】:

  • 如果 8 个计数 (2*2*2),为什么不能计数 4 个 (2*2)?
  • 我不认为顺序很难弄清楚,它似乎是一个循环序列。
  • 是的,忘了,2*2 也算在内。
  • 您基本上是在要求 5 平滑数字,请参阅 Wikipedia 上的 here。有多种方法可以按顺序正确计算它们。

标签: c++ math


【解决方案1】:

只能被 2、3 或 5 整除的数字只有 2 次方i × 3j sup> × 5k for ijk = 0, 1 , ....

这些数字很容易生成。

【讨论】:

  • 我一直在想这个。但是忘记测试了..但是有一个问题..我需要它们从小到大..
  • Nitpick - i、j 或 k 中的至少一个必须为非零。
  • (i, j, k) 应该如何递增?当然它会从 (1, 0, 0) 开始,但它的连续值是多少?
  • @LuchianGrigore:哦,好吧,我以为1 也在列表中。那好吧。我想,困难在于确定顺序。
  • @A.R.S.:使用 3 < 2 * 2 < 5 < 2 * 3 < 2 * 2 * 2 < 3 * 3 < 2 * 5 等事实。
【解决方案2】:

你要找的数字是2^n * 3^m * 5^k的形式,有n、m和k个正整数,有n+m+k > 0

我会预先生成一个排序数组,然后打印出第一个 N

【讨论】:

  • 我在另一个答案上问了同样的问题,但也许您可以提到 (i, j, k) 应如何从其初始 (1, 0, 0) 递增。
  • 有问题,让它们排序没有多余的数字
  • @waplet 您可以不按特定顺序生成数组(对于最大 N),然后对其进行排序。
  • 我想到了办法..cin << n; int arr[n*3]; for(int i = 1 ; i < n; i ++){ arr[3*i-3] = 2^i; arr[3*i-2] = 3^i; arr[3*i-1] = 5^i;} 然后就排序了?
  • @waplet C++ 不支持变长数组。使用std::vector
【解决方案3】:

我们可以通过合并汉明数序列的适当倍数来有效地按顺序生成序列,这是经典算法。

如果n > 1 是可被p 整除的汉明数,则n/p 也是汉明数,如果m 是汉明数且p 是2、3 或5 之一,则m*p 也是一个汉明数。

所以我们可以将汉明数序列描述为

H = 1 : (2*H ∪ 3*H ∪ 5*H)

其中p*H 是所有汉明数与p 相乘得到的排序序列, 表示排序后的并集(H = 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, ... 也是如此,例如2*H = 2, 4, 6, 8, 10, 12, 16, 18, 20, 24, ...2*H ∪ 3*H = (2, 4, 6, 8, 10, 12, 16, ...) ∪ (3, 6, 9, 12, 15, ...) = (2, 3, 4, 6, 8, 9, 10, 12, 15, 16, ...))。

不过,这种算法有两个缺点。首先,它会产生必须在合并 () 步骤中消除的重复项。其次,要生成N附近的汉明数,需要知道N/5N/3N/2附近的汉明数,最简单的方法是保持@987654340之间的序列部分@ 和 N 在内存中,对于大的 N 需要相当多的内存。

解决这两个问题的变体从 5 的幂序列开始,

P = 1, 5, 25, 125, 625, 3125, ...

并且在第一步中产生除了 3 或 5 之外没有素因数的数字,

T = P ∪ 3*T   (= 1 : (5*P ∪ 3*T))

(一个数 n 除了 3 和 5 之外没有任何质因数,它要么是 5 的幂(n ∈ P),要么可以被 3 整除,n/3 除了 3 和 5 之外也没有质因数(@ 987654348@))。显然P3*T这两个序列是不相交的,所以这里不会产生重复。

那么,我们最终得到汉明数序列

H = T ∪ 2*H

再次,很明显没有重复产生,并且要生成N附近的汉明数,我们需要知道N附近的序列T,这需要知道P附近的NT 靠近 N/3,序列 H 靠近 N/2。在内存中只保留H 的部分在N/2N 之间,以及T 的部分在N/3N 之间需要的空间比保持H 的部分在N/5 之间要少得多和N 在内存中。

my Haskell code 粗略翻译成 C++(毫无疑问是单调的,但我几乎没有写过 C++,而且我学到的 C++ 很古老)产生了

#include <iostream>
#include <cstdlib>
#include <vector>
#include <algorithm>
#include <gmpxx.h>

class Node {
public:
    Node(mpz_class n) : val(n) { next = 0; };
    mpz_class val;
    Node *next;
};

class ListGenerator {
public:
    virtual mpz_class getNext() = 0;
    virtual ~ListGenerator() {};
};

class PurePowers : public ListGenerator {
    mpz_class multiplier, value;
public:
    PurePowers(mpz_class p) : multiplier(p), value(p) {};
    mpz_class getNext() {
        mpz_class temp = value;
        value *= multiplier;
        return temp;
    }
    // default destructor is fine here
    // ~PurePowers() {}
};

class Merger : public ListGenerator {
    mpz_class multiplier, thunk_value, self_value;
    // generator of input sequence
    // to be merged with our own output
    ListGenerator *thunk;
    // list of our output we need to remember
    // to generate the next numbers
    // Invariant: list is never empty, and sorted
    Node *head, *tail;
public:
    Merger(mpz_class p, ListGenerator *gen) : multiplier(p) {
        thunk = gen;
        // first output would be 1 (skipped here, though)
        head = new Node(1);
        tail = head;
        thunk_value = thunk->getNext();
        self_value = multiplier;
    }
    mpz_class getNext() {
        if (thunk_value < self_value) {
            // next value from the input sequence is
            // smaller than the next value obtained
            // by multiplying our output with the multiplier
            mpz_class num = thunk_value;
            // get next value of input sequence
            thunk_value = thunk->getNext();
            // and append our next output to the bookkeeping list
            tail->next = new Node(num);
            tail = tail->next;
            return num;
        } else {
            // multiplier * head->val is smaller than next input
            mpz_class num = self_value;
            // append our next output to the list
            tail->next = new Node(num);
            tail = tail->next;
            // and delete old head, which is no longer needed
            Node *temp = head->next;
            delete head;
            head = temp;
            // remember next value obtained from multiplying our own output
            self_value = head->val * multiplier;
            return num;
        }
    }
    ~Merger() {
        // delete wrapped thunk
        delete thunk;
        // and list of our output
        while (head != tail) {
            Node *temp = head->next;
            delete head;
            head = temp;
        }
        delete tail;
    }
};

// wrap list generator to include 1 in the output
class Hamming : public ListGenerator {
    mpz_class value;
    ListGenerator *thunk;
public:
    Hamming(ListGenerator *gen) : value(1) {
        thunk = gen;
    }
    // construct a Hamming number generator from a list of primes
    // If the vector is empty or contains anything but primes,
    // horrible things may happen, I don't care
    Hamming(std::vector<unsigned long> primes) : value(1) {
        std::sort(primes.begin(), primes.end());
        ListGenerator *gn = new PurePowers(primes.back());
        primes.pop_back();
        while(primes.size() > 0) {
            gn = new Merger(primes.back(), gn);
            primes.pop_back();
        }
        thunk = gn;
    }
    mpz_class getNext() {
        mpz_class num = value;
        value = thunk->getNext();
        return num;
    }
    ~Hamming() { delete thunk; }
};

int main(int argc, char *argv[]) {
    if (argc < 3) {
        std::cout << "Not enough arguments provided.\n";
        std::cout << "Usage: ./hamming start_index count [Primes]" << std::endl;
        return 0;
    }
    unsigned long start, count, n;
    std::vector<unsigned long> v;
    start = strtoul(argv[1],NULL,0);
    count = strtoul(argv[2],NULL,0);
    if (argc == 3) {
        v.push_back(2);
        v.push_back(3);
        v.push_back(5);
    } else {
        for(int i = 3; i < argc; ++i) {
            v.push_back(strtoul(argv[i],NULL,0));
        }
    }
    Hamming *ham = new Hamming(v);
    mpz_class h;
    for(n = 0; n < start; ++n) {
        h = ham->getNext();
    }
    for(n = 0; n < count; ++n) {
        h = ham->getNext();
        std::cout << h << std::endl;
    }
    delete ham;
    return 0;
}

在没有太低效的情况下完成这项工作:

$ ./hamming 0 20
1
2
3
4
5
6
8
9
10
12
15
16
18
20
24
25
27
30
32
36
$ time ./hamming 1000000 2
519381797917090766274082018159448243742493816603938969600000000000000000000000000000
519386406319142860380252256170487374054333610204770704575899579187200000000000000000

real    0m0.310s
user    0m0.307s
sys     0m0.003s
$ time ./hamming 100000000 1
181401839647817990674757344419030541037525904195621195857845491990723972119434480014547
971472123342746229857874163510572099698677464132177627571993937027608855262121141058201
642782634676692520729286408851801352254407007080772018525749444961547851562500000000000
000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000

real    0m52.138s
user    0m52.111s
sys     0m0.050s

(Haskell版本更快,GHC优化惯用Haskell比我优化unidiomatic C++更好)

【讨论】:

    【解决方案4】:

    总是有蛮力的方式:

    int[] A = int[N];
    int i=0;
    int j=2;
    while(i<N)
    {
        if(j%2==0)
        {
            if(j/2==1 || A contains j/2)
            {
                 A[i]=j;
                 i++;
            }
        }
        else if(j%3==0)
        {
            if(j/3==1 || A contains j/3)
            {
                 A[i]=j;
                 i++;
            }
        }
        else if(j%5==0)
        {
            if(j/5==1 || A contains j/5)
            {
                 A[i]=j;
                 i++;
            }
        }
        j++;
    }
    

    对于“A 包含 X”部分,您可以在 0 到 i-1 范围内使用二进制搜索,因为 A 在那里排序。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多