【问题标题】:Efficiently find the nth number in a series where the numbers are only divisible by 2,3 and 5有效地找到系列中的第 n 个数字,其中数字只能被 2,3 和 5 整除
【发布时间】:2021-05-16 23:38:32
【问题描述】:

我想在一系列只能被 2、3 和 5 整除且不能被任何其他素数整除的数字中找到第 n 个数字。

这是我找到第 1500 个数字的最简单解决方案。这需要大约 16 秒来执行。我想知道是否有办法让它更快,比如使用多线程。

#include <iostream>
using namespace std;

int main()
{
    int temp, seriesCounter = 1, currentNumber = 2;

    while (seriesCounter < 1500)
    {
        temp = currentNumber;

        //Remove all multiple of 2
        while (temp % 2 == 0)
        {
            temp /= 2;
        }
        //Remove all multiple of 3
        while (temp % 3 == 0)
        {
            temp /= 3;
        }
        //Remove all multiple of 5
        while (temp % 5 == 0)
        {
            temp /= 5;
        }
        // If only 1 remains, the number is valid, else not.
        if (temp == 1)
        {
            seriesCounter++;
        }
        currentNumber++;
    }

    cout << "The 1500th number in the series is : " << --currentNumber << endl << endl;

    return 1;
}

【问题讨论】:

  • 你可以考虑重新组织它,这样你就可以枚举一堆三元组(m, n, q)并查看2^m * 3^n * 5^q 的值。可能需要仔细考虑如何将它们放回正确的顺序,但这意味着您不会花时间考虑一堆不在序列中的数字。
  • 我不知道,但可能有一些巧妙的数学公式可以解决 O(1) 中的问题。如果没有,请考虑使用筛选算法或某种记忆。现在你正在重复很多工作。
  • 2、3 和 5 的倍数形成一个模式,每 30 个数字重复一次。不幸的是,要确定哪些倍数不能被另一个素数整除并不容易。
  • 如果您想获得关于工作代码的反馈,您可以向codereview.stackexchange.com 提问。

标签: c++ multithreading optimization c++14


【解决方案1】:

这是一种非常简单的方法。它在一秒钟内找到第 1500 个元素(我没有费心更精确地测量)。

#include <set>
#include <iostream>

int main() {
    int n = 1500;

    std::set<long long> s;
    s.insert(2);
    s.insert(3);
    s.insert(5);
    long long num = 0;
    while (n--) {
        num = *s.begin();
        s.erase(s.begin());
        s.insert(num*2);
        s.insert(num*3);
        s.insert(num*5);
    }
    std::cout << num;
}

【讨论】:

  • 我真的很喜欢这个解决方案。
【解决方案2】:

在 i7 笔记本电脑上在 0.000031 秒内完成快速解决方案(结果:num[1499]=860934420):

时间复杂度为 O(n)(在您的情况下为 n=1500)。假设当前序列是 num[]。我们希望将其增加到 1500 个元素。主要思想是记录序列num[]中的三个位置,我们称它们为idx[0]、idx[1]和idx[3]。这样 2num[idx[0]]、3num[idx[1]] 和 5*num[idx[2]] 就变成 >= 序列中的最后一个数字(num.back ())。然后将三个中最小的添加到序列的末尾(成为新的 num.back())。

#include <iostream>
#include <vector>
#include <algorithm>
#include <chrono>
using std::vector;
using namespace std::chrono;

int main()
{
    auto start = high_resolution_clock::now();
    const vector<int> p{ 2,3,5 };
    vector<int> idx{ 0,0,0 };
    vector<int> num = { 2,3,4,5 }; num.reserve(1500);
    vector<int> candidate(3);
    while (num.size() < 1500) {
        int candidate = INT_MAX;
        for (int i = 0; i < p.size(); i++) {
            int t;
            while (num.back() >= (t=num[idx[i]] * p[i])) idx[i]++;
            candidate = std::min(candidate, t);
        }
        num.push_back(candidate);
    }
    auto stop = high_resolution_clock::now();
    auto duration = duration_cast<microseconds>(stop - start);

    std::cout << num.back();
    std::cout <<"\ntakes "<< duration.count() << " microseconds";
}

代码可以进一步改进。想法是一样的,但是通过记住数组候选[3]中的乘法结果,减少了执行的乘法次数:

const vector<int> p{ 2,3,5 };
vector<int> idx{ 1,0,0 };
vector<int> num = { 2,3,4,5 }; num.reserve(1500);
vector<int> candidate{ 6,6,10 };
while (num.size() < 1500) {
    auto it = std::min_element(candidate.begin(), candidate.end());
    if (num.back() != *it) num.push_back(*it);
    int which_p = it - candidate.begin();
    *it = num[++idx[which_p]] * p[which_p];
}

【讨论】:

    猜你喜欢
    • 2016-06-12
    • 2016-04-11
    • 1970-01-01
    • 1970-01-01
    • 2012-09-09
    • 1970-01-01
    • 2011-08-13
    • 1970-01-01
    • 2020-08-04
    相关资源
    最近更新 更多