challenge Meertens数

お題#100「正整数のゲーデル数化?」で定義した goedel を適用すると自分自身になるような数,すなわち goedel (n) == n となるような正整数 n を見つける関数を定義してください.

このような数のことをMeertens数と言うそうです.

32bitsの符号なし整数(あるいは10進10桁整数)までの範囲で探すのにどのくらい計算時間がかかったかをCPUのスペックとともに教えてください.また,その実装で64bit符号なし整数(あるいは10進20桁整数)までの範囲で探すのにどのくらい計算時間がかかりそうか見積ってください(もちろん実際に計算して計算時間を示していただくのでもかまいません).

補足:

自分でやってみたHaskellでは30行ほどのプログラムで、10進10桁の範囲までの探索は ghci で 4.56秒(CPU Intel Core 2 Duo 2.6GHz)でした。

Posted feedbacks - Nested

Flatten Hidden

(meertens n) で n 桁以下のものを探します。n=10 のとき、CLISP で1.6秒、SBCL で 0.5秒ほど(Celeron 2.66GHz)。

8~13 で試してみたところ桁数を1増やすごとにほぼ3倍になっているようなので、20桁の場合 SBCL なら単純計算で8時間ぐらいでしょうか。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
(defconstant *primes*
  '(2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97))

(defun primes (n) (loop repeat n for p in *primes* collect p))
(defun basis (n) (loop repeat n for x = (expt 10 (1- n)) then (/ x 10) collect x))

(defun f (primes basis bound prod sum zero-ok)
  (if primes
      (loop for i from (if zero-ok 0 1) to 9
        as dp = (expt (car primes) i) and ds = (* i (car basis))
        while (<= dp bound) nconc
        (f (cdr primes) (cdr basis) (floor bound dp)
           (* prod dp) (+ sum ds)
           t))
    (if (= prod sum) (list sum) nil)))

(defun meertens (n)
  (loop for i from 1 to n nconc (f (primes i) (basis i) (expt 10 i) 1 0 nil)))

少し効率化して時間が 2/3 程度になりました。寝てる間に20桁まで探させてみたところ約三時間で終わりましたが、結局一つしか見つからなくてちょっと残念です。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
(defun f (primes basis bound prod sum zero-ok)
  (declare (optimize (speed 3) (safety 0))
           (type integer bound prod sum))
  (loop repeat (if zero-ok 10 9)
    with p fixnum = (car primes) and prest = (cdr primes)
    and b integer = (car basis) and brest = (cdr basis)
    for newbd integer = (if zero-ok bound (floor bound p)) then (floor newbd p)
    for newprod integer = (if zero-ok prod (* prod p)) then (* newprod p)
    for newsum integer = (if zero-ok sum (+ sum b)) then (+ newsum b)
    while (and (plusp newbd) (<= newprod newsum)) nconc
    (if prest
        (f prest brest newbd newprod newsum t)
      (if (= newprod newsum) (list newsum) nil))))

Haskell の勉強にと移植してみました。GHCi で33秒とあんまり速くはありませんが、わりとすっきりしたコードになったので投稿してみます。ついでに中身について少し説明でも。

上の桁から順に決めていくのですが、その際に桁数を固定することでゲーデル数化する前と後の数(sum, prod)の両方を部分的に計算でき、枝刈りがやりやすくなります。

実際にやっているのは次の二つですが、他にも末尾の 0 の数で刈れたりするかもしれません。

  • 積>和 になったら打ち切り(積のほうが速く増えるので)
  • bound にまだ決まっていない部分の積の上限を保持し、それを超えたら打ち切り

後者のは実質「積が 10^i を超えたら」なのですが、上のようにした方が速いようです。(扱う数が fixnum に収まりやすい?)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
primes = flip take [2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
                     31, 37, 41, 43, 47, 53, 59, 61, 67, 71]
basis n = map (\x -> 10^x) [n-1,n-2..0]

f :: [Integer] -> [Integer] -> Integer -> Integer -> Integer -> Integer -> [Integer]
f [] [] _ prod sum _
    | prod == sum = [sum]
    | otherwise   = []
f (p:ps) (b:bs) bound prod sum start
    | start == 0 && prod > sum = []
    | otherwise =
        [x | i <- [start..9], p^i <= bound,
             x <- f ps bs (div bound $ p^i) (prod * p^i) (sum + b * i) 0]

meertens n = [x | i <- [1..n], x <- f (primes i) (basis i) (10^i) 1 0 1]
あーそうか。桁数を固定すればよかったのか。
そうすればかなりの量が枝狩りできますね。

桁数可変で探索してたから、枝がまったく狩れなかった。

ちょっと書き直してみよう。
SMLにも移植。爆速になったけど、MLtonでもSBCLより遅い。。。
10桁で0.79秒、20桁で9時間56分。

  ./110 10  0.79s user 0.00s system 99% cpu 0.795 total
  ./110 20  35746.36s user 4.25s system 99% cpu 9:56:05.18 total
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
val p = [
   2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
  31, 37, 41, 43, 47, 53, 59, 61, 67, 71
  ] : IntInf.int list

fun basis n = (rev o map (fn x => IntInf.pow (10, x)) o List.tabulate) (n, fn x => x)

fun meertens n = let
  val lst = List.tabulate (n, fn x => x + 1)

  open IntInf

  fun loop _ [] _ prod sum _ =
    if prod = sum then print (toString sum ^ "\n") else ()
    | loop (p::ps) (b::bs) bound prod sum start = let
      fun f x = let
        val pp = pow (p, x)
      in
        if pp <= bound then
          loop ps bs (bound div pp) (prod * pp) (sum + b * fromInt x) 0
        else ()
      end

      open Int
      val lst = List.tabulate (10 - start, fn x => x + start)
    in
      app f lst
    end
in
  app (fn i => loop p (basis i) (IntInf.pow (10, i)) 1 0 1) lst
end

val _ = (meertens o valOf o Int.fromString o hd o CommandLine.arguments) ()

面白くはないけど、これでちょっと速くなりました。

SBCL との差は……あまり自信ありませんが、loop の中でクロージャを作って呼び出すオーバーヘッドとか?

1
2
3
4
5
6
7
8
9
       end

       open Int
-      val lst = List.tabulate (10 - start, fn x => x + start)
+      val lst = if start = 0 then [0,1,2,3,4,5,6,7,8,9]
+                else [1,2,3,4,5,6,7,8,9]
     in
       app f lst
     end

独自のpowでさらに枝刈り。20桁での時間は約4時間までになりました。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
--- 110.sml     2007-12-21 19:45:26.606130399 +0000
+++ 110-3.sml   2007-12-21 20:58:19.679130289 +0000
@@ -13,16 +13,30 @@
   fun loop _ [] _ prod sum _ =
     if prod = sum then print (toString sum ^ "\n") else ()
     | loop (p::ps) (b::bs) bound prod sum start = let
+      fun pow (x, y) = let
+        fun loop r 0 = 1
+          | loop r 1 = r
+          | loop r n = let
+              val z = r * x
+            in
+              if z <= bound then loop z (n - 1)
+              else bound + 1
+            end
+      in
+        loop x y
+      end
+
       fun f x = let
         val pp = pow (p, x)
       in
         if pp <= bound then
-          loop ps bs (bound div pp) (prod * pp) (sum + b * fromInt x) 0
+          loop ps bs (bound div pp) (prod * pp) (sum + b * x) 0
         else ()
       end

       open Int
-      val lst = List.tabulate (10 - start, fn x => x + start)
+      val lst = if start = 0 then [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+                else [1, 2, 3, 4, 5, 6, 7, 8, 9]
     in
       app f lst
     end

そっか、そこ刈れてなかったんですね。それなら f の繰り返しを再帰呼び出しにするとまだ速くなります。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
--- 4926.sml    2007-12-22 15:37:01.000000000 +0900
+++ meertens.sml        2007-12-22 15:36:18.000000000 +0900
@@ -26,11 +26,12 @@
         loop x y
       end

-      fun f x = let
+      fun f [] = ()
+        | f (x::xs) = let
         val pp = pow (p, x)
       in
         if pp <= bound then
-          loop ps bs (bound div pp) (prod * pp) (sum + b * x) 0
+          (loop ps bs (bound div pp) (prod * pp) (sum + b * x) 0; f xs)
         else ()
       end

@@ -38,7 +39,7 @@
       val lst = if start = 0 then [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
                 else [1, 2, 3, 4, 5, 6, 7, 8, 9]
     in
-      app f lst
+      f lst
     end
 in
   app (fn i => loop p (basis i) (IntInf.pow (10, i)) 1 0 1) lst
MLtonでコンパイルして、符号無し32bit整数までで約2時間40分。64bitでは834174年。遅すぎですね。

  mlton meertes.sml
  ./meertes 4294967295
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
fun goedel n = let
  open IntInf

  val p = [
     2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
    31, 37, 41, 43, 47, 53, 59, 61, 67, 71
    ]
  val a = map (valOf o Int.fromString o str) ((explode o toString) n)
in
  ListPair.foldl (fn (x, y, z) => pow (x, y) * z) 1 (p, a)
end

fun meertens n = let
  fun loop i =
    if i > n then ()
    else
      if goedel n = n then (print (IntInf.toString n ^ "\n"); loop (i + 1))
      else loop (i + 1)
  in
    loop 0
  end

val _ = (meertens o valOf o IntInf.fromString o hd o CommandLine.arguments) ()

あ、CPUはPentiumD 3.0GHzです。

うっかり "{0}" を書き忘れたまま実行して、2時間を棒に振りました。やりなおしやりなおし。
32bit 符号無し整数42億9496万7296個を、実に2時間1分18秒で総当たり完了。8桁の数が1個だけヒットしました。
ちなみに 64bit 符号無しだと、およそ100万年でしょうか。

……何かが根本的に間違っている気がします orz
それから、明らかに num.ToString() と Math.Pow が遅いよなぁ……と。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
using System;
using System.Collections.Generic;
static class Program {
    static void Main() {
        DateTime before = DateTime.Now;
        Console.WriteLine("meertens = {0}", string.Join(", ",
            Array.ConvertAll<long, string>(Meertens(), delegate(long x) { return x.ToString(); })
        ));
        Console.WriteLine("time = {0}", DateTime.Now - before);
    }
    static long[] Meertens() {
        int[] primes = Primes(20);
        List<long> meertens = new List<long>();
        for(long num = uint.MaxValue; 0 <= num; num--)
            if (Goedel(num, primes) == num)
                meertens.Add(num);
        return meertens.ToArray();
    }
    static double Goedel(long num, int[] primes) {
        double goedel = 1;
        string numstr = num.ToString();
        for(int i = 0; i < numstr.Length; i++)
            goedel *= Math.Pow(primes[i], numstr[i] - '0');
        return goedel;
    }
    static int[] Primes(int count) {
        List<int> primes = new List<int>();
        primes.Add(2);
        for(int num = 3; primes.Count < count; num += 2) {
            foreach(int p in primes)
                if (num % p == 0)
                    goto SKIP;
            primes.Add(num);
        SKIP:;
        }
        return primes.ToArray();
    }
}
とりあえず ToString 使わないように改造してみた。30秒速くなった。変化がねえ(;´Д`)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
--- meertens.old.cs     Wed Dec 19 01:16:26 2007
+++ meertens.cs Wed Dec 19 01:17:20 2007
@@ -18,9 +18,10 @@
     }
     static double Goedel(long num, int[] primes) {
         double goedel = 1;
-        string numstr = num.ToString();
-        for(int i = 0; i < numstr.Length; i++)
-            goedel *= Math.Pow(primes[i], numstr[i] - '0');
+        for(int i = Length(num) - 1; 0 <= i; i--) {
+            goedel *= Math.Pow(primes[i], num % 10);
+            num /= 10;
+        }
         return goedel;
     }
     static int[] Primes(int count) {
@@ -34,5 +35,14 @@
         SKIP:;
         }
         return primes.ToArray();
+    }
+    static int Length(long num) {
+        long num2 = 10;
+        int cnt = 1;
+        while(num2 <= num) {
+            cnt++;
+            num2 *= 10;
+        }
+        return cnt;
     }
 }
あ、ごめんなさい。CPU は C2D の 2.0GHz です。ただ優先度は通常以下で実行してました。
とりあえず、再帰で書いてみた。
searchMeertens(n)でn桁の範囲で探索します。
8桁で一つ見つかりました。

8桁を調べるのに160秒かかりました。
桁数が上がるごとに10倍の計算量になるので、
10桁では4時間くらいかなぁ。

とりあえず、累乗計算をしないようにしたけど、これからどうしようかな。
もうちょっと枝が刈れそうな気がするけど。

Cで書けばもうちょっと早くなるかな。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
import time

def CreatePrimes(n):
    primes = []
    c = 2
    while len(primes) < n:
        primes.append(c)
        for x in primes[:-1]:
            if c%x == 0:
                primes.pop();
                break
        c += 1
    return primes

def searchMeertens(keta):
    def _search(keta,count,r,n,primes,first):
        n *= 10
        for x in range(first,10):
            if x:
                r *= primes[count]
                n += 1
            if r == n:
                print "Found Meertens.",n
            if keta > 1:
                _search(keta - 1, count + 1, r ,n ,primes, 0)
    p = CreatePrimes(20)
    _search(keta,0,1,0,p,1)


keta = 10
t0 = time.time()
t1 = t0

for n in range(1,keta+1):
    searchMeertens(n)
    print n , time.time() - t1 , "sec"
    t1 = time.time()
    
print "total time", time.time() - t0

Cで書き直したら恐ろしく速くなった。 10桁実行するのに176秒@AthlonXP2500+ pythonで書いたのと比較して、だいたい100倍高速化した計算に。

ゲーテル数って10桁でintの範囲をあっさりと突破するのね・・・バグに気づかなかった。 unsigned long long int なんて初めて使ったよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
#include <time.h>

int primes[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};

void searchMeertens(int keta, int count, unsigned long long int r, unsigned long long int n, int first){
    int x;
    n *= 10;
    for(x = first; x < 10; x++){
        if (x){
            r *= primes[count];
            n++;
        }    
        if (r == n){
            printf("Found Meertens %d\n",n);
        }
        if (keta > 1){
            searchMeertens(keta -1, count +1, r, n, 0);
        }
    }
}


int main(void)
{
    int keta;
    int x;
    time_t t;
    keta = 10;
    t = time(NULL);
    for(x = 1; x <= keta; x++){
        searchMeertens(x,0,1,0,1);
        printf("%d %dsec\n",x, time(NULL)-t);
        t = time(NULL);
    }
    printf("push anykey to exit.");
    getchar();
}
一応手元で11桁まで回り終わりましたが、
一桁増えると計算時間も10倍になりました。
(当然と言えば当然ですが)

なので、google先生に聞いたところ、20桁では
(176 * (10^10)) *秒 = 55 772.2258 年
だそうです。



とりあえず40秒くらいまで縮まったけど、
他の人が何で爆速なのかが分からない。
あとどんな枝狩りすりゃいいねん。

枝狩りで分岐予測がこける関係で、Pen4系では遅いと思う。

関数型言語は読めないよ・・・
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
#include <stdio.h>
#include <time.h>

int primes[] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97};

void _searchMeertens(int keta, int count, unsigned long long int r, unsigned long long int n){
    int x;
    n *= 10;
    for(x = (count == 0); x < 10; x++){
        if (x){
            r *= primes[count];
            n++;
        }    
        if (keta == 0){
            if(r > n){        //枝狩り
                return;    
            }
            else if (r == n){
                printf("Found Meertens %d\n",n);
            }
        }else{
            _searchMeertens(keta -1, count +1, r, n);
        }
    }
}

void searchMeertens(int keta)
{
    int x;
    time_t t;
    t = time(NULL);
    for(x = 0; x < keta; x++){
        _searchMeertens(x,0,1,0);
        printf("keta = %d time = %d\n", x+1, time(NULL) - t);
        t = time(NULL);
    }
}

int main(void)
{
    int keta;
    time_t t;

    keta = 10;
    t = time(NULL);
    searchMeertens(keta);
    printf("%dsec\n", time(NULL)-t);

    printf("push anykey to exit.");
    getchar();
}

r が、その桁の最大値を超えたときを狩れば結構削れますよ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void _searchMeertens(int keta, int count, unsigned long long int r, unsigned long long int n, unsigned long long int nmax)
{
    int x;
    n *= 10;
    for (x = (count == 0); x < 10; x++) {
        if (x) {
            r *= primes[count];
            if (r > nmax)    /* 枝狩り2 */
                return;
            n++;
        }
        if (keta == 0) {
            if(r > n)       /* 枝狩り*/
                return;
            if (r == n)
                printf("Found Meertens %d\n", n);
        }else{
            _searchMeertens(keta -1, count + 1, r, n, nmax);
        }
    }
}

void searchMeertens(int keta)
{
    int x;
    unsigned long long int nmax;
    time_t t;
    t = time(NULL);
    for (x = 0; x < keta; x++) {
        nmax = (unsigned long long)pow(10.0, x + 1) - 1UL;
        _searchMeertens(x, 0, 1, 0, nmax);
        printf("keta = %d time = %d\n", x + 1, time(NULL) - t);
        t = time(NULL);
    }
}
なるほどなーと納得しました。

手元の奴で10桁が1秒未満で完了した・・・。
確かにそうすればよかったのかと後悔してます。

ちなみに私の環境での実行結果。
あまり時間が無かったので、18桁まで実行させましたが、20桁は20分くらいですね。
なのでトータルタイムは30分くらいでしょうか。

keta = 1 time = 0
keta = 2 time = 0
keta = 3 time = 0
keta = 4 time = 0
keta = 5 time = 0
keta = 6 time = 0
keta = 7 time = 0
Found Meertens 81312000
keta = 8 time = 0
keta = 9 time = 0
keta = 10 time = 0
keta = 11 time = 0
keta = 12 time = 0
keta = 13 time = 1
keta = 14 time = 2
keta = 15 time = 6
keta = 16 time = 16
keta = 17 time = 45
keta = 18 time = 127
最初の乗算(n *= 10)を外に出してみれば、もうちょいいけるかも....。

keta = 14 time = 2
keta = 15 time = 4
keta = 16 time = 12
keta = 17 time = 34
keta = 18 time = 96

微々たるもんしか減らないorz。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <stdio.h>
#include <time.h>

#ifndef ARRAYSIZE
#define ARRAYSIZE(x)  (sizeof(x)/sizeof(x[0]))
#endif

unsigned long long int dec_base[20];

void _searchMeertens(int keta, int d, unsigned long long int r, unsigned long long int n) {
    int x;
    for (int x = (d == 0); x < 10; x++) {
        if (x > 0) {
            r *= primes[d];
            if (r >= dec_base[keta])
                return;
            n += dec_base[keta - (d + 1)];
        }
        if ((d + 1) >= keta) {
            if (r >= n) {
                if (r == n)
                    printf("Found Meertens %lld\n", n);
                return;
            }
        } else {
            _searchMeertens(keta, d + 1, r, n);
        }
    }
}

void searchMeertens(int keta) {
    int x;
    time_t t;
    t = time(NULL);
    for (x = 1; x <= keta; x++) {
        _searchMeertens(x, 0, 1, 0);
        printf("keta = %d time = %d\n", x, time(NULL) - t);
        t = time(NULL);
    }
}

void main() {
    int i;
    dec_base[0] = 1;
    for (i = 1; i < ARRAYSIZE(dec_base); i++) {
        dec_base[i] = dec_base[i - 1] * 10;
    }
    searchMeertens(19);
}

多倍長演算を意識する・しないかで言語間の格差が出ますなぁ。

各桁の素数を0~9で乗算したテーブル作成+論理演算+枝切り。 残念ながら20桁だとunsigned long long(64bit)を溢れてしまうので、19桁までしか計算できません。 これ以上は面倒なので勘弁してくださいorz

10桁の計算結果は、1つ(81312000)。 計算時間は23.203[秒](BITSET_SIZE=136, Pen4 3.06GHz, WinXP SP2, VS2005)。ゲーデル数チェック時に使用するbitset(のbit数)に依存する割合が高いので、適正なbit数にすればちょびっと速くなります(10桁を72bitで計算して10.156[秒])。

枝切りもあるので断定できませんが、テーブル生成時間がほぼ0なので 20桁は10桁の時間×(10の10乗)ぐらいなのかなと。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
#include <time.h>
#include <iostream>
#include <bitset>

#ifndef ARRAYSIZE
#define ARRAYSIZE(x)  (sizeof(x)/sizeof(x[0]))
#endif

#define BITSET_SIZE (64*2+8)
//#define BITSET_SIZE (32*2+8)

static unsigned long long digit_base[] = {
0x0000000000000001, 0x000000000000000a,
0x0000000000000064, 0x00000000000003E8,
0x0000000000002710, 0x00000000000186A0,
0x00000000000F4240, 0x0000000000989680,
0x0000000005F5E100, 0x000000003B9ACA00,
0x00000002540BE400, 0x000000174876E800,
0x000000E8D4A51000, 0x000009184E72A000,
0x00005AF3107A4000, 0x00038D7EA4C68000,
0x002386F26FC10000, 0x016345785D8A0000,
0x0DE0B6B3A7640000, 0x8AC7230489E80000 };

static unsigned long long prime[20] = {
 2,  3,  5,  7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71 };

static std::bitset<BITSET_SIZE> PRIME_TABLE[ARRAYSIZE(prime)][10];

int CompareBitset(std::bitset<BITSET_SIZE> a, std::bitset<BITSET_SIZE> b) {
    for (int n = BITSET_SIZE - 1; n >= 0; n--) {
        if (a[n] != b[n]) { return a[n] ? 1 : -1; }
    }
    return 0;
}

std::bitset<BITSET_SIZE> bitset_add(std::bitset<BITSET_SIZE> a, std::bitset<BITSET_SIZE> b) {
    std::bitset<BITSET_SIZE> c = (a & b) << 1;
    if (c.any()) { return bitset_add((a ^ b), c); }
    else         { return (a ^ b); }
}

std::bitset<BITSET_SIZE> bitset_multi(std::bitset<BITSET_SIZE> a, std::bitset<BITSET_SIZE> b) {
    std::bitset<BITSET_SIZE> ans;
    if (a.any() && b.any()) {
        for (int i = 0; a.any() && (i < BITSET_SIZE); i++, a <<= 1) {
            if (b[i]) { ans = bitset_add(ans, a); }
        }
    }
    return ans;
}

std::bitset<BITSET_SIZE> bitset_pow(std::bitset<BITSET_SIZE> a, unsigned int n) {
    std::bitset<BITSET_SIZE> v((unsigned long)1);
    while (n != 0) {
        if (n & 0x1)       { v = bitset_multi(v, a); }
        if ((n /= 2) != 0) { a = bitset_multi(a, a); }
    }
    return v;
}

unsigned int digit(unsigned long long n, unsigned int base)
{
    unsigned int d = 1;
    for (; n > (base - 1); n /= base) { d++; }
    return d;
}

std::bitset<BITSET_SIZE> ulonglong2bitset(unsigned long long v) {
    std::bitset<BITSET_SIZE> high((unsigned long)(v >> (sizeof(v)*4)));
    std::bitset<BITSET_SIZE> low((unsigned long)v);
    return ((high << (sizeof(v)*4)) | low);
}

void InitPrimeTable() {
    for (int i = 0; i < ARRAYSIZE(PRIME_TABLE); i++) {
        for (int j = 0; j < ARRAYSIZE(PRIME_TABLE[0]); j++) {
            PRIME_TABLE[i][j] = bitset_pow(ulonglong2bitset(prime[i]), j);
        }
    }
}

int CheckMeertensNumber(unsigned long long n) {
    std::bitset<BITSET_SIZE> m = ulonglong2bitset(n);
    std::bitset<BITSET_SIZE> g(1UL);
    for (unsigned int d = digit(n, 10); d > 0; d--, n /= 10) {
        unsigned int p = (unsigned int)(n % 10);
        if (CompareBitset(PRIME_TABLE[d - 1][p], m) > 0)
            return 1;
        g = bitset_multi(g, PRIME_TABLE[d - 1][p]);
        if (CompareBitset(g, m) > 0)
            return 1;
    }
    return (m == g) ? 0 : -1;
}

int SearchMeertensNumber(unsigned long long base, unsigned int d, int& cnt) {
    if (d <= 0) {
        int ret = CheckMeertensNumber(base);
        if (ret == 0) {
            std::cout << base << std::endl;
            cnt++;
        }
        return ret;
    }
    if (SearchMeertensNumber(base, d - 1, cnt) > 0)
        return (base == 0) ? 0 : 1;
    for (unsigned long long i = 1; i < 10; i++) {
        base += digit_base[d - 1];
        if (SearchMeertensNumber(base, d - 1, cnt) > 0)
            break;
    }
    return 0;
}

void main(int argc, char