Mersenne使用Lucas-Lehmer素数测试

时间:2016-09-07 22:32:11

标签: c algorithm primes scientific-computing primality-test

以下是代码,其中limit = 8

#include <stdio.h>
#include <math.h> // pow(x, exp)

//----------------------------------------------------------

char isMersenneLucasLehmer(unsigned int prime)
{
    unsigned int i, termN = 4;
    unsigned long mersenne;
    unsigned int limit;
    int res;

    mersenne = (unsigned long) pow(2, (double)prime) - 1;
    if (prime % 2 == 0)
    {
        return prime == 2;
    }
    else 
    {
        res = (int) sqrt((double) prime);
        for (i = 3; i <= res; i += 2)
        {
            if (prime % i == 0)
            {
                return 0;  
            }
        }

        limit = prime - 2;
        for (i = 1; i <= limit; ++i)
        {
            termN = (termN * termN - 2) % mersenne;
        }
    }
    return termN == 0;
}
//----------------------------------------------------------

/*
    Function: findMersenneLucasLehmer()

*/
void findMersenneLucasLehmer(unsigned int limit)
{
    unsigned int i, current = 0;
    unsigned long mersenne, bitsInLong = 64;

    for (i = 2; i <= bitsInLong; i++)
    {
        if (current >= limit)
        {
            break;
        }

        if (isMersenneLucasLehmer(i))   
        {
            mersenne = (unsigned long) pow(2, (double)i) - 1;
            printf("current = %lu, mersenne = %lu, index = %u\n", current, mersenne, i);
            ++current;
        } 
    }
}
//----------------------------------------------------------

int main()
{
    unsigned int limit = 8;
    findMersenneLucasLehmer(limit);
    return 0;
}

输出:

current = 0, mersenne = 3, index = 2
current = 1, mersenne = 7, index = 3
current = 2, mersenne = 31, index = 5
current = 3, mersenne = 127, index = 7
current = 4, mersenne = 8191, index = 13

它只返回第一个5而不是8,我无法弄清楚原因。

更新

它正在从13开始跳过所有索引。我怀疑错误发生在isMersenneLucasLehmer(unsigned int)的最后几行。我一直盯着看太久而无法找到它。

4 个答案:

答案 0 :(得分:3)

termN * termN处的可能整数溢出。通常,您应该将非常大的数字值表示为双精度数,并且尽可能避免在不同的数字类型之间进行转换,尤其是在整数和浮点数之间进行转换。

答案 1 :(得分:3)

改变这个:

unsigned int termN = 4;

到此:

unsigned long int termN = 4;

主要是因为您稍后执行termN * termN,当unsigned int类型时可能会导致溢出。

输出:

current = 0, mersenne = 3, index = 2
current = 1, mersenne = 7, index = 3
current = 2, mersenne = 31, index = 5
current = 3, mersenne = 127, index = 7
current = 4, mersenne = 8191, index = 13
current = 5, mersenne = 131071, index = 17
current = 6, mersenne = 524287, index = 19
current = 7, mersenne = 2147483647, index = 31

按照你应该的方式打印你的类型会很好:

C02QT2UBFVH6-lm:~ gsamaras$ gcc -Wall main.c
main.c:58:67: warning: format specifies type 'unsigned long' but the argument has type 'unsigned int' [-Wformat]
            printf("current = %lu, mersenne = %lu, index = %u\n", current, mersenne, i);
                              ~~~                                 ^~~~~~~
                              %u

所以将%lu更改为%u

我是如何开始调试的?

在循环开始时使用print语句,如下所示:

for (i = 2; i <= bitsInLong; i++)
{
    printf("Loop i = %u, current = %u\n", i, current);
    ...

你会看到:

current = 4, mersenne = 8191, index = 13
Loop i = 14, current = 5
...
Loop i = 63, current = 5
Loop i = 64, current = 5

这意味着你没有看到8个Mersenne数字,因为你在你的函数fins 8之前你正在结束你的循环!

答案 2 :(得分:2)

问题在于:

termN = (termN * termN - 2) % mersenne;

您将termN声明为unsigned int(32位,在您的环境中),但该产品可能会变得如此之大,以至于无法通过此类型表示,并且产生的溢出会导致循环发散。< / p>

解决方案是使用范围更大的类型,例如unsigned long long int(64位)。

请参阅Ideone.com上的示例。

答案 3 :(得分:2)

让我们进一步推送这个数字并抛出所有浮点代码。虽然下一个mersenne prime适合64位,但问题是termN * termN表达式在模数可以控制之前会溢出。如果我们有真正的模幂运算,我们可能会避免这个问题。相反,我们在计算该值时会在GCC / clang中使用模拟的128位类型:

#include <stdio.h>
#include <stdbool.h>

typedef unsigned __int128 uint128_t;

bool isPrime(unsigned number)
{
    if (number % 2 == 0)
    {
        return number == 2;
    }

    for (unsigned i = 3; i * i <= number; i += 2)
    {
        if (number % i == 0)
        {
            return false;
        }
    }

    return true;
}

bool isMersenneExponent(unsigned exponent)
{
    if (exponent == 2)
    {
        return true;
    }

    if (!isPrime(exponent))
    {
        return false;
    }

    unsigned long termN = 4, mersenne = (1L << exponent) - 1;

    unsigned limit = exponent - 1;

    for (unsigned i = 1; i < limit; i++)
    {
        termN = (((uint128_t) termN * termN) % mersenne) - 2;
    }

    return termN == 0;
}

void findMersennePrime(unsigned limit)
{
    unsigned bit_limit = sizeof(unsigned long) * 8;

    for (unsigned current = 0, i = 2; current < limit && i < bit_limit; i++)
    {
        if (isMersenneExponent(i)) 
        {
            unsigned long mersenne = (1L << i) - 1;
            printf("current = %u, mersenne = %lu, index = %u\n", current++, mersenne, i);
        }
    }
}

int main()
{
    unsigned limit = 9;
    findMersennePrime(limit);
    return 0;
}

i * iisPrime()的效率略低,但由于指数素数很小,因此无关紧要。

<强>输出

current = 0, mersenne = 3, index = 2
current = 1, mersenne = 7, index = 3
current = 2, mersenne = 31, index = 5
current = 3, mersenne = 127, index = 7
current = 4, mersenne = 8191, index = 13
current = 5, mersenne = 131071, index = 17
current = 6, mersenne = 524287, index = 19
current = 7, mersenne = 2147483647, index = 31
current = 8, mersenne = 2305843009213693951, index = 61