いつの魔に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.pl
use 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 のオーバーフローで落ちる。
このブログにコメントするにはログインが必要です。
さんログアウト
この記事には許可ユーザしかコメントができません。