Chuỗi trò chơi cuộc sống không lặp lại dài nhất


16

Cho một số nguyên dương N, xác định mẫu bắt đầu trên lưới N x N mang lại chuỗi không lặp lại dài nhất theo quy tắc Trò chơi cuộc sống và kết thúc bằng một mẫu cố định (chu kỳ dài 1), được phát trên hình xuyến.

Mục tiêu không phải là chương trình ngắn nhất, mà là nhanh nhất.

Vì thế giới là hữu hạn, cuối cùng bạn sẽ kết thúc trong một vòng lặp, do đó lặp lại trạng thái đã được truy cập. Nếu vòng lặp này có chu kỳ 1, thì mẫu bắt đầu là một ứng cử viên hợp lệ.

Đầu ra: Mẫu bắt đầu và tổng số trạng thái duy nhất trong chuỗi (bao gồm cả mẫu bắt đầu).

Bây giờ, 1x1-torus là đặc biệt, vì một tế bào có thể được coi là lân cận với chính nó hay không, nhưng trong thực tế, không có vấn đề gì, một tế bào sống duy nhất trong cả hai trường hợp sẽ chết (quá đông hoặc cô đơn). Do đó, đầu vào 1 mang lại một chuỗi có độ dài 2, chuỗi là một tế bào sống, sau đó chết vĩnh viễn.

Động lực cho câu hỏi này là tương tự chức năng hải ly bận rộn, nhưng chắc chắn ít phức tạp hơn, vì chúng ta có một ràng buộc về bộ nhớ. Đây cũng sẽ là một chuỗi hay để đưa vào OEIS.

Với N = 3, độ dài chuỗi là 3, bất kỳ mẫu nào ở phía bên trái đạt đến hình vuông 3x3 hoàn toàn màu đen và sau đó chết. (Tất cả các mẫu là một phần của 1 chu kỳ đã bị xóa).

Biểu đồ trạng thái


1
À, được rồi. Mã tốt nhất là mã quản lý để tính toán độ dài chuỗi cho N lớn nhất trong vòng 2 giờ. Độ phức tạp rõ ràng là 2 ^ (N ^ 2), vì vậy nếu có thể cải thiện điều này, điều này sẽ rất tốt.
Per Alexandersson

1
Ở các kích thước không tầm thường, mỗi mẫu là một phần của lớp đẳng cấu gồm 8N ^ 2 mẫu, vì vậy nếu có một cách nhanh chóng để chuẩn hóa thì điều đó sẽ tăng cường vừa phải.
Peter Taylor

1
Trình tự này đã được thêm vào OEIS?
mbomb007

1
Không phải tôi biết, sẽ rất vui khi thấy nó ở đó.
Per Alexandersson

1
Tôi đã gửi trình tự này (2, 2, 3, 10, 52, 91) cho OEIS với tên A294241 .
Peter Kagey

Câu trả lời:


13

C ++ lên tới 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                                                         

Kỹ thuật này được tập trung xung quanh một chức năng trạng thái tiếp theo nhanh. Mỗi bảng được biểu diễn dưới dạng bitmask của N ^ 2 bit và các thủ thuật hai bit được sử dụng để tính toán trạng thái tiếp theo cho tất cả các ô cùng một lúc. nextbiên dịch xuống khoảng 200 100 hướng dẫn lắp ráp cho N <= 8. Sau đó, chúng tôi chỉ cần thực hiện thử-tiêu chuẩn tất cả các trạng thái cho đến khi chúng lặp lại và chọn cái dài nhất.

Mất vài giây lên tới 5x5, vài giờ cho 6x6.

#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
#error "bad N"
#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
  ADD(n4);
  ADD(n5);
  ADD(n6);
  ADD(n7);
  ADD(n8);
  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
Bạn có thể nhận được một sự cải thiện vừa phải nextbằng cách đếm thay vì sắp xếp. #define H(x,y){x^=y;y&=(x^y);}và sau đó đại loại như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

Giải pháp thực sự tuyệt vời!
Per Alexandersson

@PeterTaylor: cảm ơn, tôi đã thực hiện (một sơ đồ khác cho) đếm, đã lưu một loạt các hướng dẫn.
Keith Randall

9

Tôi thấy các cách tiếp cận giải pháp có thể sau đây:

  • Lý thuyết nặng nề. Tôi biết có một số tài liệu về Cuộc sống trên một hình xuyến, nhưng tôi chưa đọc nhiều về nó.
  • Brute force về phía trước: cho mỗi bảng có thể, kiểm tra điểm số của nó. Về cơ bản, đây là những gì phương pháp của Matthew và Keith làm, mặc dù Keith giảm số lượng bảng để kiểm tra theo hệ số 4.
    • Tối ưu hóa: đại diện chính tắc. Nếu chúng ta có thể kiểm tra xem một bảng có đại diện chính tắc nhanh hơn nhiều so với đánh giá điểm của nó hay không, chúng ta sẽ tăng tốc độ của một hệ số khoảng 8N ^ 2. (Cũng có cách tiếp cận một phần với các lớp tương đương nhỏ hơn).
    • Tối ưu hóa: DP. Lưu trữ điểm số cho mỗi bảng, vì vậy thay vì đi qua chúng cho đến khi chúng hội tụ hoặc phân kỳ chúng ta chỉ cần đi bộ cho đến khi chúng ta tìm thấy một bảng mà chúng ta đã thấy trước đó. Về nguyên tắc, điều này sẽ giúp tăng tốc theo hệ số điểm trung bình / độ dài chu kỳ (có thể từ 20 trở lên), nhưng trong thực tế, chúng ta có khả năng bị tráo đổi mạnh. Ví dụ: với N = 6, chúng tôi cần dung lượng cho 2 ^ 36 điểm, với một byte cho mỗi điểm là 16 GB và chúng tôi cần truy cập ngẫu nhiên để chúng tôi không thể mong đợi địa phương bộ đệm tốt.
    • Kết hợp cả hai. Với N = 6, biểu diễn chính tắc đầy đủ sẽ cho phép chúng tôi giảm bộ đệm DP xuống còn khoảng 60 điểm. Đây là một cách tiếp cận đầy hứa hẹn.
  • Lực lượng vũ phu ngược. Điều này có vẻ kỳ lạ lúc đầu, nhưng nếu chúng ta giả sử rằng chúng ta có thể dễ dàng tìm thấy sự sống và chúng ta có thể dễ dàng đảo ngược Next(board)chức năng, chúng ta thấy rằng nó có một số lợi ích, mặc dù không nhiều như tôi mong đợi ban đầu.
    • Chúng tôi không bận tâm với bảng phân kỳ ở tất cả. Không tiết kiệm nhiều, vì chúng khá hiếm.
    • Chúng ta không cần lưu trữ điểm số cho tất cả các bảng, do đó sẽ có ít áp lực bộ nhớ hơn so với phương pháp DP chuyển tiếp.
    • Làm việc ngược lại thực sự khá dễ dàng bằng cách thay đổi một kỹ thuật mà tôi đã thấy trong văn học trong bối cảnh liệt kê các cuộc sống vẫn còn. Nó hoạt động bằng cách coi mỗi cột là một chữ cái trong bảng chữ cái và sau đó quan sát rằng một chuỗi gồm ba chữ cái xác định chữ cái ở giữa trong thế hệ tiếp theo. Song song với việc liệt kê các cuộc sống vẫn còn rất gần mà tôi đã tái cấu trúc chúng lại với nhau thành một phương pháp hơi khó xử,Prev2 .
    • Có vẻ như chúng ta chỉ có thể chuẩn hóa các cuộc sống tĩnh lặng và có được thứ gì đó như tăng tốc 8N ^ 2 với chi phí rất thấp. Tuy nhiên, theo kinh nghiệm, chúng tôi vẫn nhận được một sự giảm mạnh về số lượng bảng được xem xét nếu chúng tôi hợp quy hóa ở mỗi bước.
    • Một tỷ lệ cao đáng ngạc nhiên của các bảng có điểm 2 hoặc 3, do đó vẫn có áp lực bộ nhớ. Tôi thấy cần phải chuẩn hóa khi đang bay hơn là xây dựng thế hệ trước và sau đó chuẩn hóa. Có thể rất thú vị để giảm mức sử dụng bộ nhớ bằng cách thực hiện tìm kiếm theo chiều sâu thay vì tìm kiếm theo chiều rộng, nhưng làm như vậy mà không làm tràn ngăn xếp và không thực hiện các phép tính dự phòng đòi hỏi cách tiếp cận đồng quy / tiếp tục để liệt kê các bảng trước đó.

Tôi không nghĩ rằng tối ưu hóa vi mô sẽ cho phép tôi bắt kịp mã của Keith, nhưng vì lợi ích tôi sẽ đăng những gì tôi có. Điều này giải quyết N = 5 trong khoảng một phút trên máy 2GHz sử dụng Mono 2.4 hoặc .Net (không có PLINQ) và trong khoảng 20 giây sử dụng PLINQ; N = 6 chạy trong nhiều giờ.

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

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

        private readonly int _Size;
        private readonly uint _AlphabetSize;
        private readonly uint[] _Transitions;
        private readonly uint[][] _PrevData1;
        private readonly uint[][] _PrevData2;
        private readonly uint[,,] _CanonicalData;

        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
                        tmpPrev1[Pack(jprime, j)].Add(k);
                        tmpPrev2[Pack(jprime, i, j)].Add(k);
                    }
                }
            }

            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)) {
                    accum.Add(Canonicalise(prev));
                }
            }

            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));
        }
    }
}

Tôi cũng đang làm việc trên một phiên bản khác để đi lùi từ các điểm cố định. Tôi đã liệt kê các điểm cố định lên tới N = 8 (đối với N = 8, có 84396613 trong số chúng trước khi chuẩn hóa). Tôi đã có một công việc đơn giản, nhưng nó quá chậm. Một phần của vấn đề chỉ là kích thước của sự vật, với N = 6, bảng trống có 574384901 người tiền nhiệm (trước khi chuẩn hóa).
Keith Randall

1
3 ngày và 11 giờ để xác nhận rằng 91 là câu trả lời cho 6x6.
Peter Taylor

1

Hệ số

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 ;

: pad-to-with ( str len padstr -- rstr )
[ 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 ;

Một số thống kê thời gian:

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 } }
}

Và thử nghiệm 5 đã bị lỗi REPL. Hừm. Phần không hiệu quả nhất của chương trình có lẽ là chuỗi trò chơi chức năng. Tôi có thể làm cho nó tốt hơn sau này.


Mát mẻ! Tôi nghĩ rằng đầu ra của bạn là 1 quá lớn, đối với các mẫu 3x3, chuỗi không lặp lại dài nhất có độ dài 3, không phải 4 ...
Per Alexandersson
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.