目次

  1. 問題
    1. 条件
    2. 実現性に関する補足
    3. 参考
  2. プログラム
  3. ログ
  4. 結果
    1. 閉路の数
    2. 閉路一覧
    3. 出発進行
  5. 関連リンク
  6. 更新履歴

1. 問題

プラレールの直線レールと曲線レールを合わせて N 本使ってできる閉路は何通りあるか。

1.1. 条件

曲線レールは直線レールの長さを半径とする中心角 45 度の円弧とする。

ある閉路とそれを回転または裏返してできる閉路は同じと見なす。

1.2. 実現性に関する補足

これは平面上の線分と円弧の配置を数える問題である。実際にプラレールで組み立てることができなくてもよい。

例えば、レールが交差している場所があってもよい。レールだけなら多少歪ませれば組み立てられるが、列車を走らせるにはポイントレールや坂レールを使って平面交差や立体交差を作らなければならない。

更に、レールが重なっている場所があってもよい。曲線レールを 8 本使った円を 2 周して 16 本使ったことにしてよい。実際に 16 本使って円を 2 周させるとコイルになってしまい始点と終点を繋ぐことができない。

1.3. 参考

三谷先生のツイート

2. プログラム

閉路の数が一致するまで三谷先生のプログラムを見ないで作った。座標の変化をまとめた配列が似ているのは偶然。

plarail.c
     1: //------------------------------------------------------------------------------------------------
     2: //
     3: //  ファイル名
     4: //    plarail.c
     5: //
     6: //  作った人
     7: //    Makoto Kamada
     8: //
     9: //  更新した日
    10: //    2019-06-15
    11: //
    12: //  ウェブサイト
    13: //    https://stdkmd.net/plarail/
    14: //
    15: //------------------------------------------------------------------------------------------------
    16: //
    17: //  タイトル
    18: //    プラレールの閉路の数え上げ
    19: //
    20: //  問題
    21: //    プラレールの直線レールと曲線レールを合わせて N 本使ってできる閉路は何通りあるか。
    22: //
    23: //  条件
    24: //    曲線レールは直線レールの長さを半径とする中心角 45 度の円弧とする。
    25: //    ある閉路とそれを回転または裏返してできる閉路は同じと見なす。
    26: //
    27: //  参考
    28: //    三谷先生のツイート
    29: //    https://twitter.com/jmitani/status/880070117635235840
    30: //    https://twitter.com/jmitani/status/1136228098314125313
    31: //
    32: //------------------------------------------------------------------------------------------------
    33: 
    34: #include <stdio.h>
    35: #include <stdlib.h>
    36: #include <string.h>
    37: #include <math.h>
    38: #include <time.h>
    39: 
    40: //レールの座標
    41: //  右が+x、下が+yの平面座標を用いる
    42: //    ┌→+x
    43: //    ↓
    44: //    +y
    45: //  直線レールはS、曲線レールは進行方向に対して右に曲がるものをR、左に曲がるものをLと書く
    46: //  Sは長さが2の線分、RとLは半径が2で中心角が45度の円弧とする
    47: //  右を向いているとき
    48: //    Sは右に2進む
    49: //    Rは右にsqrt(2)、下に2-sqrt(2)進み、右に45度方向転換する
    50: //    Lは右にsqrt(2)、上に2-sqrt(2)進み、左に45度方向転換する
    51: //                            ○
    52: //                           /│
    53: //                          / │
    54: //                         /  │
    55: //    ●SSSSSSSSS●RR────┐  /   │
    56: //              │  RR  │ /    │
    57: //              │    RR│/     │
    58: //              │      ●      │
    59: //              │     / LL    │
    60: //              │    /   \LL  │
    61: //              │   /     \ LL●
    62: //              │  /       \ / 
    63: //              │ /            
    64: //              │/             
    65: //              ○              
    66: //    +-----+---------------------------+-------------------------------+-------------------------------+
    67: //    |     |            S=0            |              R=1              |              L=2              |
    68: //    |  h  +----------+----------+-----+------------+------------+-----+------------+------------+-----+
    69: //    |     |    dx    |    dy    |  dh |     dx     |     dy     |  dh |     dx     |     dy     |  dh |
    70: //    +-----+----------+----------+-----+------------+------------+-----+------------+------------+-----+
    71: //    |   0 |     2    |     0    |   0 |   sqrt(2)  |  2-sqrt(2) |  45 |   sqrt(2)  | -2+sqrt(2) | -45 |
    72: //    |  45 |  sqrt(2) |  sqrt(2) |   0 |  2-sqrt(2) |   sqrt(2)  |  45 |   sqrt(2)  |  2-sqrt(2) | -45 |
    73: //    |  90 |     0    |     2    |   0 | -2+sqrt(2) |   sqrt(2)  |  45 |  2-sqrt(2) |   sqrt(2)  | -45 |
    74: //    | 135 | -sqrt(2) |  sqrt(2) |   0 |  -sqrt(2)  |  2-sqrt(2) |  45 | -2+sqrt(2) |   sqrt(2)  | -45 |
    75: //    | 180 |    -2    |     0    |   0 |  -sqrt(2)  | -2+sqrt(2) |  45 |  -sqrt(2)  |  2-sqrt(2) | -45 |
    76: //    | 225 | -sqrt(2) | -sqrt(2) |   0 | -2+sqrt(2) |  -sqrt(2)  |  45 |  -sqrt(2)  | -2+sqrt(2) | -45 |
    77: //    | 270 |     0    |    -2    |   0 |  2-sqrt(2) |  -sqrt(2)  |  45 | -2+sqrt(2) |  -sqrt(2)  | -45 |
    78: //    | 315 |  sqrt(2) | -sqrt(2) |   0 |   sqrt(2)  | -2+sqrt(2) |  45 |  2-sqrt(2) |  -sqrt(2)  | -45 |
    79: //    +-----+----------+----------+-----+------------+------------+-----+------------+------------+-----+
    80: //  座標を2*a+sqrt(2)*bの形で表現する
    81: static const int coeff[3][8][5] = {
    82:   {  //S
    83:     {  1,  0,  0,  0,  0 },  //  0
    84:     {  0,  1,  0,  1,  0 },  // 45
    85:     {  0,  0,  1,  0,  0 },  // 90
    86:     {  0, -1,  0,  1,  0 },  //135
    87:     { -1,  0,  0,  0,  0 },  //180
    88:     {  0, -1,  0, -1,  0 },  //225
    89:     {  0,  0, -1,  0,  0 },  //270
    90:     {  0,  1,  0, -1,  0 },  //315
    91:   },
    92:   {  //R
    93:     {  0,  1,  1, -1,  1 },  //  0
    94:     {  1, -1,  0,  1,  1 },  // 45
    95:     { -1,  1,  0,  1,  1 },  // 90
    96:     {  0, -1,  1, -1,  1 },  //135
    97:     {  0, -1, -1,  1,  1 },  //180
    98:     { -1,  1,  0, -1,  1 },  //225
    99:     {  1, -1,  0, -1,  1 },  //270
   100:     {  0,  1, -1,  1,  1 },  //315
   101:   },
   102:   {  //L
   103:     {  0,  1, -1,  1, -1 },  //  0
   104:     {  0,  1,  1, -1, -1 },  // 45
   105:     {  1, -1,  0,  1, -1 },  // 90
   106:     { -1,  1,  0,  1, -1 },  //135
   107:     {  0, -1,  1, -1, -1 },  //180
   108:     {  0, -1, -1,  1, -1 },  //225
   109:     { -1,  1,  0, -1, -1 },  //270
   110:     {  1, -1,  0, -1, -1 },  //315
   111:   },
   112: };
   113: 
   114: //文字
   115: //  S=0,R=1,L=2
   116: static const char chr[4] = { 'S', 'R', 'L', 'X' };
   117: 
   118: //  メイン
   119: int main (int argc, char *argv[]) {
   120: 
   121:   //パラメータ
   122:   int numofRails = 8;  //レールの数
   123:   char outputFileName[1024] = "";  //出力ファイル名
   124:   switch (argc) {
   125:   case 3:
   126:     if (sscanf (argv[2], "%1023[-().0-9A-Z_a-z~]", outputFileName) != 1) {
   127:       goto help;
   128:     }
   129:   case 2:
   130:     if (sscanf (argv[1], "%d", &numofRails) != 1 ||
   131:         numofRails < 2 || 32 < numofRails) {  //レールの数が2~32でない
   132:       goto help;
   133:     }
   134:     break;
   135:   default:
   136:   help:
   137:     printf ("usage: plarail <number-of-rails(8-32)> <output-file-name>\n");
   138:     return 1;
   139:   }
   140: 
   141:   //計測開始
   142:   struct timespec startTime;
   143:   clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &startTime);
   144: 
   145:   //最上位の3進数のリストを作る
   146:   int (*ternaryList1)[8192] = calloc (8192 * (8 + 1), sizeof (ternaryList1[0][0]));  //最上位の3進数のリスト
   147:   ternaryList1[0][0] = 0;  //0桁
   148:   ternaryList1[0][1] = -1;
   149:   for (int d = 1; d <= 8; d++) {  //最上位の桁数
   150:     int w = d << 1;  //ビット数
   151:     int m = 0xffff >> (16 - w);  //下位wビットが1
   152:     int *p = ternaryList1[d];  //d桁の3進数のリストの先頭
   153:     for (int n = 0; n <= m; n++) {  //nはd桁の4進数
   154:       int x = (n >> 1) & n & 0x5555;  //3を抽出する
   155:       if (x) {  //3があるとき
   156:         n |= (x & -x) - 1;  //一番右にある3の右側をすべて3にする
   157:         continue;
   158:       }
   159:       //nはd桁の3進数
   160:       for (int i = w - 2; 0 < i; i -= 2) {
   161:         if ((n & (m >> i)) < (n >> i)) {  //下位w-iビットが上位w-iビットより若い
   162:           goto nextN;
   163:         }
   164:       }
   165:       x = ((n >> 1) & 0x5555) | ((n & 0x5555) << 1);  //RとLを入れ替える
   166:       for (int i = w - 2; 0 <= i; i -= 2) {
   167:         if ((x & (m >> i)) < (n >> i)) {  //RとLを入れ替えた下位w-iビットが元の上位w-iビットより若い
   168:           goto nextN;
   169:         }
   170:       }
   171:       //逆順にする
   172:       x = ((n & 0x3333) << 2) | ((n >> 2) & 0x3333);
   173:       x = ((x & 0x0f0f) << 4) | ((x >> 4) & 0x0f0f);
   174:       x = ((x << 8) | (x >> 8)) & 0xffff;
   175:       x >>= 16 - w;
   176:       for (int i = w - 2; 0 <= i; i -= 2) {
   177:         if ((x & (m >> i)) < (n >> i)) {  //逆順にした下位w-iビットが元の上位w-iビットより若い
   178:           goto nextN;
   179:         }
   180:       }
   181:       x = ((x >> 1) & 0x5555) | ((x & 0x5555) << 1);  //RとLを入れ替える
   182:       for (int i = w - 2; 0 <= i; i -= 2) {
   183:         if ((x & (m >> i)) < (n >> i)) {  //逆順にしてRとLを入れ替えた下位w-iビットが元の上位w-iビットより若い
   184:           goto nextN;
   185:         }
   186:       }
   187:       *p++ = n;
   188:     nextN:
   189:       ;
   190:     }
   191:     *p = -1;  //番兵
   192:   }
   193: 
   194:   //最上位以外の3進数のリストを作る
   195:   int *ternaryList2 = calloc (8192, sizeof (ternaryList2[0]));  //最上位以外の3進数のリスト
   196:   {
   197:     int *p = ternaryList2;  //8桁の3進数のリストの先頭
   198:     for (int n = 0x0000; n <= 0xffff; n++) {  //nは8桁の4進数
   199:       int x = (n >> 1) & n & 0x5555;  //3を抽出する
   200:       if (x) {  //3があるとき
   201:         n |= (x & -x) - 1;  //一番右にある3の右側をすべて3にする
   202:         continue;
   203:       }
   204:       //nは8桁の3進数
   205:       *p++ = n;
   206:     }
   207:     *p = -1;  //番兵
   208:   }
   209: 
   210:   //8桁の3進数の閉路条件のハッシュテーブルを作る
   211:   //  下位8桁の3進数の方向条件と閉路条件の符号を反転する
   212:   //  下位8桁が0の3進数の閉路条件(xa,xb,ya,yb)のハッシュ関数を(((xa*7+xb)*7+ya)*7+yb)&(512-1)とする
   213:   //  下位8桁が0の3進数の方向条件(h0)毎に8個のハッシュテーブルを作る
   214:   //  下位8桁が0の3進数の方向条件と閉路条件に合う下位8桁を連結することで方向条件と閉路条件を満たす3進数を構成する
   215: #define CIRCUIT_HASH_SIZE 512
   216: #define CIRCUIT_LIST_SIZE 256
   217:   //  circuitHashTable[h0][g][5*i]={n,-xa,-xb,-ya,-yb}
   218:   //  [CIRCUIT_LIST_SIZE-1]はカウンタ。ハッシュテーブルを作るときだけ使うので邪魔にならないところに置く
   219:   int (*circuitHashTable)[CIRCUIT_HASH_SIZE][CIRCUIT_LIST_SIZE] =
   220:     calloc (CIRCUIT_LIST_SIZE * CIRCUIT_HASH_SIZE * 8, sizeof (circuitHashTable[0][0][0]));
   221:   for (int h0 = 0; h0 < 8; h0++) {
   222:     for (int g = 0; g < CIRCUIT_HASH_SIZE; g++) {
   223:       circuitHashTable[h0][g][CIRCUIT_LIST_SIZE - 1] = 0;  //カウンタ
   224:     }
   225:   }
   226:   for (int n = 0x0000; n <= 0xffff; n++) {  //nは8桁の4進数
   227:     int x = (n >> 1) & n & 0x5555;  //3を抽出する
   228:     if (x) {  //3があるとき
   229:       n |= (x & -x) - 1;  //一番右にある3の右側をすべて3にする
   230:       continue;
   231:     }
   232:     //nは8桁の3進数
   233:     for (int h0 = 0; h0 < 8; h0++) {
   234:       //方向条件と閉路条件を計算する
   235:       int xa = 0;
   236:       int xb = 0;
   237:       int ya = 0;
   238:       int yb = 0;
   239:       int h = h0;
   240:       for (int i = 2 * 8 - 2; 0 <= i; i -= 2) {
   241:         const int *c = coeff[(int) (n >> i) & 3][h];
   242:         xa += c[0];
   243:         xb += c[1];
   244:         ya += c[2];
   245:         yb += c[3];
   246:         h = (h + c[4]) & 7;
   247:       }
   248:       if (h == 0) {  //方向条件
   249:         xa = -xa;
   250:         xb = -xb;
   251:         ya = -ya;
   252:         yb = -yb;
   253:         //ハッシュテーブルを更新する
   254:         int g = (((xa * 7 + xb) * 7 + ya) * 7 + yb) & (CIRCUIT_HASH_SIZE - 1);  //ハッシュコード
   255:         int *p = circuitHashTable[h0][g];
   256:         int i = p[CIRCUIT_LIST_SIZE - 1];
   257:         p[i    ] = n;
   258:         p[i + 1] = xa;
   259:         p[i + 2] = xb;
   260:         p[i + 3] = ya;
   261:         p[i + 4] = yb;
   262:         p[CIRCUIT_LIST_SIZE - 1] = i + 5;
   263:       }  //if 方向条件
   264:     }  //for h0
   265:   }  //for n
   266:   for (int h0 = 0; h0 < 8; h0++) {
   267:     for (int g = 0; g < CIRCUIT_HASH_SIZE; g++) {
   268:       circuitHashTable[h0][g][circuitHashTable[h0][g][CIRCUIT_LIST_SIZE - 1]] = -1;  //番兵
   269:     }
   270:   }
   271: 
   272:   //閉路の配列を確保する
   273:   //  8  10  12  14  16   18   20    22     24      26       28       30         32   レールの数 n
   274:   //  1   1   4   9  42  161  847  4739  29983  198683  1375928  9786630   35658701?  閉路の数
   275:   //  1   2   6  18  61  238 1058  5380  31012  201439  1465610 11879054  106724526   確保する数 floor(pow(0.03*n+0.8,n+0.7))
   276:   unsigned long long *uniqueCircuitArray =
   277:     calloc ((size_t) floor (pow (0.03 * (double) numofRails + 0.8, (double) numofRails + 0.7)),
   278:             sizeof (unsigned long long));  //閉路の配列
   279: 
   280:   //閉路を作る
   281:   int circuitWidth = numofRails << 1;  //閉路のビット数
   282:   unsigned long long circuitMask = 0xffffffffffffffffULL >> (64 - circuitWidth);  //閉路のマスク。(1ULL<<circuitWidth)-1ULLは不可
   283:   unsigned long long invertedCircuitMask = ~circuitMask;
   284:   unsigned long long invertedCircuitMask1 = invertedCircuitMask | 0x000000000000ffffULL;
   285:   unsigned long long invertedCircuitMask2 = invertedCircuitMask | 0x00000000ffffffffULL;
   286:   unsigned long long circuitRSS = 1ULL << (circuitWidth - 2);  //RSS...SSである閉路
   287:   unsigned long long circuitSRSS = 1ULL << (circuitWidth - 4);  //SRSS...SSである閉路
   288:   unsigned long long circuitSSRSS = 1ULL << (circuitWidth - 6);  //SSRSS...SSである閉路
   289:   size_t numofUniqueCircuits = 0;  //重複を除いた閉路の数
   290:   int *tp3 = numofRails <= 24 ? ternaryList1[0] :                    ternaryList1[numofRails - 24];
   291:   int *tp2 = numofRails <= 16 ? ternaryList1[0] : numofRails <= 24 ? ternaryList1[numofRails - 16] : ternaryList2;
   292:   int *tp1 = numofRails <=  8 ? ternaryList1[0] : numofRails <= 16 ? ternaryList1[numofRails -  8] : ternaryList2;
   293:   while (0 <= *tp3) {
   294:     unsigned long long n000 = (unsigned long long) *tp3++ << 48;  //ビット63~48
   295:     while (0 <= *tp2) {
   296:       unsigned long long n00 = n000 | ((unsigned long long) *tp2++ << 32);  //ビット47~32を連結する
   297:       //先頭がSでないときSを含むものを除く
   298:       if (circuitRSS <= n00) {
   299:         unsigned long long x = n00 | invertedCircuitMask2;
   300:         if ((~((x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //00がある
   301:           continue;
   302:         }
   303:       }
   304:       //先頭がSSでないときSSを含むものを除く
   305:       if (circuitSRSS <= n00) {
   306:         unsigned long long x = n00 | invertedCircuitMask2;
   307:         if ((~((x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //0000がある
   308:           continue;
   309:         }
   310:       }
   311:       //先頭がSSSでないときSSSを含むものを除く
   312:       if (circuitSSRSS <= n00) {
   313:         unsigned long long x = n00 | invertedCircuitMask2;
   314:         if ((~((x >> 5) | (x >> 4) | (x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //000000がある
   315:           continue;
   316:         }
   317:       }
   318:       while (0 <= *tp1) {
   319:         unsigned long long n0 = n00 | ((unsigned long long) *tp1++ << 16);  //ビット31~16を連結する
   320:         //n0はnumofRails桁の先頭が2ではない下位8桁が0の3進数
   321:         //先頭がSでないときSを含むものを除く
   322:         if (circuitRSS <= n0) {
   323:           unsigned long long x = n0 | invertedCircuitMask1;
   324:           if ((~((x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //00がある
   325:             continue;
   326:           }
   327:         }
   328:         //先頭がSSでないときSSを含むものを除く
   329:         if (circuitSRSS <= n0) {
   330:           unsigned long long x = n0 | invertedCircuitMask1;
   331:           if ((~((x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //0000がある
   332:             continue;
   333:           }
   334:         }
   335:         //先頭がSSSでないときSSSを含むものを除く
   336:         if (circuitSSRSS <= n0) {
   337:           unsigned long long x = n0 | invertedCircuitMask1;
   338:           if ((~((x >> 5) | (x >> 4) | (x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL) != 0) {  //000000がある
   339:             continue;
   340:           }
   341:         }
   342:         //方向条件と閉路条件を計算する
   343:         int xa = 0;
   344:         int xb = 0;
   345:         int ya = 0;
   346:         int yb = 0;
   347:         int h = 0;
   348:         for (int i = circuitWidth - 2; 2 * 8 <= i; i -= 2) {  //下位8桁はここにはない
   349:           const int *c = coeff[(int) (n0 >> i) & 3][h];
   350:           xa += c[0];
   351:           xb += c[1];
   352:           ya += c[2];
   353:           yb += c[3];
   354:           h = (h + c[4]) & 7;
   355:         }
   356:         if (xa < -8 || 8 < xa || xb < -8 || 8 < xb ||
   357:             ya < -8 || 8 < ya || yb < -8 || 8 < yb) {  //閉路条件
   358:           continue;
   359:         }
   360:         for (int *p = circuitHashTable[h][(((xa * 7 + xb) * 7 + ya) * 7 + yb) & (CIRCUIT_HASH_SIZE - 1)];
   361:              0 <= *p;
   362:              p += 5) {
   363:           if (p[1] == xa && p[2] == xb && p[3] == ya && p[4] == yb) {  //閉路条件
   364:             unsigned long long circuit = n0 | (unsigned long long) *p;  //ビット15~0を連結する
   365:             //circuitはnumofRails桁の3進数で方向条件と閉路条件を満たしている
   366:             //先頭がSでないときSを含むものを除く
   367:             if (circuitRSS <= circuit) {
   368:               unsigned long long x = circuit | invertedCircuitMask;
   369:               x = ~((x >> 1) | x) & 0x5555555555555555ULL;  //00を抽出する
   370:               if (x != 0) {  //00がある
   371:                 circuit |= (x & -x) - 1ULL;  //一番右にある00の右側をすべて1にする
   372:                 goto nextCircuit;
   373:               }
   374:             }
   375:             //先頭がSSでないときSSを含むものを除く
   376:             if (circuitSRSS <= circuit) {
   377:               unsigned long long x = circuit | invertedCircuitMask;
   378:               x = ~((x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL;  //0000を抽出する
   379:               if (x != 0) {  //0000がある
   380:                 circuit |= (x & -x) - 1ULL;  //一番右にある0000の右側をすべて1にする
   381:                 goto nextCircuit;
   382:               }
   383:             }
   384:             //先頭がSSSでないときSSSを含むものを除く
   385:             if (circuitSSRSS <= circuit) {
   386:               unsigned long long x = circuit | invertedCircuitMask;
   387:               x = ~((x >> 5) | (x >> 4) | (x >> 3) | (x >> 2) | (x >> 1) | x) & 0x5555555555555555ULL;  //000000を抽出する
   388:               if (x != 0) {  //000000がある
   389:                 circuit |= (x & -x) - 1ULL;  //一番右にある000000の右側をすべて1にする
   390:                 goto nextCircuit;
   391:               }
   392:             }
   393:             //ローテートすると若くなるものを除く
   394:             {
   395:               unsigned long long x = circuit;
   396:               for (int i = circuitWidth - 2; 0 < i; i -= 2) {
   397:                 if ((unsigned long long) (((x << (circuitWidth - i)) | (x >> i)) & circuitMask) < circuit) {
   398:                   goto nextCircuit;
   399:                 }
   400:               }
   401:             }
   402:             //RとLを入れ替えてローテートすると若くなるものを除く
   403:             {
   404:               unsigned long long x = circuit;
   405:               x = ((x & 0x5555555555555555ULL) << 1) | ((x >> 1) & 0x5555555555555555ULL);  //RとLを入れ替える
   406:               if (x < circuit) {
   407:                 goto nextCircuit;
   408:               }
   409:               for (int i = circuitWidth - 2; 0 < i; i -= 2) {
   410:                 if ((unsigned long long) (((x << (circuitWidth - i)) | (x >> i)) & circuitMask) < circuit) {
   411:                   goto nextCircuit;
   412:                 }
   413:               }
   414:             }
   415:             //逆順に繋いだ閉路を作る
   416:             unsigned long long reversedCircuit;
   417:             {
   418:               //16ビットずつテーブルを使って反転するより速い
   419:               unsigned long long x = circuit;
   420:               //x = ((x & 0x5555555555555555ULL) <<  1) | ((x >>  1) & 0x5555555555555555ULL);
   421:               x = ((x & 0x3333333333333333ULL) <<  2) | ((x >>  2) & 0x3333333333333333ULL);
   422:               x = ((x & 0x0f0f0f0f0f0f0f0fULL) <<  4) | ((x >>  4) & 0x0f0f0f0f0f0f0f0fULL);
   423:               x = ((x & 0x00ff00ff00ff00ffULL) <<  8) | ((x >>  8) & 0x00ff00ff00ff00ffULL);
   424:               x = ((x & 0x0000ffff0000ffffULL) << 16) | ((x >> 16) & 0x0000ffff0000ffffULL);
   425:               x = (x << 32) | (x >> 32);
   426:               reversedCircuit = x >> (64 - circuitWidth);
   427:             }
   428:             //逆順に繋いでローテートすると若くなるものを除く
   429:             {
   430:               unsigned long long x = reversedCircuit;
   431:               if (x < circuit) {
   432:                 goto nextCircuit;
   433:               }
   434:               for (int i = circuitWidth - 2; 0 < i; i -= 2) {
   435:                 if ((unsigned long long) (((x << (circuitWidth - i)) | (x >> i)) & circuitMask) < circuit) {
   436:                   goto nextCircuit;
   437:                 }
   438:               }
   439:             }
   440:             //逆順に繋いでRとLを入れ替えてローテートすると若くなるものを除く
   441:             {
   442:               unsigned long long x = reversedCircuit;
   443:               x = ((x & 0x5555555555555555ULL) << 1) | ((x >> 1) & 0x5555555555555555ULL);  //RとLを入れ替える
   444:               if (x < circuit) {
   445:                 goto nextCircuit;
   446:               }
   447:               for (int i = circuitWidth - 2; 0 < i; i -= 2) {
   448:                 if ((unsigned long long) (((x << (circuitWidth - i)) | (x >> i)) & circuitMask) < circuit) {
   449:                   goto nextCircuit;
   450:                 }
   451:               }
   452:             }
   453:             //重複を除いた閉路を記録する
   454:             uniqueCircuitArray[numofUniqueCircuits++] = circuit;
   455:           nextCircuit:
   456:             ;
   457:           }  //if 閉路条件
   458:         }  //for p
   459:       }  //while tp1
   460:       tp1 = ternaryList2;
   461:     }  //while tp2
   462:     tp2 = ternaryList2;
   463:   }  //while tp3
   464: 
   465:   //計測終了
   466:   struct timespec endTime;
   467:   clock_gettime (CLOCK_PROCESS_CPUTIME_ID, &endTime);
   468:   time_t sec = endTime.tv_sec - startTime.tv_sec;
   469:   long nsec = endTime.tv_nsec - startTime.tv_nsec;
   470:   if (nsec < 0) {
   471:     sec--;
   472:     nsec += 1000000000L;
   473:   }
   474:   double elapsedTime = (double) sec + (double) nsec / 1000000000.0;
   475: 
   476:   //閉路を出力する
   477:   //  閉路は昇順に見つかるのでソートする必要はない
   478:   if (outputFileName[0] != '\0') {
   479:     FILE *fp;
   480:     if (strcmp (outputFileName, "-") == 0) {
   481:       fp = stdout;
   482:     } else {
   483:       fp = fopen (outputFileName, "wb");
   484:       if (fp == NULL) {
   485:         printf ("%s cannot be opened for writing.\n", outputFileName);
   486:       }
   487:     }
   488:     if (fp != NULL) {
   489:       for (size_t index = 0; index < numofUniqueCircuits; index++) {
   490:         unsigned long long circuit = uniqueCircuitArray[index];
   491:         char text[256];
   492:         char *p = text;
   493:         for (int i = circuitWidth - 2; 0 <= i; i -= 2) {
   494:           *p++ = chr[(int) (circuit >> i) & 3];
   495:         }
   496:         *p = '\0';
   497:         fprintf (fp, "%s\n", text);  //比較するとき邪魔なので個々の閉路に番号を振らない
   498:       }
   499:       if (fp != stdout) {
   500:         fclose (fp);
   501:         printf ("%s has been updated.\n", outputFileName);
   502:       }
   503:     }
   504:   }
   505: 
   506:   //閉路の数と所要時間を出力する
   507:   printf ("It took %.3lf seconds to find %zd circuits made by connecting %d rails.\n",
   508:           elapsedTime, numofUniqueCircuits, numofRails);
   509: 
   510:   return 0;
   511: }
   512: 

3. ログ

動作を確認した環境は Intel Core i7-3770 3.4GHz、RAM 8GB、Windows 10 Pro、Cygwin。

makoto@betelgeuse ~
$ gcc -finput-charset=utf-8 -fexec-charset=utf-8 -std=c11 -pedantic-errors -Wall -O3 -march=native -o plarail plarail.c

makoto@betelgeuse ~
$ ./plarail 8 answer8.txt
answer8.txt has been updated.
It took 0.000 seconds to find 1 circuits made by connecting 8 rails.

makoto@betelgeuse ~
$ ./plarail 10 answer10.txt
answer10.txt has been updated.
It took 0.000 seconds to find 1 circuits made by connecting 10 rails.

makoto@betelgeuse ~
$ ./plarail 12 answer12.txt
answer12.txt has been updated.
It took 0.000 seconds to find 4 circuits made by connecting 12 rails.

makoto@betelgeuse ~
$ ./plarail 14 answer14.txt
answer14.txt has been updated.
It took 0.016 seconds to find 9 circuits made by connecting 14 rails.

makoto@betelgeuse ~
$ ./plarail 16 answer16.txt
answer16.txt has been updated.
It took 0.000 seconds to find 42 circuits made by connecting 16 rails.

makoto@betelgeuse ~
$ ./plarail 18 answer18.txt
answer18.txt has been updated.
It took 0.000 seconds to find 161 circuits made by connecting 18 rails.

makoto@betelgeuse ~
$ ./plarail 20 answer20.txt
answer20.txt has been updated.
It took 0.016 seconds to find 847 circuits made by connecting 20 rails.

makoto@betelgeuse ~
$ ./plarail 22 answer22.txt
answer22.txt has been updated.
It took 0.031 seconds to find 4739 circuits made by connecting 22 rails.

makoto@betelgeuse ~
$ ./plarail 24 answer24.txt
answer24.txt has been updated.
It took 0.203 seconds to find 29983 circuits made by connecting 24 rails.

makoto@betelgeuse ~
$ ./plarail 26 answer26.txt
answer26.txt has been updated.
It took 3.281 seconds to find 198683 circuits made by connecting 26 rails.

makoto@betelgeuse ~
$ ./plarail 28 answer28.txt
answer28.txt has been updated.
It took 19.500 seconds to find 1375928 circuits made by connecting 28 rails.

makoto@betelgeuse ~
$ ./plarail 30 answer30.txt
answer30.txt has been updated.
It took 151.219 seconds to find 9786630 circuits made by connecting 30 rails.

makoto@betelgeuse ~
$ ./plarail 32 answer32.txt
answer32.txt has been updated.
It took 448.578 seconds to find 35658701 circuits made by connecting 32 rails.

makoto@betelgeuse ~
$

4. 結果

4.1. 閉路の数

読み込み中

4.2. 閉路一覧

読み込み中

4.3. 出発進行

読み込み中

5. 関連リンク

6. 更新履歴

2019 年

2019 年 6 月 15 日

プログラムを更新。先頭 1~8 桁の 3 進数のリストを作るとき後で除かれることが明らかなものを予め除いておくようにした。30 本のとき 2 分台になった。

ログを追加。

2019 年 6 月 14 日

公開。

プログラムを更新。先頭が S でないとき S を含むものを除く処理を閉路を作る途中に入れた。30 本のとき 30 分近くかかっていたが 5 分台になった。