フェルマー数を求める

2002/12/15作成

フェルマー数

フェルマー数とはフェルマーが考案した素数を生成する式で、22n+1(n≧0)というものです。

プログラムは例によってフェルマー数の定義そのままに作成しています。べき乗をループを回して計算してしまっていますが、これはGMPにmpz_pow()が存在しないためです(あっておかしくないと思うのですが、なぜかありません)。

#include <stdio.h>
#include <stdlib.h>
#include <gmp.h>

int isprime( const mpz_t n );

int main( int ac, char *av[] )
{
    int n;
    mpz_t f, p, i;

    /*  コマンドラインから指数を決定する    */
    if( ac < 2 ) {
        fprintf( stderr, "usage : fermat n\n" );
        return( 1 );
    }
    n = (int)strtol( av[1], NULL, 10 );

    /*  フェルマー数を求める    */
    mpz_init_set_ui( p, 2 );
    mpz_pow_ui( p, p, n );

    mpz_init_set_ui( i, 1 );
    mpz_init_set_ui( f, 2 );
    while( mpz_cmp( i, p ) < 0 ) {
        mpz_mul_ui( f, f, 2 );
        mpz_add_ui( i, i, 1 );
    }
    mpz_add_ui( f, f, 1 );

    if( isprime( f ) )
        printf( "OK\n" );
    else
        printf( "NG\n" );

    mpz_clear( f );
    mpz_clear( p );
    mpz_clear( i );

    return( 0 );
}

int isprime( const mpz_t n )
{
    mpz_t i, tmp;
    int d, ret;

    /*  mpz_probab_prime_p() で確定するか確認 */
    switch( mpz_probab_prime_p( n, 25 ) ) {
        case  0:
            return( 0 );
        case  2:
            return( 1 );
    }

    /*  自乗がn以下の2,3の倍数以外での剰余が0かどうか調べる   */
    d = 2;
    mpz_init_set_ui( i, 5 );
    mpz_init( tmp );
    ret = 1;
    while( 1 ) {
        mpz_mul( tmp, i, i );
        if( mpz_cmp( tmp, n ) > 0 )
            break;

        if( mpz_divisible_p( n, i ) ) {
            ret = 0;
            break;
        }
        mpz_add_ui( i, i, d );
        d = ( d == 2 ? 4 : 2 );
    }

    mpz_clear( i );
    mpz_clear( tmp );

    return( ret );
}

nが0,1,2,3,4の場合、フェルマー数は素数となります。しかし、nが5の場合はフェルマー数は4294967297となりこれは合成数でした。この先はどうかというと、現在のところ5以上のフェルマー数については合成数しか見つかっていないそうです。

(2012/2/14追記)

メルセンヌ素数と同様に、GMPを用いてプログラムを書き直しました。

(2020/4/25追記)

ソースコードを少し整理しました。

(2026/3/7追記)

素数判定で mpz_probab_prime_p() を使用するように修正するとともに、ソースコードを少し整理しました。素数生成式を検証すると同様、mpz_probab_prime_p() は確定時のみ採用しています。