ลำดับ Game-of-Life ที่ไม่ซ้ำกันนานที่สุด


16

รับจำนวนเต็มบวก N กำหนดรูปแบบเริ่มต้นบน N x N- กริดที่ให้ลำดับที่ไม่ซ้ำกันที่ยาวที่สุดภายใต้ Game of Life-rules และสิ้นสุดด้วยรูปแบบคงที่ (วงจรความยาว 1) เล่นบนพรู

เป้าหมายไม่ใช่โปรแกรมที่สั้นที่สุด แต่เร็วที่สุด

เนื่องจากโลกนี้มีขอบเขต จำกัด ในที่สุดคุณก็จะจบลงด้วยการวนซ้ำดังนั้นจึงเป็นรัฐที่เยี่ยมชมแล้วซ้ำอีก หากการวนซ้ำนี้มีระยะเวลา 1 รูปแบบเริ่มต้นจะเป็นตัวเลือกที่ถูกต้อง

เอาต์พุต: รูปแบบเริ่มต้นและจำนวนรวมของสถานะที่ไม่ซ้ำกันในลำดับ (รวมถึงรูปแบบเริ่มต้น)

ตอนนี้ 1x1-torus เป็นพิเศษเนื่องจากเซลล์อาจถูกพิจารณาว่าอยู่ใกล้เคียงกับตัวเองหรือไม่ แต่ในทางปฏิบัติไม่มีปัญหาเซลล์ที่มีชีวิตเดียวจะตายในกรณีใดกรณีหนึ่ง ดังนั้นอินพุต 1 ให้ผลตามลำดับความยาว 2 ลำดับเป็นหนึ่งเซลล์ที่มีชีวิตจากนั้นก็ตายไปตลอดกาล

แรงจูงใจสำหรับคำถามนี้คืออนาล็อกของฟังก์ชันบีเวอร์ที่วุ่นวาย แต่มีความซับซ้อนน้อยกว่าเนื่องจากเรามีความจำที่ จำกัด นี่จะเป็นลำดับที่ดีที่จะรวมไว้ใน OEIS เช่นกัน

สำหรับ N = 3 ความยาวของลำดับคือ 3 รูปแบบใด ๆ บนด้านซ้ายมือถึงพื้นที่สี่เหลี่ยมจัตุรัสสีดำสนิทขนาด 3x3 แล้วตาย (รูปแบบทั้งหมดที่เป็นส่วนหนึ่งของการลบ 1 รอบ)

กราฟของอเมริกา


1
อ่าใช่แล้ว โค้ดที่ดีที่สุดคือโค้ดที่จัดการเพื่อคำนวณความยาวของลำดับสำหรับ N ที่ใหญ่ที่สุดภายใน 2 ชั่วโมง ความซับซ้อนที่ชัดเจนคือ 2 ^ (N ^ 2) ดังนั้นหากเป็นไปได้ที่จะปรับปรุงสิ่งนี้มันจะดี
ต่อ Alexandersson

1
ที่ขนาดที่ไม่สำคัญแต่ละรูปแบบเป็นส่วนหนึ่งของคลาส isomorphism ของรูปแบบ 8N ^ 2 ดังนั้นหากมีวิธีที่รวดเร็วในการกำหนดมาตรฐานแล้วจะเพิ่มระดับปานกลาง
ปีเตอร์เทย์เลอร์

1
มีการเพิ่มลำดับนี้ลงใน OEIS หรือไม่
mbomb007

1
ไม่ว่าฉันรู้แล้วยินดีที่จะเห็นมันที่นั่น
ต่อ Alexandersson

1
ผมได้ส่งลำดับนี้ (2, 2, 3, 10, 52, 91) การ OEIS เป็นA294241
Peter Kagey

คำตอบ:


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                                                         

เทคนิคนี้มีศูนย์กลางอยู่ที่ฟังก์ชันสถานะถัดไปที่รวดเร็ว แต่ละบอร์ดจะแสดงเป็นบิตมาสก์ของ N ^ 2 บิตและใช้เทคนิคเคล็ดลับ bit-twiddly เพื่อคำนวณสถานะถัดไปสำหรับเซลล์ทั้งหมดในครั้งเดียว nextรวบรวมคำแนะนำในการประกอบประมาณ200 100 คำสั่งสำหรับ N <= 8 จากนั้นเราก็ทำแบบลองทุกสภาวะจนกว่าจะทำซ้ำและเลือกอันที่ยาวที่สุด

ใช้เวลาสองถึงสามวินาทีถึง 5x5 ไม่กี่ชั่วโมงสำหรับ 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
คุณอาจได้รับการปรับปรุงในระดับปานกลาง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

ทางออกที่ยอดเยี่ยมจริงๆ!
ต่อ Alexandersson

@ PeterTaylor: ขอบคุณฉันใช้งาน (รูปแบบที่แตกต่างกันสำหรับการนับ) บันทึกคำแนะนำมากมาย
Keith Randall

9

ฉันเห็นวิธีการแก้ปัญหาที่เป็นไปได้ต่อไปนี้:

  • ทฤษฎีหนัก ฉันรู้ว่ามีบางวรรณกรรมเกี่ยวกับชีวิตบนพรู แต่ฉันไม่ได้อ่านมาก
  • กำลังเดินหน้าอย่างดุเดือด: สำหรับทุกบอร์ดที่เป็นไปได้ตรวจสอบคะแนน นี่เป็นสิ่งที่แมทธิวและคี ธ ทำอยู่ถึงแม้ว่าคี ธ จะลดจำนวนกระดานเพื่อตรวจสอบด้วยปัจจัย 4
    • การเพิ่มประสิทธิภาพ: การแสดงแบบบัญญัติ หากเราสามารถตรวจสอบได้ว่าบอร์ดนั้นมีภาพมาตรฐานที่รวดเร็วกว่าที่เราคาดไว้หรือไม่เราสามารถเพิ่มความเร็วได้ถึง 8N ^ 2 (นอกจากนี้ยังมีวิธีการบางส่วนที่มีคลาสความเท่าเทียมขนาดเล็ก)
    • การเพิ่มประสิทธิภาพ: DP แคชคะแนนสำหรับแต่ละบอร์ดดังนั้นแทนที่จะเดินผ่านพวกเขาจนกว่าพวกเขาจะมาบรรจบกันหรือแตกต่างเราเพิ่งเดินจนกว่าเราจะพบกระดานที่เราเคยเห็นมาก่อน ในหลักการนี้จะให้ความเร็วโดยปัจจัยของคะแนนเฉลี่ย / ความยาวรอบ (อาจ 20 หรือมากกว่า) แต่ในทางปฏิบัติเรามีแนวโน้มที่จะแลกเปลี่ยนอย่างหนัก เช่นสำหรับ N = 6 เราต้องการความสามารถสำหรับ 2 ^ 36 คะแนนซึ่งเป็นไบต์ต่อคะแนนคือ 16GB และเราต้องการการเข้าถึงแบบสุ่มดังนั้นเราจึงไม่คาดหวังว่าจะมีพื้นที่แคชที่ดี
    • รวมทั้งสอง สำหรับ N = 6 การเป็นตัวแทนแบบบัญญัติเต็มรูปแบบจะช่วยให้เราสามารถลดแคช DP ลงได้ประมาณ 60 ล้านคะแนน นี่เป็นวิธีที่มีแนวโน้ม
  • กำลังดุร้ายย้อนหลัง สิ่งนี้ดูแปลกในตอนแรก แต่ถ้าเราคิดว่าเราสามารถหาสิ่งมีชีวิตได้อย่างง่ายดายและเราสามารถสลับการNext(board)ทำงานได้อย่างง่ายดายเราเห็นว่ามันมีประโยชน์บางอย่างแม้ว่าจะไม่มากเท่าที่ฉันหวังไว้ในตอนแรก
    • เราไม่ต้องกังวลกับกระดานที่แยกออกเลย ไม่ค่อยประหยัดเพราะค่อนข้างหายาก
    • เราไม่จำเป็นต้องเก็บคะแนนสำหรับบอร์ดทั้งหมดดังนั้นควรมีความกดดันหน่วยความจำน้อยกว่าวิธีการส่งต่อ DP
    • การย้อนกลับเป็นเรื่องง่ายมากโดยการปรับเปลี่ยนเทคนิคที่ฉันเห็นในวรรณกรรมในบริบทของการแจกแจงภาพนิ่ง มันทำงานโดยการรักษาแต่ละคอลัมน์เป็นตัวอักษรในตัวอักษรแล้วสังเกตว่าลำดับของตัวอักษรสามตัวเป็นตัวกำหนดกลางหนึ่งในรุ่นต่อไป เส้นขนานที่มีการแจกแจงภาพนิ่งยังคงใกล้เคียงกันมากที่ฉันได้จัดทำใหม่ด้วยวิธีที่น่าอึดอัดใจเล็กน้อยเท่านั้นPrev2 .
    • มันอาจดูเหมือนว่าเราสามารถยอมรับสิ่งมีชีวิตนิ่งเฉยและรับความเร็ว 8N ^ 2 ด้วยค่าใช้จ่ายเพียงเล็กน้อย อย่างไรก็ตามสังเกตุเรายังคงได้รับการลดจำนวนมากพิจารณาถ้าเรากำหนดมาตรฐานในแต่ละขั้นตอน
    • สัดส่วนที่สูงอย่างน่าประหลาดใจของบอร์ดมีคะแนน 2 หรือ 3 ดังนั้นยังคงมีแรงกดดันของหน่วยความจำ ฉันพบว่าจำเป็นต้องได้รับการยอมรับอย่างรวดเร็วมากกว่าการสร้างคนรุ่นก่อนหน้า อาจเป็นเรื่องที่น่าสนใจในการลดการใช้หน่วยความจำโดยทำการค้นหาในเชิงลึกมากกว่าการค้นหาแบบกว้างแรก แต่การทำเช่นนั้นโดยไม่ล้นสแต็คและไม่ต้องทำการคำนวณซ้ำซ้อนต้องใช้แนวทางร่วม

ฉันไม่คิดว่าการเพิ่มประสิทธิภาพแบบไมโครจะให้ฉันตามโค้ดของ Keith แต่เพื่อประโยชน์ฉันจะโพสต์สิ่งที่ฉันมี วิธีนี้จะแก้ปัญหา N = 5 ในเวลาประมาณหนึ่งนาทีบนเครื่อง 2GHz โดยใช้ Mono 2.4 หรือ. Net (ไม่มี PLINQ) และในประมาณ 20 วินาทีโดยใช้ PLINQ; N = 6 ทำงานเป็นเวลาหลายชั่วโมง

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

ฉันยังทำงานกับรุ่นอื่นเพื่อเดินถอยหลังจากจุดคงที่ ฉันได้ระบุจุดคงที่ไว้แล้วถึง N = 8 (สำหรับ N = 8 มี 84396613 ของพวกเขาก่อนที่จะกำหนดให้เป็นมาตรฐาน) ฉันเคยทำงานง่าย ๆ มาก่อน แต่มันช้าเกินไป ส่วนหนึ่งของปัญหาเป็นเพียงขนาดของสิ่งต่าง ๆ สำหรับ N = 6 บอร์ดว่างเปล่ามีรุ่นก่อนหน้า 574384901 (ก่อนการตั้งค่าแบบ canonicalization)
Keith Randall

1
3 วัน 11 ชั่วโมงเพื่อยืนยันว่า 91 เป็นคำตอบสำหรับ 6x6
Peter Taylor

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 ;

: 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 ;

สถิติเวลา:

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

และการทดสอบ 5 ล้มเหลว REPL ฮึ่ม ส่วนที่ไม่มีประสิทธิภาพมากที่สุดของโปรแกรมน่าจะเป็นลำดับของเกม ฉันอาจจะทำให้ดีขึ้นในภายหลัง


เย็น! ผมคิดว่าการส่งออกของคุณเป็นที่ 1 มีขนาดใหญ่เกินไปสำหรับรูปแบบ 3x3, ไม่ซ้ำลำดับที่ยาวที่สุดมีความยาว 3, 4 ไม่ได้ ...
ต่อ Alexandersson
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.