寻找Nth Twin Prime

时间:2012-04-13 15:08:44

标签: algorithm math primes

我试图解决SPOJ上的问题。我们需要计算第n个孪生素数对(素数相差2)。 n可以大到10 ^ 5。我尝试使用筛子进行预先计算,我必须筛分至10 ^ 8以获得最大的n孪生素数,但时间限制是严格的(2s)并且它超时。我注意到人们已经在0.00秒内解决了它,所以我在谷歌周围寻找一个公式,并且无法获得任何有用的信息。有人可以指导我吗?

提前致谢!!

7 个答案:

答案 0 :(得分:2)

我有0.66秒的AC。因为,有0.0s的解决方案我认为可以进行更好的优化,但是,我在这里描述了我的方法。

我在Sieve of Eratosthenes中使用了一个基本优化。你知道2是唯一的素数,使用它你可以减少计算时间和内存,以便计算素数的一半。

其次,所有孪生素数都不是23的倍数(因为它们是素数!)。因此,这些数字的格式为6N+16N+5(其余部分肯定不是素数)。 6N+5 = 6N+6-1 = 6(N+1)-1。因此可以看出6N+16N-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 - 1k = 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 == 53*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. 将Kutools导入Excel
  2. 将感兴趣的素数列入第1列。
  3. 在第2列中插入除数3 - 向下填充第1列中列表中最大素数的级别。
  4. 将第1列的第一行除以第2列的第一行,并将商放在第3列
  5. 将第3列填写到第1列中列表中最大素数的级别。
  6. 正确到零小数。保持数字列3(商)选中。
  7. 从“条件格式化” - 从菜单
  8. 中选择“重复值”
  9. 转到Kutools并选择'to actual' - 这将突出显示商数列3中散布的所有双胞胎的细胞。
  10. 选择第3列
  11. 中的商数
  12. 在Excel中选择“排序和过滤”
  13. 选择“自定义排序”
  14. 填写菜单(对于值,选择商栏中突出显示的颜色),然后单击“确定”。
  15. 孪生素数将在列中组合在一起。 然后可以使用此列表查找素数之间的差距。
  16. 要找到最大的孪生素数,请使用上述程序,将一系列最大的已知素数放入第1列(例如最高的10k素数)。

    如果在此范围内未找到素数双胞胎,则转到下一个最低范围,直到找到孪生素数。这将是最大的双胞胎。

    希望这有帮助。