我的程序从该表达式打印所有素数:
((1 + sin(0.1*i))*k) + 1, i = 1, 2, ..., N.
输入格式:
不超过100个示例。每个示例在同一行上都有2个正整数。
输出格式:
将每个数字打印在单独的行上。
样本输入:
4 10
500100
样本输出:
5
17
但是我的算法不够有效。我该如何添加Eratosthenes筛网,这样它才能足够有效地避免打印“由于超时而终止”。
#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");
答案 0 :(得分:1)
编辑:我添加了第三个方法来提高效率
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是解决方案的原因,并且在先前的类似(但略有不同)问题中得到了解释。现在,相同的问题以稍有不同的形式再次出现“我如何在该程序中插入Sieve算法...(再次使用k * sin())”。然后我意识到筛网不是解决方案。我对问题的理解也犯了同样的错误,所以这并不是对以前的评论的批评。但是,我认为是时候提出一个新的解决方案了,即使它与新问题并不完全吻合
答案 1 :(得分:1)
使用简单的Wheel factorization时,可以获得非常不错的代码加速。 2阶的车轮分解利用了以下事实:对于自然的 n 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
可以进行其他改进,但可能会产生较小的影响:
sin(0.1*i)
的正弦表i
从0预先计算为1000。这样可以避免一遍又一遍地重新计算这些正弦。但是,这影响不大,因为大多数时间都浪费在了测试上。res(i) == res(i+1)
是否几乎没有影响,因为取决于n
和k
,最连续的res
不相等。原始答案:
我的建议如下:
sin(0.1*i)
的正弦i
从0预先计算为1000。这样可以避免一遍又一遍地重新计算这些正弦。另外,要聪明一点(请参阅第3点)res
的最大可能值res_max=(2*k)+1
使用Sieve of Eratosthenes查找res_max
的所有素数。此外,请注意,对于自然的 n ,所有大于3的素数都可以写为 6n + 1 或 6n + 5 。甚至更进一步,所有大于5的素数都可以写为 30n + m ,其中 m 在 {1,7,11,13,17,19, 23,29} 。这就是所谓的Wheel factorization。因此,请勿打扰检查其他任何数字。 (更多信息here)
有一个查找表,指出数字是否为质数。
答案 2 :(得分:1)
仅通过在审判部门做得更好,您就可以将速度提高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秒钟左右。