我试图解决SPOJ上的问题。我们需要计算第n个孪生素数对(素数相差2)。 n可以大到10 ^ 5。我尝试使用筛子进行预先计算,我必须筛分至10 ^ 8以获得最大的n孪生素数,但时间限制是严格的(2s)并且它超时。我注意到人们已经在0.00秒内解决了它,所以我在谷歌周围寻找一个公式,并且无法获得任何有用的信息。有人可以指导我吗?
提前致谢!!
答案 0 :(得分:2)
我有0.66秒的AC。因为,有0.0s的解决方案我认为可以进行更好的优化,但是,我在这里描述了我的方法。
我在Sieve of Eratosthenes
中使用了一个基本优化。你知道2
是唯一的素数,使用它你可以减少计算时间和内存,以便计算素数的一半。
其次,所有孪生素数都不是2
和3
的倍数(因为它们是素数!)。因此,这些数字的格式为6N+1
和6N+5
(其余部分肯定不是素数)。 6N+5 = 6N+6-1 = 6(N+1)-1
。因此可以看出6N+1
和6N-1
可能是N
> = 1的双素数。因此,您使用之前计算过的素数预先计算所有这些值。 (琐碎的案例是3 5)
注意:您不需要计算质数直到10 ^ 8,上限要低得多。 [编辑:如果你愿意,我可以分享我的代码,但如果你自己想出一个解决方案会更好。 :)]
答案 1 :(得分:2)
出于好奇,我使用两种Eratosthenes筛子解决了the problem。第一个变体在测试机上完成0.93s,第二个变体在0.24s完成。为了进行比较,在我的计算机上,第一个完成0.08秒,第二个完成0.04秒。
第一个是关于奇数的标准筛,第二个是更精细的筛子,除了偶数之外还省略了3的倍数。
SPOJ的测试机器老旧而且速度慢,所以程序运行的时间比最近的典型盒子长得多;并且它们具有较小的缓存,因此保持较小的计算非常重要。
这样做,Eratosthenes的筛子很容易就够了。但是,保持较小的内存使用非常重要。第一个变体,每个数字使用一个字节,给出"超出时间限制"在SPOJ上,但在我的盒子上跑了0.12秒。因此,鉴于SPOJ测试机的特性,使用位筛在给定时间内解决它。
在SPOJ机器上,通过进一步减少筛子的空间,我获得了显着的加速(运行时间0.14秒)。因为 - 除了第一对(3,5)之外 - 所有素数双胞胎都具有(6*k-1, 6*k+1)
形式,如果k
没有产生,你不必知道这两个数字中的哪一个是复合的对于双素数对,仅筛选指数k
就足够了。
(6*k + 1
可以被5整除,当且仅当k = 5*m + 4
m
6*k - 1
和k = 5*m+1
可以被5整除时当且仅当m
一些5*m ± 1, m >= 1
,因此5将6*k+1
标记为不会产生孪生素数。同样,k = 13*m + 2
可以被13整除,当且仅当m
对于某些6*k - 1
}和k = 13*m - 2
当且仅当某m
13*m ± 2
时,{13}会标记#define limit 100000000
int prime1[MAXN];
int prime2[MAXN];
。)
这并不会改变标记的数量,因此在缓存足够大的情况下,运行时间的变化很小,但对于小缓存,这是一个显着的加速。
还有一件事。你的10 8 的限制太高了。我使用了一个下限(2000万),并没有过多地估计100,000个 th 双素数对。限制为10 8 ,第一个变体肯定没有及时完成,第二个可能没有。
随着限制的减少,Atkin的Sieve需要进行一些优化以超越Eratosthenes变体,省略偶数和3的倍数,一个天真的实现将明显变慢。
关于你(维基百科的伪代码)阿特金筛的一些评论:
int root = ceil(sqrt(limit));
bool sieve[limit];
你不需要第二个阵列,一对素数对的较大伙伴可以很容易地从较小的一对计算出来。您浪费空间并从两个阵列中销毁缓存局部性。 (不过,与筛分所需的时间相比,这是次要的。)
bool
在如今的许多操作系统上,这是一个即时的段错误,即使有一个减少的限制。堆栈大小通常限制在8MB或更小。应该在堆上分配该大小的数组。
如上所述,每个数字使用一个std::bitset
会使程序运行得比必要的慢得多。您应该自己使用std::vector<bool>
或 for (int x = 1; x <= root; x++)
{
for (int y = 1; y <= root; y++)
{
//Main part of Sieve of Atkin
int n = (4*x*x)+(y*y);
if (n <= limit && (n % 12 == 1 || n % 12 == 5)) sieve[n] ^= true;
n = (3*x*x)+(y*y);
if (n <= limit && n % 12 == 7) sieve[n] ^= true;
n = (3*x*x)-(y*y);
if (x > y && n <= limit && n % 12 == 11) sieve[n] ^= true;
}
}
或旋转这些位。另外,建议至少省略偶数。
4*x^2 + y^2
这非常低效。它会尝试太多的x-y组合,对于每个组合,它会执行三个或四个分区来检查模12的余数,并在数组中来回跳跃。
分开不同的样方。
对于x < sqrt(limit)/2
,很明显您只需考虑y
和奇4*x^2 + y^2
。那么模12的余数是1,5或9.如果余数是9,那么n % 12 == 1
实际上是9的倍数,所以这样的数字将被消除为不是无方形的。但是,最好从筛子中省略3的倍数,并分别处理案例n % 12 == 5
和3*x^2 + y^2
。
对于x < sqrt(limit/3)
,很明显您只需要考虑x
,并且一点点思考就会发现y
必须是奇数且3*x^2 - y^2
偶数(并且不可分割)通过3)。
对于y < x
y < sqrt(limit/2)
,显然您只需考虑y
。查看余数模12,您会发现x
不能被3整除,而y
和{{1}}必须具有不同的奇偶校验。
答案 2 :(得分:1)
据Wolfram Alpha称,基本上,筛选高达20,000,000就足够了。使用Eratosthenes的普通筛子,在C ++中使用vector<bool>
(您使用什么语言BTW?)。
跟踪筛网内的孪生素数。当你找到双胞胎时,将一对下面的素数存储在一个单独的向量中,如果请求了一个无序(小于前一个)索引(并且它们与描述页面上显示的例子相反),从这个存储中获得素数:
size_t n = 10000000, itop=2236;
vector<bool> s;
vector<int> twins;
s.resize(n, true);
int cnt, k1, k2, p1=3, p2, k=0;
cin >> cnt;
if( cnt-- > 0 )
{
cin >> k1;
for( size_t i=1; i < n; ++i ) // p=2i+1
{
if( s[i] )
{
p2 = 2*i+1;
if( p2-p1 == 2 ) { ++k; twins.push_back(p1); }
if( k==k1 )
{
cout << p1 << " " << p2 << endl;
......
等。接受1.05秒(Ideone上0.18秒)。或解开逻辑 - 只需立即预先计算100,000个双素数对,然后在一个单独的循环中访问它们(0.94秒)。
答案 3 :(得分:1)
可以在此处找到解决此问题的有效算法的说明@ Programming Praxis entry此外,还提供了Scheme和Perl示例代码。
答案 4 :(得分:0)
我使用Eratosthenes的Sieve预先计算了大量素数列表,然后遍历列表计数项目,这些项目比他们的继任者少2个,直到找到其中的n个。在http://ideone.com/vYjuC以1.42秒的速度运行。我也想知道如何在零秒内计算答案。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define ISBITSET(x, i) (( x[i>>3] & (1<<(i&7)) ) != 0)
#define SETBIT(x, i) x[i>>3] |= (1<<(i&7));
#define CLEARBIT(x, i) x[i>>3] &= (1<<(i&7)) ^ 0xFF;
typedef struct list {
int data;
struct list *next;
} List;
List *insert(int data, List *next)
{
List *new;
new = malloc(sizeof(List));
new->data = data;
new->next = next;
return new;
}
List *reverse(List *list) {
List *new = NULL;
List *next;
while (list != NULL)
{
next = list->next;
list->next = new;
new = list;
list = next;
}
return new;
}
int length(List *xs)
{
int len = 0;
while (xs != NULL)
{
len += 1;
xs = xs->next;
}
return len;
}
List *primes(int n)
{
int m = (n-1) / 2;
char b[m/8+1];
int i = 0;
int p = 3;
List *ps = NULL;
int j;
ps = insert(2, ps);
memset(b, 255, sizeof(b));
while (p*p < n)
{
if (ISBITSET(b,i))
{
ps = insert(p, ps);
j = (p*p - 3) / 2;
while (j < m)
{
CLEARBIT(b, j);
j += p;
}
}
i += 1; p += 2;
}
while (i < m)
{
if (ISBITSET(b,i))
{
ps = insert(p, ps);
}
i += 1; p += 2;
}
return reverse(ps);
}
int nth_twin(int n, List *ps)
{
while (ps->next != NULL)
{
if (n == 0)
{
return ps->data - 1;
}
if (ps->next->data - ps->data == 2)
{
--n;
}
ps = ps->next;
}
return 0;
}
int main(int argc, char *argv[])
{
List *ps = primes(100000000);
printf("%d\n", nth_twin(100000, ps));
return 0;
}
答案 5 :(得分:0)
这就是我的尝试。我有一串TLE。
bool mark [N];
vector <int> primeList;
void sieve ()
{
memset (mark, true, sizeof (mark));
mark [0] = mark [1] = false;
for ( int i = 4; i < N; i += 2 )
mark [i] = false;
for ( int i = 3; i * i <= N; i++ )
{
if ( mark [i] )
{
for ( int j = i * i; j < N; j += 2 * i )
mark [j] = false;
}
}
primeList.clear ();
primeList.push_back (2);
for ( int i = 3; i < N; i += 2 )
{
if ( mark [i] )
primeList.push_back (i);
}
//printf ("%d\n", primeList.size ());
}
int main ()
{
sieve ();
vector <int> twinPrime;
for ( size_t i = 1; i < primeList.size (); i++ )
{
if ( primeList [i] - primeList [i - 1] == 2 )
twinPrime.push_back (primeList [i - 1]);
}
int t;
scanf("%d",&t);
int s;
while ( t-- )
{
scanf("%d",&s);
printf ("%d %d\n", twinPrime [s - 1], twinPrime [s - 1] + 2);
}
return 0;
}
答案 6 :(得分:0)
以下是一个可以回答您问题的程序:
素数,当除以3时,在校正为十进制0(零)时具有相等的商数是Twin Primes。
这可以写成
对于任何一对素数Px,Py,如果[Px / 3,0] = [Py / 3,0],则Px和Py为素数双胞胎。
这样做的基础是,如果素数相差2,那么当商数被修正为十进制零时,除以所有感兴趣的素数将产生唯一的相等商。当被校正为十进制零时,未被2分隔的素数将不具有相等的商。
例如:
•11,13除以3时,当商被校正为十进制零时,将产生唯一的4的唯一商。
•17,19除以3时,当商被校正为十进制零时,将产生6的唯一商。
•29,31除以3时,当商被校正为十进制零时,将产生10的唯一商。
等
以下是使用Excel的简单程序:
•从任何素数列表中找到素数双胞胎 •在任何质数范围内找到孪生素数 •找到最大的素数孪生素数 •找出孪生素数之间的差距
要找到最大的孪生素数,请使用上述程序,将一系列最大的已知素数放入第1列(例如最高的10k素数)。
如果在此范围内未找到素数双胞胎,则转到下一个最低范围,直到找到孪生素数。这将是最大的双胞胎。
希望这有帮助。