いつの魔にRound 2がはじまっている。
キミならどう書く 2.0 - ROUND 2 - ? Lightweight Language Ring
お題は「Collatz予想」(角谷予想,3x+1問題)についての問題です.
[中略]
また,余力のある者は大きな n についても h(n) を求めよ.
これまたtrivialな問題なのだけど、1,000,000とか計算する場合、実は32bit整数では足りない。
タイトルどおり、113383で引っかかるのだ。
113383 → 340150 → 170075 → 510226 → 255113 → 765340 → 382670 → 191335 → 574006 → 287003 → 861010 → 430505 → 1291516 → 645758 → 322879 → 968638 → 484319 → 1452958 → 726479 → 2179438 → 1089719 → 3269158 → 1634579 → 4903738 → 2451869 → 7355608 → 3677804 → 1838902 → 919451 → 2758354 → 1379177 → 4137532 → 2068766 → 1034383 → 3103150 → 1551575 → 4654726 → 2327363 → 6982090 → 3491045 → 10473136 → 5236568 → 2618284 → 1309142 → 654571 → 1963714 → 981857 → 2945572 → 1472786 → 736393 → 2209180 → 1104590 → 552295 → 1656886 → 828443 → 2485330 → 1242665 → 3727996 → 1863998 → 931999 → 2795998 → 1397999 → 4193998 → 2096999 → 6290998 → 3145499 → 9436498 → 4718249 → 14154748 → 7077374 → 3538687 → 10616062 → 5308031 → 15924094 → 7962047 → 23886142 → 11943071 → 35829214 → 17914607 → 53743822 → 26871911 → 80615734 → 40307867 → 120923602 → 60461801 → 181385404 → 90692702 → 45346351 → 136039054 → 68019527 → 204058582 → 102029291 → 306087874 → 153043937 → 459131812 → 229565906 → 114782953 → 344348860 → 172174430 → 86087215 → 258261646 → 129130823 → 387392470 → 193696235 → 581088706 → 290544353 → 871633060 → 435816530 → 217908265 → 653724796 → 326862398 → 163431199 → 490293598 → 245146799 → 735440398 → 367720199 → 1103160598 → 551580299 → 1654740898 → 827370449 → -1812855948
64bitなら、1,000,000まで計算する場合でも何とか足りる。
というわけで、以下、Cによる実装。
#include <stdio.h> #include <stdlib.h> typedef long long I64; #define collatz(n) ((n) == 1 ? 1 : ((n) % 2) ? (n)*3+1 : (n)/2) I64 g(I64 n){ I64 i = n; I64 result = 1; do { result++; }while((i = collatz(i)) != 1); return result; } I64 *h(I64 n){ I64 i, nmax = 0, gmax = 0, gnext = 0; static I64 result[2]; for (i = 1; i <= n; i++){ gnext = g(i); #ifdef VERBOSE fprintf(stderr, "g(%qd) = %qd\r", i, gnext); #endif if (gnext > gmax){ nmax = i; gmax = gnext; } } result[0] = nmax; result[1] = gmax; return result; } int main(int argc, char **argv){ I64 n = 100; if (argc > 1){ n = atoi(argv[1]); } I64 *hg = h(n); printf("h(%qd) = %qd where g(%qd) = %qd\n", n, hg[0], hg[0], hg[1]); return 0; /* so -Wall is happy */ }
MacBook Pro 2GHzでの実行結果は、以下のとおり。
% gcc -Wall -O3 iterative.c % /usr/bin/time ./a.out 1000000 h(1000000) = 837799 where g(837799) = 525 3.19 real 3.13 user 0.01 sys
C, C++, Perlは気をつけられたし。Haskell, Ruby, Python は整数はデフォルトでbignumなのでOKこの問題はない。なお、Perlの場合-Duse64bitintを付けてcompileされたものであれば1,000,000も目指せる。use bignumでもOKだけど低速ゆえお薦めしない。
Dan the Integral Man
追記:id:palmoさんのようにビット演算でやる方法もあるが、この場合でもやはり113383で引っかかる。以下、検証コードと結果。
iterative.pluse strict; use warnings; sub g($) { use integer; my $num = shift; my $n = $num; my $step = 1; while ($n > 1) { $n = ($n & 1) ? (3 * $n + 1) : ($n >> 1); $step++; } die "g($num) got $n!" unless $n == 1; return $step; } sub h{ my $n = shift; my ($nmax, $gmax, $gnext) = (0, 0, 0); for (1..$n){ my $gnext = g($_); print STDERR "g($_) = $gnext\r"; next unless $gnext > $gmax; $nmax = $_; $gmax = $gnext; } return ($nmax, $gmax); } sub printh{ my $n = shift; my ($h, $g) = h($n); print "h($n) = $h where g($n) = $g\n"; } my $n = shift || 100; printh $n; __END__実行結果:
% perl -V | grep 64bit use64bitint=undef use64bitall=undef uselongdouble=undef % perl iterative.pl 1000000 g(113383) got -1812855948! at iterative.pl line 13.
追記^2
をを、itaさんも同じとこでハマってる。
JGeek Log - 3n+1 問題1000000 でint のオーバーフローで落ちる。
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。