如何使用埃拉托斯特内斯筛算法改进素数程序
How can I improve my prime number program with Sieve of Eratosthenes algorithm?
我的程序打印来自以下表达式的所有素数:
((1 + sin(0.1*i))*k) + 1, i = 1, 2, ..., N.
输入格式:
不超过100个示例。每个例子在同一行上都有两个正整数。
输出格式:
将每个数字单独打印一行。
样本输入:
4 10
500 100
样本输出:
5
17
但是我的算法不够有效。我如何添加埃拉托斯尼筛,使其足够有效而不打印";由于超时而终止";。
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long k, n;
int j;
while (cin >> k >> n) {
if (n>1000 && k>1000000000000000000) continue;
int count = 0;
for (int i = 1; i <= n; i++) {
int res = ((1 + sin(0.1*i)) * k) + 1;
for (j = 2; j < res; j++) {
if (res % j == 0) break;
}
if (j == res) count++;
}
cout << count << endl;
}
system("pause");
您只需在试用部门做得更好,就可以将速度提高10倍。您正在测试从2到res
的所有整数,而不是将2视为特例,只测试从3到res
的平方根的奇数:
// k <= 10^3, n <= 10^9
int main() {
unsigned k;
unsigned long long n;
while (cin >> k >> n) {
unsigned count = 0;
for (unsigned long long i = 1; i <= n; i++) {
unsigned long long j, res = (1 + sin(0.1 * i)) * k + 1;
bool is_prime = true;
if (res <= 2 || res % 2 == 0) {
is_prime = (res == 2);
} else {
for (j = 3; j * j <= res; j += 2) {
if (res % j == 0) {
is_prime = false;
break;
}
}
}
if (is_prime) {
count++;
}
}
cout << count << endl;
}
}
尽管k=500和n=500000000仍然需要40秒左右的时间
EDIT:我添加了第三个平均值来提高效率
EDIT2:添加了为什么Sieve不应该是解决方案的解释以及一些三角关系。此外,我添加了一个关于问题历史的注释
你的问题不是计算给定范围内的所有素数,而是只计算函数生成的素数。
因此,我不认为Eratosthenes筛是这个特定练习的解决方案,原因如下:n
总是很小,而k
可能很大。如果k
非常大,那么Sieve算法将不得不生成大量素数,以便最终将其用于少量候选。
你可以通过三种方式提高程序的效率:
- 避免每次计算
sin(.)
。例如,可以使用三角关系。此外,第一次计算这些值时,将它们存储在数组中并重用这些值。sin()
的计算非常耗时 - 在检查数字是否素数的测试中,将搜索限制为
sqrt(res)
。此外,考虑仅用奇数j
加上2
进行测试 - 如果候选
res
与前一个相同,则避免重新进行测试
一些三角法
如果c=cos(0.1)和s=sin(0.1),则可以使用以下关系式:
sin (0.1(i+1)) = s*cos (0.1*i) + c*sin(0.1*i))
cos (0.1(i+1)) = c*cos (0.1*i) - s*sin(0.1*i))
如果n
较大,则应定期通过函数重新计算sin()
,以避免过多的舍入误差计算。但这里不应该是这种情况,因为n
总是很小。
然而,正如我所提到的,最好在第一步只使用"记忆"技巧,并检查它是否足够。
关于这个问题的历史以及为什么这个答案的说明:
最近,这个网站收到了几个问题"如何改进我的程序,计算这个k*sin()
函数生成的素数的数量……"据我所知,这些问题都是重复的,因为筛子是解决方案,并且在之前的一个类似(但略有不同)的问题中进行了解释。现在,同样的问题以稍微不同的形式再次出现:"我如何在这个程序中插入Sieve算法…(再次使用k*sin())"。然后我意识到筛子不是解决方案。这不是对之前结束语的批评,因为我在理解这个问题时犯了同样的错误。然而,我认为是时候提出一个新的解决方案了,即使它与的新问题并不完全匹配
当您使用简单的Wheel因子分解时,您可以获得非常好的代码加速。2阶的轮因子分解利用了这样一个事实,即对于自然的n,所有大于3的素数都可以写成6n+1或6n+5。这意味着每6个数字只需要进行2次除法运算。或者更进一步,所有大于5的素数都可以写成30n+m,其中m{1,7,11,13,17,19,23,29}。(每30个数字8个分区)。
使用这个简单的原理,你可以编写以下函数来测试你的素数(轮子{2,3}):
bool isPrime(long long num) {
if (num == 1) return false; // 1 is not prime
if (num < 4) return true; // 2 and 3 are prime
if (num % 2 == 0) return false; // divisible by 2
if (num % 3 == 0) return false; // divisible by 3
int w = 5;
while (w*w <= num) {
if(num % w == 0) return false; // not prime
if(num % (w+2) == 0) return false; // not prime
w += 6;
}
return true; // must be prime
}
您可以对车轮{2,3,5}进行上述调整。此功能可在主程序中用作:
int main() {
long long k, n;
while (cin >> k >> n) {
if (n>1000 && k>1000000000000000000) continue;
int count = 0;
for (int i = 1; i <= n; i++) {
long long res = ((1 + sin(0.1*i)) * k) + 1;
if (isPrime(res)) { count++; }
}
cout << count << endl;
}
return 0;
}
一个简单的定时给了我原始代码(g++ prime.cpp
)
% time echo "6000 100000000" | ./a.out
12999811
echo "6000 100000000" 0.00s user 0.00s system 48% cpu 0.002 total
./a.out 209.66s user 0.00s system 99% cpu 3:29.70 total
而优化版本给了我
% time echo "6000 100000000" | ./a.out
12999811
echo "6000 100000000" 0.00s user 0.00s system 51% cpu 0.002 total
./a.out 10.12s user 0.00s system 99% cpu 10.124 total
可以进行其他改进,但可能影响较小:
- 为
i
从0到1000预计算正弦表sin(0.1*i)
。这将避免重复计算这些正弦。然而,这只会产生较小的影响,因为大部分时间都浪费在了原始测试上 - 检查
res(i) == res(i+1)
:这几乎没有任何影响,因为根据n
和k
,大多数连续的res
不相等 - 使用查找表,可能更方便,这确实会产生影响
原始答案:
我的建议如下:
- 为
i
从0到1000预先计算可供使用的sin(0.1*i)
。这将避免重复计算这些正弦。此外,要明智行事(见第3点) - 找到
res
的最大可能值,即res_max=(2*k)+1
用Eratosthenes筛求出
res_max
的所有素数。此外,要意识到,对于自然n,所有大于3的素数都可以写成6n+1或6n+5。或者更进一步,所有大于5的素数都可以写成30n+m,其中m{1,7,11,13,17,19,23,29}。这就是所谓的车轮分解。所以,不要麻烦检查任何其他号码。(此处提供更多信息)有一个查找表,说明一个数字是否是素数。
- 在查找表上执行所有循环操作
- Mongodb c++驱动程序:如何查询元素的数组
- C++,系统无法执行指定的程序
- 在C++程序中输入的文本文件将不起作用,除非文本被复制和粘贴
- 在VS代码中交叉编译Windows与Linux上的MinGW的SDL程序
- 为什么这个运算符<重载函数对 STL 算法不可见?
- C++ Windows 驱动程序MSB3030无法复制该文件,因为它找不到
- 我的排序算法程序中的堆错误
- 使用算法标头时,同一程序不会在 Windows 上编译
- 是否有具有用户定义的冲突处理程序的 std::唯一样式库算法
- 将C 应用程序(无接口,仅算法)移植到Android
- 如何使用埃拉托斯特内斯筛算法改进素数程序
- 我正在尝试为 Kruskal 算法实现一个C++程序,该程序需要我按权重对图形进行排序。如何按权重对结构进行排序
- dijkstra算法的C++程序中的分割错误
- 磁盘调度程序SCAN算法错误
- 用 Big-O 表示法弄清楚我的程序的算法复杂性
- 运行时向程序添加算法
- C++程序展位算法 2s 使用数组补码
- 需要帮助将 Kruskal 算法应用于使用 2D 结构存储数据的现有邻接矩阵程序
- 在应用程序中使用Rsync和增量传输算法
- 向程序添加排序算法(冒泡排序)