如何编程求解 100 以内的质数?

关注者
1,240
被浏览
312,756
登录后你可以
不限量看优质回答私信答主深度交流精彩内容一键收藏

我尝试用一行C++11代码模仿

@大人

的 Python式实现:

copy_if(range(2, 101), ostream_iterator<int>(cout, " "), [](int x){ return none_of(range(2, x), [x](int y) { return x % y == 0; }); });

这样比较容易理解:

copy_if(                              // 复制
    range(2, 101),                    // 数列 { 2, 3, .. 100 } 中的整数
    ostream_iterator<int>(cout, " "), // 至cout(每个输出后加入空格)
    [](int x) {                       // 仅当对于每个整数x
        return none_of(               // 没有一个
            range(2, x),              // y in { 2, ..., x - 1 }
            [x](int y) {              // 乎合
                return x % y == 0;    // y 为 x 的因子
            }
        );
    }
);

-----

为了实现这个函数式实现,C++11不提供range()的功能,需要自行实现一个。完整的程序是这样的:

#include <algorithm>
#include <iostream>
#include <iterator>

using namespace std;

template <typename T>
class range_type {
public:
    class range_iterator {
    public:
        typedef T value_type;
        typedef T* pointer;
        typedef T& reference;
        typedef ptrdiff_t difference_type;
        typedef forward_iterator_tag iterator_category;

        range_iterator(T v) : v_(v) {}
        T operator*() const { return v_; }
        range_iterator operator++() { ++v_; return *this; }
        range_iterator operator++(int) { range_iterator copy(*this); ++v_; return copy; }
        bool operator==(const range_iterator &other) const { return v_ == other.v_; }
        bool operator!=(const range_iterator &other) const { return v_ != other.v_; }
    private:
        T v_;
    };

    range_type(T lower, T upper) : lower_(lower), upper_(upper) {}
    range_iterator begin() const { return range_iterator(lower_); }
    range_iterator end() const { return range_iterator(upper_); }

private:
    T lower_, upper_;
};

template <typename T>
range_type<T> range(T lower, T upper) { return range_type<T>(lower, upper); }

template <typename Container, typename UnaryPredicate>
bool none_of(const Container& c, UnaryPredicate pred) { return none_of(c.begin(), c.end(), pred); }

template <typename Container, typename DestIterator, typename UnaryPredicate>
void copy_if(const Container& c, DestIterator d, UnaryPredicate pred) { copy_if(c.begin(), c.end(), d, pred); }

int main() {
    copy_if(range(2, 101), ostream_iterator<int>(cout, " "), [](int x){ return none_of(range(2, x), [x](int y) { return x % y == 0; }); });
    return 0;
}

另外,因为C++ algorithm标头档的函数都使用 begin, end 作为参数,为了把程序写成一行,我加入了使用container作为参数的none_of()和copy_if()。

相信没有学生会把这段代码当功课交吧。

-----

2014/11/10更新:不少评论说了各种「优化」方法。

包括把内循环的范围从[2, x)降至[2, \left \lfloor \frac{x}{2} \right \rfloor + 1),或者更好的[2, \left \lfloor \sqrt{x} \right \rfloor + 1)。另外也可以只测试之前的质数是否因子。

我作了一个简单的测试:

void test(int t, std::vector<int>& p, int n, function<bool(int)> isPrime) {
    p.clear();
    clock_t start = clock();
    copy_if(range(2, n + 1), back_inserter(p), isPrime);
    clock_t end = clock();
    cout << t << ". " << p.size() << " " << (end - start) * 1000.0 / CLOCKS_PER_SEC << "ms" << endl;
}

int main() {
    std::vector<int> p;
    p.reserve(10000);

    const int n = 100000;

    test(1, p, n, [](int x) { return none_of(range(2, x), [x](int y) { return x % y == 0; }); });
    test(2, p, n, [](int x) { return none_of(range(2, x), [x](int y) { return y * 2 <= x && x % y == 0; }); });
    test(3, p, n, [](int x) { return none_of(range(2, x / 2 + 1), [x](int y) { return x % y == 0; }); });
    
    test(4, p, n, [](int x) { return none_of(range(2, x), [x](int y) { return y * y <= x && x % y == 0; }); });
    test(5, p, n, [](int x) { return none_of(range(2, x), [x](int y) { return y <= (int)sqrt(x) && x % y == 0; }); });
    test(6, p, n, [](int x) { return none_of(range(2, (int)sqrt(x) + 1), [x](int y) { return x % y == 0; }); });

    test(7, p, n, [&p](int x) { return none_of(p, [x](int y) { return x % y == 0; }); });
    test(8, p, n, [&p](int x) { return none_of(p, [x](int y) { return y * y <= x && x % y == 0; }); });
    test(9, p, n, [&p](int x) { return none_of(p, [x](int y) { return y <= (int)sqrt(x) && x % y == 0; }); });
    test(10, p, n, [&p](int x) {
        for (auto y : p) {
            if (y > (int)sqrt(x))
                return true;
            else if (x % y == 0)
                return false;
        }
        return true;
    });

    //copy(p, ostream_iterator<int>(cout, " "));

    return 0;
}

为了测试性能,我把n调至100000,并避免输出字符串的开销,只把结果写在vector里。

运行结果:

1. 9592 1564ms
2. 9592 927ms
3. 9592 784ms
4. 9592 569ms
5. 9592 293ms
6. 9592 11ms
7. 9592 159ms
8. 9592 65ms
9. 9592 33ms
10. 9592 4ms

如果能减少范围,直接改动上界(测试3、6)是较好的。虽然测试5减少了调用sqrt(),但因为仍然需要迭代整个范围,会比测试6要慢得多。

对于使用之前结果中的质数,测试7比测试6(简单的开方优化)要慢。但在这情况下,我们不能改变上界(不知道现时已存的质数哪个大于x的开方),测试8和9的结果仍然比测试6要慢。

最后,唯有放弃使用none_of(),才能避免迭代整个范围,达到这些测试中最优的测试10。