Quantcast
Channel: 円周率近似値の日に生まれて理系じゃないわけないだろ! - knifeのblog
Viewing all articles
Browse latest Browse all 5376

10!=7!*6! -解答編-

$
0
0

午後のひとときにプログラミングの問題を考えてみる。


問題
a!=b!*c!
x>a>b>c>1

を満たす、すべての解a, b, cを出力するプログラムを組め。
但し、
xは出来る限る大きいことが望ましく、出来る限り高速性が高いものを考えてください。


シンキングタ~イム


解答編です。

まずは数学的な考証からです。

n!とは
n!=n*(n-1)*(n-2)*…*1
ということです。
但し、
0!=1
と便宜上定義されています。

今回の問題は、a、b、cの大小関係を固定していることからも解るように、
1!=1!*0!
1!=1!*1!
2!=2!*1!
2!=2!*0!

といったものを排除していますね。

続いて、自明な解を考えます。

(n!)!=n!*(n!-1)!

これは階乗の定義から自明ですが、一応解説します。

m=n!とすると、
m!=m*(m-1)!

もう解りますよね。

では、自明ではない、非自明な解はというと、タイトルの

10!=7!*6!

こういう解があるということを踏まえて、プログラミングする必要があるということです。

さて、問題文の注意書きにもあるように、xを出来る限り大きく取りたい。

仮に、64ビット正整数だとすると、
最大値は、
18446744073709551615
これ以下のaを考えると、
20!=2432902008176640000
が限界となる。

これでは、
6!=5!*3!
の次の自明解
24!=23!*4!
すら表示せずに終わってしまい
自明解が1個と非自明解がタイトル以外に見つかればよいが、
おそらく見つからないだろう。

つまり、もっと頭を働かせる必要がある。

そこで登場するのが素因数分解。
素因数分解の指数だけを記録していけば、それほど大きな値にはならずに済む。
但し、最低でも素因数の数は配列が必要になってくる。

指数の配列も、素数だけに限定出来ればよいのだが、それをするには同じ大きさの素数の配列も必要になってしまう。

その辺りが肝になるだろうという感じで、プログラミングに取り掛かるとしよう。

mulfact1.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    unsigned int a, a2, b, b2, c, c2;
    unsigned int i, j, k, p, s;
    char m[1000000], n[1000000];

    for (i=0; i<1000000; i++) m[i] = 0;
    a2 = 6;
    b2 = 1;
    c2 = 3;
    for (a=2, b=0; a<=2000000; a++) {
        printf("%u!\r",a);
        s = sqrtl(a)+1;
        i = a;
        p = 2;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[(p-1)/2]++;
        }
        
        for (p=3; p<s; p+=2) {
            if ( i%p == 0 && p > b ) b = p;
            while ( i%p == 0 ) {
                i /= p;
                m[(p-1)/2]++;
            }
        }

        if ( i > 1 ) {
            if ( i > b ) b = i;
            m[(i-1)/2]++;
        }

        if ( a == b ) continue;

        for (i=b2+1; i<=b; i++) {
            s = sqrtl(i)+1;
            j = i;
            p = 2;
            while ( j%p == 0 ) {
                j /= p;
                m[(p-1)/2]--;
            }

            for (p=3; p<s; p+=2) {
                while ( j%p == 0 ) {
                    j /= p;
                    m[(p-1)/2]--;
                }
            }

            if ( j > 1 ) {
                m[(j-1)/2]--;
            }
        }
        b2 = b;

        if ( a == a2 ) {
            printf("自明解:   %u!=%u!*%u!\n",a2,a2-1,c2);
            c2++;
            a2 *= c2;
            continue;
        }

        for (i=0; i<=(b-1)/2; i++) n[i] = m[i];

        for (c=2; c<a; c++) {
            s = sqrtl(c)+1;
            i = c;
            p = 2;
            if ( i%p == 0 ) {
                i /= p;
                n[(p-1)/2]--;
            }
            if ( n[(p-1)/2] < 0 ) break;

            for (p=3; p<s; p+=2) {
                if ( i%p == 0 ) {
                    i /= p;
                    n[(p-1)/2]--;
                }
                if ( n[(p-1)/2] < 0 ) break;
            }
            if ( n[(p-1)/2] < 0 ) break;

            if ( i > 1 ) {
                n[(i-1)/2]--;
                if ( n[(i-1)/2] < 0 ) break;
            }

            for (i=0; i<=(b-1)/2; i++) {
                if ( n[i] != 0 ) break;
            }
            if ( n[i] == 0 ) {
                printf("非自明解: %u!=%u!*%u!\n",a,b,c);
            }
        }
    }

    return EXIT_SUCCESS;
}

> mulfact1.exe
自明解:   6!=5!*3!
非自明解: 10!=7!*6!
自明解:   24!=23!*4!
自明解:   120!=119!*5!
自明解:   720!=719!*6!
自明解:   5040!=5039!*7!
自明解:   40320!=40319!*8!
自明解:   362880!=362879!*9!
2000000!

char型の1000000個の配列を2つ用意出来ましたので、
偶素数、奇素数として、奇数の素因数を半分に圧縮でき、
aを2000000まで探索出来ます。

mulfact2.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    unsigned int a, a2, b, b2, c, c2;
    unsigned int i, j, k, p, q, s;
    char m[1000001], n[1000001];

    for (i=0; i<1000001; i++) m[i] = 0;
    a2 = 6;
    b2 = 1;
    c2 = 3;
    for (a=2, b=0; a<=3000000; a++) {
        printf("%u!\r",a);
        s = sqrtl(a)+1;
        i = a;
        p = 2;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[0]++;
        }
        
        p = 3;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[1]++;
        }

        for (p=5,q=2; p<s; p+=q,q=6-q) {
            if ( i%p == 0 && p > b ) b = p;
            while ( i%p == 0 ) {
                i /= p;
                m[(p+2)/3]++;
            }
        }

        if ( i > 1 ) {
            if ( i > b ) b = i;
            m[(i+2)/3]++;
        }

        if ( a == b ) continue;

        for (i=b2+1; i<=b; i++) {
            s = sqrtl(i)+1;
            j = i;
            p = 2;
            while ( j%p == 0 ) {
                j /= p;
                m[0]--;
            }

            p = 3;
            while ( j%p == 0 ) {
                j /= p;
                m[1]--;
            }

            for (p=5,q=2; p<s; p+=q,q=6-q) {
                while ( j%p == 0 ) {
                    j /= p;
                    m[(p+2)/3]--;
                }
            }

            if ( j > 1 ) {
                m[(j+2)/3]--;
            }
        }
        b2 = b;

        if ( a == a2 ) {
            printf("自明解:   %u!=%u!*%u!\n",a2,a2-1,c2);
            c2++;
            a2 *= c2;
            continue;
        }

        for (i=0; i<=(b+2)/3; i++) n[i] = m[i];

        for (c=2; c<a; c++) {
            s = sqrtl(c)+1;
            i = c;
            p = 2;
            while ( i%p == 0 ) {
                i /= p;
                n[0]--;
            }
            if ( n[0] < 0 ) break;

            p = 3;
            while ( i%p == 0 ) {
                i /= p;
                n[1]--;
            }
            if ( n[1] < 0 ) break;

            for (p=5,q=2; p<s; p+=q,q=6-q) {
                while ( i%p == 0 ) {
                    i /= p;
                    n[(p+2)/3]--;
                }
                if ( n[(p+2)/3] < 0 ) break;
            }
            if ( n[(p+2)/3] < 0 ) break;

            if ( i > 1 ) {
                n[(i+2)/3]--;
                if ( n[(i+2)/3] < 0 ) break;
            }

            for (i=0; i<=(b+2)/3; i++) {
                if ( n[i] != 0 ) break;
            }
            if ( n[i] == 0 ) {
                printf("非自明解: %u!=%u!*%u!\n",a,b,c);
            }
        }
    }

    return EXIT_SUCCESS;
}

> mulfact2.exe
自明解:   6!=5!*3!
非自明解: 10!=7!*6!
自明解:   24!=23!*4!
自明解:   120!=119!*5!
自明解:   720!=719!*6!
自明解:   5040!=5039!*7!
自明解:   40320!=40319!*8!
自明解:   362880!=362879!*9!
3000000!

char型の1000001個の配列を2個確保出来ましたので、
2、3、6±1型素数として、素因数の個数を1/3+2に圧縮出来たので、
aを3000000まで探索出来るようになりました。


mulfact3.c
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main()
{
    unsigned int a, a2, b, b2, c, c2;
    unsigned int i, j, k, p, s;
    char m[1000000], n[1000000];
    char q[8] = {4,2,4,2,4,6,2,6};
    char o[30] = {6,7,7,7,7,7,7,0,0,0,
              0,1,1,2,2,2,2,3,3,4,
              4,4,4,5,5,5,5,5,5,6};

    for (i=0; i<1000000; i++) m[i] = 0;
    a2 = 6;
    b2 = 1;
    c2 = 3;
    for (a=2, b=0; a<=3750000; a++) {
        printf("%u!\r",a);
        s = sqrtl(a)+1;
        i = a;
        p = 2;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[0]++;
        }
        
        p = 3;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[1]++;
        }

        p = 5;
        if ( i%p == 0 && p > b ) b = p;
        while ( i%p == 0 ) {
            i /= p;
            m[2]++;
        }

        for (p=7,k=0; p<s; p+=q[k%8],k++) {
            if ( i%p == 0 && p > b ) b = p;
            while ( i%p == 0 ) {
                i /= p;
                m[k+3]++;
            }
        }

        if ( i > 1 ) {
            if ( i > b ) b = i;
            m[(i-7)/30+o[i%30]+3]++;
        }
        if ( a == b ) continue;

        for (i=b2+1; i<=b; i++) {
            s = sqrtl(i)+1;
            j = i;
            p = 2;
            while ( j%p == 0 ) {
                j /= p;
                m[0]--;
            }

            p = 3;
            while ( j%p == 0 ) {
                j /= p;
                m[1]--;
            }

            p = 5;
            while ( j%p == 0 ) {
                j /= p;
                m[2]--;
            }

            for (p=7,k=0; p<s; p+=q[k%8],k++) {
                while ( j%p == 0 ) {
                    j /= p;
                    m[k+3]--;
                }
            }

            if ( j > 1 ) {
                m[(j-7)/30+o[j%30]+3]--;
            }
        }
        b2 = b;

        if ( a == a2 ) {
            printf("自明解:   %u!=%u!*%u!\n",a2,a2-1,c2);
            c2++;
            a2 *= c2;
            continue;
        }

        for (i=0; i<=b*8/30+3; i++) n[i] = m[i];

        for (c=2; c<a; c++) {
            s = sqrtl(c)+1;
            i = c;
            p = 2;
            while ( i%p == 0 ) {
                i /= p;
                n[0]--;
            }
            if ( n[0] < 0 ) break;

            p = 3;
            while ( i%p == 0 ) {
                i /= p;
                n[1]--;
            }
            if ( n[1] < 0 ) break;

            p = 5;
            while ( i%p == 0 ) {
                i /= p;
                n[2]--;
            }
            if ( n[2] < 0 ) break;

            for (p=7,k=0; p<s; p+=q[k%8],k++) {
                while ( i%p == 0 ) {
                    i /= p;
                    n[k+3]--;
                }
                if ( n[k+3] < 0 ) break;
            }
            if ( n[k+3] < 0 ) break;

            if ( i > 1 ) {
                n[(i-7)/30+o[i%30]+3]--;
                if ( n[(i-7)/30+o[i%30]] < 0 ) break;
            }

            for (i=0; i<a; i++) {
                if ( n[i] != 0 ) break;
            }

            if ( n[i] == 0 ) {
                printf("非自明解: %u!=%u!*%u!\n",a,b,c);
            }
        }
    }

    return EXIT_SUCCESS;
}

> mulfact3.exe
自明解:   6!=5!*3!
非自明解: 10!=7!*6!
自明解:   24!=23!*4!
自明解:   120!=119!*5!
自明解:   720!=719!*6!
自明解:   5040!=5039!*7!
自明解:   40320!=40319!*8!
自明解:   362880!=362879!*9!
自明解:   3628800!=3628799!*10!
3750000!

char型の1000000個の配列を2個確保出来ましたので、
2、3、5、mod 30型素数として、素因数の個数を8/30+3に圧縮出来たので、
aを3750000まで探索出来るようになりました。

それによって、自明解が一つ多く表示されます。

結局、ここまで探索しても非自明解は、
10!=7!*6!
しか見つかっていません。

自明解は無限に存在することは証明出来ますが、
非自明解は無限に存在するのだろうか、
それとも有限個なのだろうか。

どうやら未解決問題のようです。

それにしても、このブログでは素因数分解が頻繁に出てきますね。


ではでは
 

 


Viewing all articles
Browse latest Browse all 5376

Trending Articles