2006年07月14日 14:00 [Edit]
h(4294967295) = 2610744987 where g = 1051
itaさんの追記を読んでいたら、私も行き着くところまで行きたくなった。

キミならどう書く 2.0 - ROUND 2 - ? Lightweight Language Ring
このときJGeek Log - 3n+1 問題
h(n) = k, 1 ≦ k ≦ n ∧ g(k) = max (g(1),g(2),…,g(n))
について h(100) を求めよ.
time a.out 1000000000 (テーブルサイズ = n * 0.03)
g(670617279) = 987
とはいっても、行ったのはunsigned 32bit intの限界までなのだけど。
以下、FreeBSD 6.1, Dual Xeon 2.8MHz による結果。
% /usr/bin/time -l ./a.out
h(4294967295) = 2610744987 where g(2610744987) = 1051
2629.46 real 2611.42 user 2.90 sys
131892 maximum resident set size
4 average shared memory size
7574 average unshared data size
128 average unshared stack size
32860 page reclaims
0 page faults
0 swaps
0 block input operations
0 block output operations
0 messages sent
0 messages received
0 signals received
54 voluntary context switches
215379 involuntary context switches
見ての通り、一時間かかっていない。また、n = 10億までの結果はitaさんのものと一致する。
% /usr/bin/time ./a.out 1000000
h(1000000) = 837799 where g(837799) = 525
0.16 real 0.16 user 0.00 sys
% /usr/bin/time ./a.out 10000000
h(10000000) = 8400511 where g(8400511) = 686
1.77 real 1.73 user 0.03 sys
% /usr/bin/time ./a.out 100000000
h(100000000) = 63728127 where g(63728127) = 950
16.35 real 16.06 user 0.23 sys
% /usr/bin/time ./a.out 1000000000
h(1000000000) = 670617279 where g(670617279) = 987
357.63 real 355.45 user 0.24 sys
itaさんはもう少し先に行ったみたいだけど、「渡し」も「自身」がなかったのでとりあえず無難な線で一区切り。
これは自身なし
n=10000000000
g(9780657630) = 1133
4222.760u 4223.620s 2:23:12.06 98.3% 0+0k 0+0io 95pf+0w
実はMacBook Proはもっと優秀で、n=1億でこんな結果になった。
% /usr/bin/time ./a.out 100000000
h(100000000) = 63728127 where g(63728127) = 950
14.67 real 14.29 user 0.28 sys
しかもjkondoのポッドキャストを聞きながら、である。Intel Core Duoって優秀かも。
コードは以下のとおり。見ての通り、cacheはunsigned shortで実装している。それで充分であることはg(n)を観察すればわかるので。その結果、200MB弱のメモリーでitaさんよりよい成績を上げている。あと、実行時間の長いプログラムのための工夫として、65565回ごとに途中結果を表示するようにしている。この部分をはしょればもう少し早くなるかも知れない。
#include <stdio.h>
#include <stdlib.h>
#ifndef CACHE_SIZE
#define CACHE_SIZE 64*1024*1024 /* large enough */
#endif
#define MAX_U16 65535
typedef unsigned short U16;
typedef long long I64;
U16 *cache;
int cachesize;
#define lookup(n) ((n) < cachesize ? cache[(n)] : 0)
#define store(n,g) if ((n) < cachesize && g <= MAX_U16) cache[(n)] = (g)
I64 g(I64 n){
I64 result = lookup(n);
if (result) return result;
I64 i = n;
I64 l;
result = 1;
while (i > 1){
i = i & 1 ? i*3+1 : i >> 1;
l = lookup(i);
if (l){
result += l;
break;
}else{
result++;
}
}
if (i < 1) fprintf(stderr, "overflow! g(%qd) = %qd\n", n, i);
store(n, result);
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;
}
if (i & 0xffff) continue;
printf("h(%qd) = %qd where g(%qd) = %qd\r", i, nmax, nmax, gmax);
}
result[0] = nmax; result[1] = gmax;
return result;
}
#define min(x,y) ((x) < (y) ? (x) : (y))
void init_cache(I64 n){
cachesize = min(n, CACHE_SIZE);
cache = (U16 *)calloc(cachesize, sizeof(U16));
if (cache == NULL) exit(-1);
}
int main(int argc, char **argv){
I64 n = 0xffffffff; /* look @ this! */
if (argc > 1){
n = atoi(argv[1]);
}
init_cache(n);
I64 *hg = h(n);
printf("h(%qd) = %qd where g(%qd) = %qd\n", n, hg[0], hg[0], hg[1]);
return 0;
}
Enjoy!
Dan the Number Cruncher
追記:
逆まわし、すなわち最大の数からチェックした方がcacheがより有効に使われることに気がついた。
/usr/bin/time -l ./a.out 100000000
h(100000000) = 63728127 where g(63728127) = 950
184.75 real 183.95 user 0.26 sys
上に以下のpatch。
--- gogo.c.orig 2006-07-14 13:33:49.000000000 +0900
+++ gogo.c 2006-07-14 15:12:55.000000000 +0900
@@ -38,7 +38,7 @@
I64 *h(I64 n){
I64 i, nmax = 0, gmax = 0, gnext = 0;
static I64 result[2];
- for (i = 1; i <= n; i++){
+ for (i = n; i > 1 ; i--){
gnext = g(i);
#ifdef VERBOSE
fprintf(stderr, "g(%qd) = %qd\r", i, gnext);
@@ -47,7 +47,8 @@
nmax = i; gmax = gnext;
}
if (i & 0xffff) continue;
- printf("h(%qd) = %qd where g(%qd) = %qd\r", i, nmax, nmax, gmax);
+ printf("nmax = %qd, gmax = %qd, n = %qd\r", nmax, gmax, i);
+ fflush(stdout);
}
result[0] = nmax; result[1] = gmax;
return result;
@@ -64,7 +65,7 @@
int main(int argc, char **argv){
I64 n = 0xffffffff; /* look @ this! */
if (argc > 1){
- n = atoi(argv[1]);
+ n = atoll(argv[1]);
}
init_cache(n);
I64 *hg = h(n);
Posted by dankogai at 14:00│Comments(0)│TrackBack(1)
この記事へのトラックバックURL
この記事へのトラックバック
Danさんのコードを元に改良をしていたのですが,itaさんがほとんど同じことをされていたので,もう一捻りしてみました。
その結果10^10がcygwin on Pentium4 2.6GHz上で6分程度で計算できるくらい
どこまで行けるか? Collatz予想【Typemiss.net】at 2006年07月17日 00:21