# 最长的非重复生命游戏序列

16

1

1

1

mbomb007

1

1

13

## C ++最多N = 6

``````3x3 answer 3: 111 000 000
4x4 answer 10: 1110 0010 1100 0000
5x5 answer 52: 11010 10000 11011 10100 00000
6x6 answer 91: 100011 010100 110011 110100 101000 100000
``````

``````#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <algorithm>
using namespace std;

#define N 6

typedef uint64_t board;

// board is N bits by N bits, with indexes like this (N=4):
//  0  1  2  3
//  4  5  6  7
//  8  9 10 11
// 12 13 14 15

#if N==3
#define LEFT_COL (1 + (1<<3) + (1<<6))
#define RIGHT_COL ((1<<2) + (1<<5) + (1<<8))
#define ALL 0x1ffULL
#elif N==4
#define LEFT_COL 0x1111ULL
#define RIGHT_COL 0x8888ULL
#define ALL 0xffffULL
#elif N==5
#define LEFT_COL (1ULL + (1<<5) + (1<<10) + (1<<15) + (1<<20))
#define RIGHT_COL ((1ULL<<4) + (1<<9) + (1<<14) + (1<<19) + (1<<24))
#define ALL 0x1ffffffULL
#elif N==6
#define LEFT_COL (1 + (1<<6) + (1<<12) + (1<<18) + (1<<24) + (1ULL<<30))
#define RIGHT_COL ((1<<5) + (1<<11) + (1<<17) + (1<<23) + (1<<29) + (1ULL<<35))
#define ALL 0xfffffffffULL
#else
#endif

static inline board north(board b) {
return (b >> N) + (b << N*N-N);
}
static inline board south(board b) {
return (b << N) + (b >> N*N-N);
}
static inline board west(board b) {
return ((b & ~LEFT_COL) >> 1) + ((b & LEFT_COL) << N-1);
}
static inline board east(board b) {
return ((b & ~RIGHT_COL) << 1) + ((b & RIGHT_COL) >> N-1);
}

board next(board b) {
board n1 = north(b);
board n2 = south(b);
board n3 = west(b);
board n4 = east(b);
board n5 = north(n3);
board n6 = north(n4);
board n7 = south(n3);
board n8 = south(n4);

// add all the bits bitparallel-y to a 2-bit accumulator with overflow
board a0 = 0;
board a1 = 0;
board overflow = 0;
#define ADD(x) overflow |= a0 & a1 & x; a1 ^= a0 & x; a0 ^= x;

a0 = n1; // no carry yet
a1 ^= a0 & n2; a0 ^= n2; // no overflow yet
a1 ^= a0 & n3; a0 ^= n3; // no overflow yet
return (a1 & (b | a0)) & ~overflow & ALL;
}
void print(board b) {
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++) {
printf("%d", (int)(b >> i*N+j & 1));
}
printf(" ");
}
if (b >> N*N) printf("*");
printf("\n");
}

int main(int argc, char *argv[]) {
// Somewhere in the starting pattern there are a 1 and 0 together.  Using translational
// and rotational symmetry, we can put these in the first two bits.  So we need only consider
// 1 mod 4 boards.

board steps[10000];
int maxsteps = -1;
for (board b = 1; b < 1ULL << N*N; b += 4) {
int nsteps = 0;
board x = b;
while (true) {
int repeat = steps + nsteps - find(steps, steps + nsteps, x);
if (repeat > 0) {
if (repeat == 1 && nsteps > maxsteps) {
printf("%d: ", nsteps);
print(b);
maxsteps = nsteps;
}
break;
}
steps[nsteps++] = x;
x = next(x);
}
}
}
``````

1
`next`通过计数而不是排序，您可能会得到适度的改进。`#define H(x,y){x^=y;y&=(x^y);}`然后是类似的东西`H(n1,n2);H(n3,n4);H(n5,n6);H(n7,n8);H(n1,n3);H(n5,n7);H(n2,n4);H(n6,n8);H(n1,n5);H(n3,n7);H(n2,n6);H(n2,n3);H(n2,n5); return n2 & (b | n1) & ~(n3|n4|n5|n6|n7|n8) & ALL;`
Peter Taylor

@PeterTaylor：谢谢，我实现了计数（一种不同的方案），节省了一堆指令。

9

• 重理论。我知道有一些关于圆环上生活的文献，但是我还没有读太多。
• 蛮力向前：对于每个可能的棋盘，检查其得分。这基本上是Matthew和Keith的方法，尽管Keith将要检查的董事会数量减少了4倍。
• 优化：规范表示。如果我们可以检查董事会是否在规范表示中，比评估其得分要快得多，那么我们得到的加速系数约为8N ^ 2。（也有部分方法具有较小的等价类）。
• 优化：DP。缓存每个棋盘的得分，这样我们就可以步行直到找到我们之前见过的棋盘，而不是逐步浏览它们直到收敛或发散。原则上，这将使平均分数/周期长度（可能为20或更长）的速度提高，但是在实践中，我们可能会进行大量交换。例如，对于N = 6，我们需要2 ^ 36分数的容量，每分数一个字节为16GB，并且我们需要随机访问，因此我们不能期望良好的缓存局部性。
• 结合两者。对于N = 6，完整的规范表示将使我们可以将DP缓存减少到大约60个兆分数。这是一种很有前途的方法。
• 蛮力倒退。乍一看这很奇怪，但是如果我们假设我们可以轻松地找到静物并且可以轻松地反转该`Next(board)`函数，我们会发现它具有一些好处，尽管没有我最初希望的那样多。
• 我们根本不用理会不同的董事会。节省不了多少，因为它们很少见。
• 我们不需要存储所有电路板的分数，因此应该比正向DP方法具有更少的内存压力。
• 通过改变我在文献中看到的静物技术，倒退实际上很容易。它的工作原理是将每一列都视为字母中的一个字母，然后观察到三个字母的序列确定了下一代的中间一个字母。枚举静物的过程非常相似，以至于我将它们重构为一个稍微尴尬的方法`Prev2`
• 似乎我们可以规范静物，并以极少的成本获得类似8N ^ 2的加速效果。但是，根据经验，如果我们在每个步骤中都能够规范化，我们所考虑的董事会数量仍然会大大减少。
• 极高比例的木板得分为2或3，因此仍然存在内存压力。我发现有必要即时进行规范化，而不是构建上一代并进行规范化。通过进行深度优先而不是广度优先的搜索来减少内存使用量可能是有趣的，但是要做到不溢出堆栈也不进行冗余计算，就需要使用例行程序/连续方法枚举前面的板。

``````using System;
using System.Collections.Generic;
using System.Linq;

namespace Sandbox {
class Codegolf9393 {
internal static void Main() {
new Codegolf9393(4).Solve();
}

private Codegolf9393(int size) {
if (size > 8) throw new NotImplementedException("We need to fit the bits in a ulong");

_Size = size;
_AlphabetSize = 1u << _Size;

_Transitions = new uint[_AlphabetSize * _AlphabetSize * _AlphabetSize];
_PrevData1 = new uint[_AlphabetSize * _AlphabetSize][];
_PrevData2 = new uint[_AlphabetSize * _AlphabetSize * _AlphabetSize][];
_CanonicalData = new uint[_Size, 2, _AlphabetSize];

InitTransitions();
}

private void InitTransitions() {
HashSet<uint>[] tmpPrev1 = new HashSet<uint>[_AlphabetSize * _AlphabetSize];
HashSet<uint>[] tmpPrev2 = new HashSet<uint>[_AlphabetSize * _AlphabetSize * _AlphabetSize];
for (int i = 0; i < tmpPrev1.Length; i++) tmpPrev1[i] = new HashSet<uint>();
for (int i = 0; i < tmpPrev2.Length; i++) tmpPrev2[i] = new HashSet<uint>();

for (uint i = 0; i < _AlphabetSize; i++) {
for (uint j = 0; j < _AlphabetSize; j++) {
uint prefix = Pack(i, j);
for (uint k = 0; k < _AlphabetSize; k++) {
// Build table for forwards checking
uint jprime = 0;
for (int l = 0; l < _Size; l++) {
uint count = GetBit(i, l-1) + GetBit(i, l) + GetBit(i, l+1) + GetBit(j, l-1) + GetBit(j, l+1) + GetBit(k, l-1) + GetBit(k, l) + GetBit(k, l+1);
uint alive = GetBit(j, l);
jprime = SetBit(jprime, l, (count == 3 || (alive + count == 3)) ? 1u : 0u);
}
_Transitions[Pack(prefix, k)] = jprime;

// Build tables for backwards possibilities
}
}
}

for (int i = 0; i < tmpPrev1.Length; i++) _PrevData1[i] = tmpPrev1[i].ToArray();
for (int i = 0; i < tmpPrev2.Length; i++) _PrevData2[i] = tmpPrev2[i].ToArray();

for (uint col = 0; col < _AlphabetSize; col++) {
_CanonicalData[0, 0, col] = col;
_CanonicalData[0, 1, col] = VFlip(col);
for (int rot = 1; rot < _Size; rot++) {
_CanonicalData[rot, 0, col] = VRotate(_CanonicalData[rot - 1, 0, col]);
_CanonicalData[rot, 1, col] = VRotate(_CanonicalData[rot - 1, 1, col]);
}
}
}

private ICollection<ulong> Prev2(bool stillLife, ulong next, ulong prev, int idx, ICollection<ulong> accum) {
if (stillLife) next = prev;

if (idx == 0) {
for (uint a = 0; a < _AlphabetSize; a++) Prev2(stillLife, next, SetColumn(0, idx, a), idx + 1, accum);
}
else if (idx < _Size) {
uint i = GetColumn(prev, idx - 2), j = GetColumn(prev, idx - 1);
uint jprime = GetColumn(next, idx - 1);
uint[] succ = idx == 1 ? _PrevData1[Pack(jprime, j)] : _PrevData2[Pack(jprime, i, j)];
foreach (uint b in succ) Prev2(stillLife, next, SetColumn(prev, idx, b), idx + 1, accum);
}
else {
// Final checks: does the loop round work?
uint a0 = GetColumn(prev, 0), a1 = GetColumn(prev, 1);
uint am = GetColumn(prev, _Size - 2), an = GetColumn(prev, _Size - 1);
if (_Transitions[Pack(am, an, a0)] == GetColumn(next, _Size - 1) &&
_Transitions[Pack(an, a0, a1)] == GetColumn(next, 0)) {
}
}

return accum;
}

internal void Solve() {
DateTime start = DateTime.UtcNow;
ICollection<ulong> gen = Prev2(true, 0, 0, 0, new HashSet<ulong>());
for (int depth = 1; gen.Count > 0; depth++) {
Console.WriteLine("Length {0}: {1}", depth, gen.Count);
ICollection<ulong> nextGen;

#if NET_40
nextGen = new HashSet<ulong>(gen.AsParallel().SelectMany(board => Prev2(false, board, 0, 0, new HashSet<ulong>())));
#else
nextGen = new HashSet<ulong>();
foreach (ulong board in gen) Prev2(false, board, 0, 0, nextGen);
#endif

// We don't want the still lifes to persist or we'll loop for ever
if (depth == 1) {
foreach (ulong stilllife in gen) nextGen.Remove(stilllife);
}

gen = nextGen;
}
Console.WriteLine("Time taken: {0}", DateTime.UtcNow - start);
}

private ulong Canonicalise(ulong board)
{
// Find the minimum board under rotation and reflection using something akin to radix sort.
Isomorphism canonical = new Isomorphism(0, 1, 0, 1);
for (int xoff = 0; xoff < _Size; xoff++) {
for (int yoff = 0; yoff < _Size; yoff++) {
for (int xdir = -1; xdir <= 1; xdir += 2) {
for (int ydir = 0; ydir <= 1; ydir++) {
Isomorphism candidate = new Isomorphism(xoff, xdir, yoff, ydir);

for (int col = 0; col < _Size; col++) {
uint a = canonical.Column(this, board, col);
uint b = candidate.Column(this, board, col);

if (b < a) canonical = candidate;
if (a != b) break;
}
}
}
}
}

ulong canonicalValue = 0;
for (int i = 0; i < _Size; i++) canonicalValue = SetColumn(canonicalValue, i, canonical.Column(this, board, i));
return canonicalValue;
}

struct Isomorphism {
int xoff, xdir, yoff, ydir;

internal Isomorphism(int xoff, int xdir, int yoff, int ydir) {
this.xoff = xoff;
this.xdir = xdir;
this.yoff = yoff;
this.ydir = ydir;
}

internal uint Column(Codegolf9393 _this, ulong board, int col) {
uint basic = _this.GetColumn(board, xoff + col * xdir);
return _this._CanonicalData[yoff, ydir, basic];
}
}

private uint VRotate(uint col) {
return ((col << 1) | (col >> (_Size - 1))) & (_AlphabetSize - 1);
}

private uint VFlip(uint col) {
uint replacement = 0;
for (int row = 0; row < _Size; row++)
replacement = SetBit(replacement, row, GetBit(col, _Size - row - 1));
return replacement;
}

private uint GetBit(uint n, int bit) {
bit %= _Size;
if (bit < 0) bit += _Size;

return (n >> bit) & 1;
}

private uint SetBit(uint n, int bit, uint value) {
bit %= _Size;
if (bit < 0) bit += _Size;

uint mask = 1u << bit;
return (n & ~mask) | (value == 0 ? 0 : mask);
}

private uint Pack(uint a, uint b) { return (a << _Size) | b; }
private uint Pack(uint a, uint b, uint c) {
return (((a << _Size) | b) << _Size) | c;
}

private uint GetColumn(ulong n, int col) {
col %= _Size;
if (col < 0) col += _Size;
return (_AlphabetSize - 1) & (uint)(n >> (col * _Size));
}

private ulong SetColumn(ulong n, int col, uint value) {
col %= _Size;
if (col < 0) col += _Size;

ulong mask = (_AlphabetSize - 1) << (col * _Size);
return (n & ~mask) | (((ulong)value) << (col * _Size));
}
}
}
``````

1
3天11个小时，以确认91是6x6的答案。

1

# 因子

``````USING: arrays grouping kernel locals math math.functions math.parser math.order math.ranges math.vectors sequences sequences.extras ;
IN: longest-gof-pattern

:: neighbors ( x y game -- neighbors )
game length :> len
x y game -rot 2array {
{ -1 -1 }
{ -1 0 }
{ -1 1 }
{ 0 -1 }
{ 0 1 }
{ 1 -1 }
{ 1 0 }
{ 1 1 }
} [
v+ [
dup 0 <
[ dup abs len mod - abs len mod ] [ abs len mod ]
if
] map
] with map [ swap [ first2 ] dip nth nth ] with map ;

: next ( game -- next )
dup [
[
neighbors sum
[ [ 1 = ] [ 2 3 between? ] bi* and ]
[ [ 0 = ] [ 3 = ] bi* and ] 2bi or 1 0 ?
] curry curry map-index
] curry map-index ;

: suffixes ( seq -- suffixes )
{ }
[ [ [ suffix ] curry map ] [ 1array 1array ] bi append ]
reduce ;

! find largest repeating pattern
: LRP ( seq -- pattern )
dup length iota
[ 1 + [ reverse ] dip group [ reverse ] map reverse ] with
map dup [ dup last [ = ] curry map ] map
[ suffixes [ t [ and ] reduce ] map [ ] count ] map
dup supremum [ = ] curry find drop swap nth last ;

: game-sequence ( game -- seq )
1array [
dup [
dup length 2 >
[ 2 tail-slice* [ first ] [ last ] bi = not ]
[ drop t ] if
] [ LRP length 1 > not ] bi and
] [ dup last next suffix ] while ;

[ swap dup length swapd - ] dip [ ] curry replicate ""
[ append ] reduce prepend ;

:: all-NxN-games ( n -- games )
2 n sq ^ iota [
>bin n sq "0" pad-to-with n group
[ [ 48 = 0 1 ? ] { } map-as ] map
] map ;

: longest-gof-pattern ( n -- game )
all-NxN-games [ game-sequence ] map [ length ] supremum-by but-last ;
``````

``````IN: longest-gof-pattern [ 3 longest-gof-pattern ] time dup length . .
Running time: 0.08850873500000001 seconds

3
{
{ { 1 1 1 } { 0 0 0 } { 0 0 0 } }
{ { 1 1 1 } { 1 1 1 } { 1 1 1 } }
{ { 0 0 0 } { 0 0 0 } { 0 0 0 } }
}

IN: longest-gof-pattern [ 4 longest-gof-pattern ] time dup length . .
Running time: 49.667698828 seconds

10
{
{ { 0 1 1 0 } { 0 1 0 0 } { 0 1 0 0 } { 1 1 0 1 } }
{ { 0 1 1 0 } { 0 1 0 0 } { 0 1 0 0 } { 0 0 0 1 } }
{ { 0 1 1 0 } { 0 1 0 0 } { 0 0 1 0 } { 1 1 0 0 } }
{ { 0 1 1 0 } { 0 1 0 0 } { 0 0 1 0 } { 0 0 0 1 } }
{ { 0 1 1 0 } { 0 1 0 0 } { 0 0 1 0 } { 1 1 0 1 } }
{ { 0 1 1 0 } { 0 1 0 0 } { 0 0 1 1 } { 0 0 0 1 } }
{ { 0 1 0 1 } { 0 1 0 1 } { 0 0 1 1 } { 1 1 0 1 } }
{ { 1 1 0 1 } { 1 1 0 1 } { 0 0 0 0 } { 1 1 0 0 } }
{ { 1 1 0 1 } { 1 1 0 1 } { 0 0 1 1 } { 1 1 1 1 } }
{ { 0 0 0 0 } { 0 0 0 0 } { 0 0 0 0 } { 0 0 0 0 } }
}
``````