En uzun tekrarlanmayan Hayat Oyunu dizisi


16

Pozitif bir tamsayı N verildiğinde, N x N-ızgarasında, Yaşam Oyunu kuralları altında en uzun tekrarlanmayan diziyi veren başlangıç ​​desenini belirleyin ve bir torus üzerinde oynatılan sabit bir desenle (uzunluk 1 döngüsü) biter.

Amaç en kısa program değil, en hızlı programdır.

Dünya sonlu olduğu için, sonunda bir döngüye gireceksiniz, böylece daha önce ziyaret edilmiş bir devleti tekrarlayacaksınız. Bu döngü 1 periyoduna sahipse, başlangıç ​​deseni geçerli bir adaydır.

Çıktı: Başlangıç ​​deseni ve dizideki benzersiz durumların toplam sayısı (başlangıç ​​deseni dahil).

Şimdi, 1x1-torus özeldir, çünkü bir hücre kendisine komşu olarak kabul edilebilir veya edilmeyebilir, ancak pratikte sorun yoktur, tek bir canlı hücre her iki durumda da sadece ölür (aşırı kalabalık veya yalnızlık). Böylece, giriş 1 uzunluk 2 dizisini verir, dizi bir hücre yaşayan, sonra sonsuza dek ölüdür.

Bu soru için motivasyon, meşgul kunduz işlevinin bir analogudur, ancak kesinlikle daha az karmaşıktır, çünkü hafızada bir bağımız vardır. Bu, OEIS'e de dahil olmak için güzel bir dizi olacak.

N = 3 için dizi uzunluğu 3'tür, sol taraftaki herhangi bir desen tamamen siyah 3x3 kareye ulaşır ve sonra ölür. (1 döngünün parçası olan tüm desenler kaldırıldı).

Durumların grafiği


1
Ah, tamam. En iyi kod, 2 saat içinde en büyük N için dizi uzunluğunu hesaplamayı başaran koddur. Bariz karmaşıklık 2 ^ (N ^ 2) 'dir, bu yüzden bunu iyileştirmek mümkünse, bu iyi olurdu.
Per Alexandersson

1
Önemsiz boyutlarda, her bir desen 8N ^ 2 paterni izomorfizma sınıfının bir parçasıdır, bu nedenle kanonikleştirmenin hızlı bir yolu varsa, bu ılımlı bir artış sağlar.
Peter Taylor

1
Bu sekans OEIS'e eklendi mi?
mbomb007

1
Farkında olduğumdan değil, orada görmekten mutlu olurum.
Per Alexandersson

1
Bu diziyi (2, 2, 3, 10, 52, 91) O29'e A294241 olarak gönderdim .
Peter Kagey

Yanıtlar:


13

N = 6'ya kadar C ++

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                                                         

Bu teknik hızlı bir sonraki durum fonksiyonu etrafında toplanır. Her kart, N ^ 2 bitlik bir bit maskesi olarak temsil edilir ve tüm hücreler için bir sonraki durumu bir kerede hesaplamak için bit-twidly hileler kullanılır. N <8 nextiçin yaklaşık 200 100 montaj talimatını derler . Sonra tekrarlayana kadar standart try-all-state'ları yaparız ve en uzun olanı seçeriz.

6x6 için birkaç saat, 5x5'e kadar birkaç saniye sürer.

#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
nextSıralama yerine sayma yoluyla orta derecede iyileşme elde edebilirsiniz . #define H(x,y){x^=y;y&=(x^y);}ve sonra böyle bir şeyH(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

Gerçekten harika bir çözüm!
Per Alexandersson

@PeterTaylor: teşekkürler, saymayı (farklı bir şema) uyguladım, bir sürü talimatı kaydettim.
Keith Randall

9

Aşağıdaki olası çözüm yaklaşımlarını görüyorum:

  • Ağır teori. Torus'ta Yaşam hakkında bazı literatür olduğunu biliyorum, ama çok fazla okumadım.
  • Kaba kuvvet ileri: olası her kart için puanını kontrol edin. Bu, Matthew ve Keith'in yaklaşımlarının yaptığı şeydir, ancak Keith, kontrol etmek için tahta sayısını 4 faktör azaltıyor.
    • Optimizasyon: kanonik temsil. Bir tahtanın puanını değerlendirmek için gerekenden çok daha hızlı kanonik temsilde olup olmadığını kontrol edebilirsek, yaklaşık 8N ^ 2'lik bir faktörün hızlanmasını elde ederiz. (Daha küçük denklik sınıflarıyla kısmi yaklaşımlar da vardır).
    • Optimizasyon: DP. Her bir tahta için skoru önbelleğe alın, böylece yakınlaşana kadar veya ayrılıncaya kadar yürümek yerine daha önce gördüğümüz bir tahta bulana kadar yürürüz. Prensipte bu, ortalama skor / döngü uzunluğunun (belki 20 veya daha fazla) bir faktörüne hız kazandıracaktır, ancak pratikte büyük olasılıkla çok değişiyoruz. Örneğin, N = 6 için 2 ^ 36 skor için kapasiteye ihtiyacımız var, bu da skor başına bir baytta 16GB ve rastgele erişime ihtiyacımız var, bu yüzden iyi önbellek yeri bekleyemeyiz.
    • İkisini birleştirin. N = 6 için, tam kanonik temsil DP önbelleğini yaklaşık 60 mega puana indirmemize izin verecektir. Bu umut verici bir yaklaşım.
  • Kaba kuvvet geriye doğru. Bu başlangıçta tuhaf görünüyor, ancak hareketsiz yaşamları kolayca bulabileceğimizi ve Next(board)işlevi kolayca tersine çevirebileceğimizi varsayarsak, umduğum kadar olmasa da bazı faydaları olduğunu görüyoruz.
    • Tahta panoları ile hiç uğraşmıyoruz. Tasarruf pek değil, çünkü oldukça nadirdirler.
    • Tüm kartlar için puan depolamamız gerekmiyor, bu nedenle ileri DP yaklaşımından daha az bellek basıncı olmalı.
    • Geriye doğru çalışmak, literatürde gördüğüm bir tekniği, hala yaşamları numaralandırma bağlamında değiştirerek oldukça kolaydır. Her sütuna alfabedeki bir harf gibi davranıp, ardından üç harflik bir dizinin gelecek nesilde ortadaki diziyi belirlediğini gözlemleyerek çalışır. Hala hayatları numaralandırmaya paralel olarak o kadar yakın ki, onları sadece biraz garip bir yönteme dönüştürdüm,Prev2 .
    • Hareketsiz yaşamları sadece kanonikleştirebiliriz ve çok az maliyetle 8N ^ 2 hızlandırma gibi bir şey elde edebiliriz. Bununla birlikte, ampirik olarak, her adımda kanonikleşirsek, dikkate alınan kurul sayısında hala büyük bir azalma elde ediyoruz.
    • Şaşırtıcı derecede yüksek bir tahta oranı 2 veya 3 puana sahiptir, bu nedenle hala hafıza basıncı vardır. Önceki nesli inşa etmek ve sonra kanonlaşmak yerine kanonikleşmeyi gerekli buldum. Önce genişlik arama yerine derinlik ilkesi yaparak bellek kullanımını azaltmak ilginç olabilir, ancak bunu yığının üzerine taşmadan ve gereksiz hesaplamalar yapmadan yapmak önceki panoları numaralandırmak için bir rutin / devam yaklaşımı gerektirir.

Mikro-optimizasyonun Keith'in kodunu yakalamama izin vereceğini sanmıyorum, ancak ilgi uğruna sahip olduğum şeyi göndereceğim. Bu, N = 5'i Mono 2.4 veya .Net (PLINQ olmadan) kullanan bir 2GHz makinede yaklaşık bir dakika içinde ve PLINQ kullanarak yaklaşık 20 saniye içinde çözer; N = 6 saatlerce çalışır.

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

Ayrıca sabit noktalardan geriye doğru yürümek için başka bir sürüm üzerinde çalışıyorum. Zaten N = 8'e kadar sabit noktaları numaralandırdım (N = 8 için kanonikleştirmeden önce 84396613 tane var). Basit bir önceki çalışmam var, ama çok yavaş. Sorunun bir kısmı sadece şeylerin boyutları, N = 6 için boş tahta 574384901 öncüllerine (kanonikleştirmeden önce) sahip.
Keith Randall

1
3 gün 11 saat 91 olduğunu doğrulamak için 6x6 cevap.
Peter Taylor

1

faktör

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 ;

Bazı zaman istatistikleri:

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

Ve test 5 REPL'i çökertti. Hıh. Programın en verimsiz kısmı muhtemelen oyun dizisidir. Daha sonra daha iyi yapabilirim.


Güzel! Bence sizin çıktı 1 çok büyük, 3x3 desenleri için, en uzun tekrarlama dizisi uzunluğu 3 değil, 4 ...
Per Alexandersson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.