午後のひとときにプログラミング問題を考える。
7334490=2 * 3 * 5 * 41 * 67 * 89
7334490を素因数分解すると、小町算になっている。
小町数とは、1から9までを1つずつ使った数。
小町算とは、演算子を含めた計算式が小町数になっている式、または虫食い算の解が小町数となる式。
大町数や大町算は、0を含めた小町数や小町算で、0は数字の先頭には使えない。
これらを踏まえて、
問題1
素因数分解が小町算となる最小の自然数及び素因数分解を求めるプログラムを組め。
問題2
素因数分解が大町算となる最小の自然数及び素因数分解を求めるプログラムを組め。
問題3
素因数分解が小町算となる小町数及び素因数分解をすべて求めるプログラムを組め。
問題4
素因数分解が大町算となる大町数及び素因数分解をすべて求めるプログラムを組め。
シンキングタ~イム
解答編です。
まずは、数学的な考証です。
素因数分解は、素因数の積の形にするということ。
偶数の素数は2のみ、つまり、小町算にしろ、大町算にしろ、4、6、8、は一の位には来ない。
最低でも2桁以上の素数に含まれる必要がある。
1や9は1桁では素数ではないので、これも2桁以上の素数に含まれる必要がある。
大町算の0は、先頭桁、末尾桁には来ないので、最低でも3桁以上の素数に含まれる必要がある。
問題1、問題2は、最小の小町数、最小の大町数を求めるので、
例えば、13と31という同じ1と3を使う素数だが、素因数として31を使うと最小でないことは明白ですね。
例えば、2^3と3^2と2*3という同じ2と3を使う項だが、2^3=8と3^2=9と2*3=6なので、べき乗表記が必ず大きくなるので、べき乗表記は無視出来る。
プログラムを組まずとも、机上で解を求めることも出来そうです。
とは言っても、素数をどこまで知っているかということになってしまいますので、
かなり厳しいかもしれません。
とりあえず、例として7桁の小町算が示されているので、7桁以下であることは解ります。
これらを踏まえて7桁までの素数を絞り込むと、
2
3
5
7
13
17
19
23
29
37
41
43
47
53
59
61
67
79
83
89
127
137
139
149
157
163
167
179
239
241
251
257
263
269
281
283
347
349
359
367
379
389
431
457
461
463
467
479
487
523
541
563
569
587
619
683
769
827
829
853
859
1237
1249
1259
1279
1283
1289
1367
1423
1427
1439
1453
1459
1483
1487
1489
1523
1567
1579
1583
1627
1693
1697
1753
1783
1789
1867
1973
2347
2357
2389
2459
2467
2539
2543
2579
2657
2659
2683
2687
2689
2693
2749
2789
2837
2843
2851
2857
2861
3457
3461
3467
3469
3659
3697
3847
3947
4157
4261
4289
4561
4567
4583
4597
4679
4691
4789
4861
5189
5261
5387
5623
5683
5689
5839
5849
5861
5867
5879
6389
8467
12347
12379
12457
12479
12487
12539
12569
12583
12589
12637
12647
12653
12689
12697
12893
13249
13457
13469
13487
13567
13597
13679
13687
13789
13859
14389
14563
14593
14629
14657
14683
14759
14867
14869
14879
15287
15649
15679
15683
16823
16879
18457
23459
23497
23567
23687
23689
23789
23857
23957
24631
24659
24683
24697
24763
24851
24859
25463
25639
25679
25847
25867
26489
26879
28349
28579
34589
34679
34687
34759
34897
35869
35879
36457
36587
42853
45697
45863
45869
47869
51869
52861
56897
123457
123479
124367
124567
124679
124693
124759
124783
124853
124897
125387
125639
125683
125687
125789
126397
126487
126839
126859
128549
129853
134587
134597
134789
134867
135467
135469
135697
136849
136859
136879
145679
145687
145879
146987
234587
234589
234659
234769
234869
235679
235789
236879
238657
245681
245683
245789
248569
248657
257689
345679
345689
348769
357689
1234657
1234687
1234759
1234769
1234789
1235789
1235867
1238459
1238659
1245683
1245689
1246579
1246879
1256897
1345879
1346957
1346987
1354687
1456789
2345867
2345869
2347859
2357689
2456879
302個に絞られる。
小さい方から、試行錯誤していくと、
2 * 3 * 5 * 67 * 1489
で小町算になることがわかる。
2992989=2 * 3 * 5 * 67 * 1489
は、例題よりは小さいことがわかるが、これが最小かどうかは、もう少し試行錯誤する必要はあるだろう。
後は、1から調べるのはアホらしいので、
9! = 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2 * 1 = 362880
から調べればいいということはわかるだろう。
さて、プログラミングする上で、必要そうな情報は出揃ったかな。
先に示した7桁までの素数のリストをこねくり回す方法もあるだろうが、今回は純粋に362880からインクリメントしていくこととする。
解答1
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
unsigned long long p, q, r;
int i, t;
int num[10], prm[10], m, n;
for (i=362880; i<10000000; i++) {
for (n=0; n<10; n++) num[n] = 0;
t = i;
m = 0;
p = 2;
if ( t%(p*p) == 0 ) continue;
if ( t%p == 0 ) {
t /= p;
prm[m++] = p;
num[p]++;
}
r = sqrtl(i)+1;
for (p=3; p<r; p+=2) {
if ( t%(p*p) == 0 ) break;
if ( t%p == 0 ) {
t /= p;
q = p;
prm[m++] = p;
while ( q > 0 ) {
num[q%10]++;
q /= 10;
}
}
}
if ( p < r ) continue;
if ( t > 1 ) {
prm[m++] = t;
while ( t > 0 ) {
num[t%10]++;
t /= 10;
}
}
for (n=1; n<10; n++) if ( num[n] != 1 ) break;
if ( num[0] == 0 && n == 10 ) {
printf("%llu=",i);
for (n=0; n<m; n++) {
printf("%d",prm[n]);
if ( n+1 < m ) printf(" * ");
}
printf("\n");
break;
}
}
return EXIT_SUCCESS;
}
出力1
2992890=2 * 3 * 5 * 67 * 1489
解答2
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
unsigned long long p, q, r;
int i, t;
int num[10], prm[10], m, n;
for (i=362880; i<100000000; i++) {
for (n=0; n<10; n++) num[n] = 0;
t = i;
m = 0;
p = 2;
if ( t%(p*p) == 0 ) continue;
if ( t%p == 0 ) {
t /= p;
prm[m++] = p;
num[p]++;
}
r = sqrtl(i)+1;
for (p=3; p<r; p+=2) {
if ( t%(p*p) == 0 ) break;
if ( t%p == 0 ) {
t /= p;
q = p;
prm[m++] = p;
while ( q > 0 ) {
num[q%10]++;
q /= 10;
}
}
}
if ( p < r ) continue;
if ( t > 1 ) {
prm[m++] = t;
while ( t > 0 ) {
num[t%10]++;
t /= 10;
}
}
for (n=0; n<10; n++) if ( num[n] != 1 ) break;
if ( n == 10 ) {
printf("%d=",i);
for (n=0; n<m; n++) {
printf("%d",prm[n]);
if ( n+1 < m ) printf(" * ");
}
break;
}
}
return EXIT_SUCCESS;
}
出力2
15618090=2 * 3 * 5 * 487 * 1069
特に説明の必要はないかとは思うが、変更点はわずかです。
続いて、問題3と問題4について、追加の数学的考察です。
小町数や大町数を素因数分解することになるのですが、
小町数は1から9までを使うので各桁の和は45、
大町数は0から9までを使うので各桁の和は45、
各桁の和が9の倍数のとき、元の数は9の倍数である。
つまり、素因数分解には3^2は必ず含まれる。
但し、指数の2とは限らず大きい可能性もあります。
問題3、問題4は、問題1、問題2のようにべき乗は無視してはいけない問題だということ。
べき乗表記において、1乗の表記をすると、1が複数個になる恐れがあるので、1乗に関しては表記しないものとする。
また、プログラム内部で小町数では9桁、大町数では10桁の数を、インクリメントして探していくことは、かなり非効率であるので、あまりプログラム的にはご法度かもしれないが、多重ネストで書いてみようかと思う。
解答3
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int a,b,c,d,e,f,g,h,i;
unsigned long long p, q, r, s, t;
int num[10], n, prm[20], m;
for (a=1; a<10; a++)
for (b=1; b<10; b++) {
if ( a == b ) continue;
for (c=1; c<10; c++) {
if ( a == c || b == c ) continue;
for (d=1; d<10; d++) {
if ( a == d || b == d || c == d ) continue;
for (e=1; e<10; e++) {
if ( a == e || b == e || c == e || d == e ) continue;
for (f=1; f<10; f++) {
if ( a == f || b == f || c == f || d == f || e == f ) continue;
for (g=1; g<10; g++) {
if ( a == g || b == g || c == g || d == g || e == g || f == g ) continue;
for (h=1; h<10; h++) {
if ( a == h || b == h || c == h || d == h || e == h || f == h || g == h ) continue;
i = 45-a-b-c-d-e-f-g-h;
if ( a == i || b == i || c == i || d == i || e == i || f == i || g == i || h == i ) continue;
p = q = a*100000000LL+b*10000000LL+c*1000000LL+d*100000LL+e*10000LL+f*1000LL+g*100LL+h*10LL+i;
printf("%llu\r",p);
for (n=0; n<10; n++) num[n] = 0;
r = sqrtl(p)+1;
m = 0;
s = 2;
if ( q%s == 0 ) {
q /= s;
num[s]++;
n = 1;
prm[m++] = s;
while ( q%s == 0 ) {
q /= s;
n++;
}
prm[m++] = n;
if ( n > 1 ) num[n]++;
}
for (s=3; s<r; s+=2) {
if ( q%s == 0 ) {
q /= s;
t = s;
while ( t > 0 ) {
num[t%10]++;
t/=10;
}
n = 1;
prm[m++] = s;
while ( q%s == 0 ) {
q /= s;
n++;
}
prm[m++] = n;
if ( n > 1 ) num[n]++;
}
}
if ( q > 1 ) {
prm[m++] = q;
prm[m++] = 1;
while ( q > 0 ) {
num[q%10]++;
q /= 10;
}
}
for (n=1; n<10; n++) if ( num[n] != 1 ) break;
if ( num[0] == 0 && n == 10 ) {
printf("\r%llu=",p);
for (n=0; n<m; n+=2) {
printf("%d",prm[n]);
if ( prm[n+1] > 1 ) printf("^%d",prm[n+1]);
if ( n+2 < m ) printf(" * ");
}
printf("\n");
}
}}}}}}}
printf("\t\t\r");
return EXIT_SUCCESS;
}
出力3
176348259=3^5 * 17 * 42689
239586741=3^4 * 2957861
312854967=3^5 * 1287469
392846517=3^4 * 7 * 692851
419536827=3^5 * 1726489
492865317=3^4 * 7 * 869251
493261875=3^2 * 5^4 * 87691
589231746=2 * 3^6 * 41 * 9857
627539481=3^5 * 41 * 62987
解答4
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main()
{
int a,b,c,d,e,f,g,h,i,j;
unsigned long long p, q, r, s, t;
int num[10], n, prm[20], m;
for (a=1; a<10; a++)
for (b=0; b<10; b++) {
if ( a == b ) continue;
for (c=0; c<10; c++) {
if ( a == c || b == c ) continue;
for (d=0; d<10; d++) {
if ( a == d || b == d || c == d ) continue;
for (e=0; e<10; e++) {
if ( a == e || b == e || c == e || d == e ) continue;
for (f=0; f<10; f++) {
if ( a == f || b == f || c == f || d == f || e == f ) continue;
for (g=0; g<10; g++) {
if ( a == g || b == g || c == g || d == g || e == g || f == g ) continue;
for (h=0; h<10; h++) {
if ( a == h || b == h || c == h || d == h || e == h || f == h || g == h ) continue;
for (i=0; i<10; i++) {
if ( a == i || b == i || c == i || d == i || e == i || f == i || g == i || h == i ) continue;
j = 45-a-b-c-d-e-f-g-h-i;
if ( a == j || b == j || c == j || d == j || e == j || f == j || g == j || h == j || i == j ) continue;
p = q = a*1000000000LL+b*100000000LL+c*10000000LL+d*1000000LL+e*100000LL+f*10000LL+g*1000LL+h*100LL+i*10L+j;
printf("%llu\r",p);
for (n=0; n<10; n++) num[n] = 0;
r = sqrtl(p)+1;
m = 0;
s = 2;
if ( q%s == 0 ) {
q /= s;
num[s]++;
n = 1;
prm[m++] = s;
while ( q%s == 0 ) {
q /= s;
n++;
}
prm[m++] = n;
if ( n > 1 ) num[n]++;
}
for (s=3; s<r; s+=2) {
if ( q%s == 0 ) {
q /= s;
t = s;
while ( t > 0 ) {
num[t%10]++;
t /= 10;
}
n = 1;
prm[m++] = s;
while ( q%s == 0 ) {
q /= s;
n++;
}
prm[m++] = n;
if ( n > 1 ) num[n]++;
}
}
if ( q > 1 ) {
prm[m++] = q;
prm[m++] = 1;
while ( q > 0 ) {
num[q%10]++;
q /= 10;
}
}
for (n=0; n<10; n++) if ( num[n] != 1 ) break;
if ( n == 10 ) {
printf("\r%llu=",p);
for (n=0; n<m; n+=2) {
printf("%d",prm[n]);
if ( prm[n+1] > 1 ) printf("^%d",prm[n+1]);
if ( n+2 < m ) printf(" * ");
}
printf("\n");
}
}}}}}}}}
printf("\t\t\r");
return EXIT_SUCCESS;
}
出力4
1293804576=2^5 * 3^4 * 617 * 809
1465820793=3^4 * 859 * 21067
1526783094=2 * 3^5 * 409 * 7681
2094738651=3^4 * 25860971
3189056427=3^6 * 29 * 150847
3405961872=2^4 * 3^5 * 876019
3642180957=3^6 * 487 * 10259
3905614827=3^5 * 16072489
4072531689=3^4 * 50278169
4072568139=3^4 * 50278619
4283615907=3^5 * 17628049
4610289375=3^2 * 5^4 * 819607
4681203597=3^4 * 7 * 8256091
4682579301=3^4 * 57809621
4701953286=2 * 3^5 * 9674801
5260483197=3^5 * 21648079
5623147089=3^5 * 7 * 41 * 80629
5680421379=3^4 * 70128659
5821067349=3^4 * 71865029
6085417329=3^4 * 75128609
6173480529=3^4 * 76215809
6581934207=3^5 * 27086149
6704329581=3^4 * 82769501
6971825034=2 * 3^6 * 59 * 81047
9831476250=2 * 3^6 * 5^4 * 10789
解答3と解答4は、0を使わない使うで、かなりの変更点がありますね。
当然ネストも1つ増えます。
高速化のために、桁数が不変なので、ゴリゴリと書いていますが、再帰処理で書くのがスマートでしょうね。
問題1や問題2もべき乗を考えて、すべて見つけるという問題だとしたら、2^987654321というようなことも可能になるので、インクリメントでは厳しいですので、右辺の素数のリストを作って、大小ごちゃまぜになるかとは思うが、その方法がベストだろうか。
興味があれば、チャレンジして、何パターンあるのか数えてみるのも一考かと思いますが、手を出さない方がいいかもしれません。
べき乗なしならば、そこまで多くないかとはおもいますね。
ではでは
knifeのmy Pick