Електрони підстрибують у дроті


46

Уявіть собі «дріт», який має nпробіли. Далі уявіть, що в цьому дроті є "електрони". Ці електрони живуть лише одну одиницю часу. Будь-які проміжки в дроті, які примикають до точно одного електрона, стають електроном. У термінології Game of Life це так B1/S.

Наприклад, це дріт довжиною 10, з періодом 62.

введіть тут опис зображення

Правила

  • Вхідне значення,, nє єдиним, додатним цілим числом.
  • Вихід повинен бути єдиним цілим числом, що позначає період дроту довжиною n.
  • Початковий стан - це один електрон на одному кінці дроту.
  • Період не обов'язково включає стартовий стан. Деякі довжини ніколи не повертаються до початкового стану, але всі вони періодичні.
  • Статичний провід (тобто той, що не має електронів) має період 1.
  • Прикордонні умови не періодичні. Тобто, дріт ніяк не тороїдальний.

Тестові справи

Особлива подяка orlp за створення цього списку. (Я перевірив це до n = 27.)

1 1
2 2
3 1
4 6
5 4
6 14
7 1
8 14
9 12
10 62
11 8
12 126
13 28
14 30
15 1
16 30
17 28
18 1022
19 24
20 126
21 124
22 4094
23 16
24 2046
25 252
26 1022
27 56
28 32766
29 60
30 62
31 1
32 62
33 60
34 8190
35 56
36 174762
37 2044
38 8190
39 48
40 2046
41 252
42 254
43 248
44 8190
45 8188

Тут ви можете переглянути тестові випадки для n = 2 до 21 за допомогою мого симулятора Game-of-Life-Esque: Варіації життя .


EDIT: послідовність тут була опублікована як A268754 !


всі вони періодичні. І період обмежений 2^n-1, тому що це кількість можливих ненульових станів "дроту"
Луїс Мендо

@LuisMendo: Насправді верхня межа менша, тому що електрони ніколи не суміжні. Скільки саме менше, я не знаю.
El'endia Starman

The period does not necessarily include the starting state. Some lengths never return to the starting state, but all of them are periodic.У вас є приклад?
edc65

@ edc65: Провід довжиною 5 є найменшим прикладом.
El'endia Starman

1
Більш сильно, електрони чергуються між непарними і парними положеннями, тому період становить максимум 2 ^ (n / 2 + 1).
xnor

Відповіді:


10

Python 2, 148 142 87 байт

n=input()
p=l=1
t=1
h=2
while t!=h:
 if p==l:t,l,p=h,0,p*2
 h=h/2^h*2%2**n;l+=1
print l

Використовує алгоритм виявлення циклу Brent і, таким чином, фактично працює швидко.


Я також написав анімацію на Python (обидві роботи 2 та 3). Це потрібно pygletбігти. Ви можете переглянути анімацію, запустивши:

python -m pip install --user pyglet
curl -s https://gist.githubusercontent.com/orlp/f32d158130259cbae0b0/raw/ | python

(Не соромтеся завантажувати суть і перевіряти код перед запуском.) Ви можете натиснути клавіші + і -, щоб збільшити / зменшити кількість візуалізованих n .


І нарешті, для тих, хто зацікавлений у подальшому дослідженні цієї послідовності чисел, ось читабельна версія (це було використано для генерації тестових випадків вище):

# Brent's cycle detection, modified from wikipedia.
def electron_period(n):
    wire_mask = (1 << n) - 1
    power = lam = 1
    tortoise, hare = 1, 2
    while tortoise != hare:
        if power == lam:
            tortoise = hare
            power *= 2
            lam = 0
        hare = ((hare << 1) ^ (hare >> 1)) & wire_mask
        lam += 1
    return lam

Я знаю, що минуло більше року, але мені цікаво, чи використання HashLife би скоротило його взагалі
лише для ASCII

7

Математика, 127 байт

p@n_:=Tr[#2-#]&@@Position[#,Last@#]&@NestWhileList[1~Table~n~ArrayPad~1*18~CellularAutomaton~#&,{1}~ArrayPad~{1,n},Unequal,All]

Пояснення

Правило 18 :

{0,0,0} -> 0
{0,0,1} -> 1
{0,1,0} -> 0
{0,1,1} -> 0
{1,0,0} -> 1
{1,0,1} -> 0
{1,1,0} -> 0
{1,1,1} -> 0

Тестові справи

p/@Range[10]
(* {1,2,1,6,4,14,1,14,12,62} *)

7

Python 2, 68 байт

f=lambda n,k=1,l=[]:k in l and-~l.index(k)or f(n,k/2^k*2%2**n,[k]+l)

Виявлення циклу може бути кращим, але ітеративний крок приємний.

k -> k/2^k*2%2**n

Представляючи масив як двійкове число k, оновлення можна здійснити, взявши XOR kзсунутого ліворуч /2і одного правого з *2, а потім обрізання до nбайтів як %2**n.


4

Python 3 2, 134 121 118 байт

Q=input()
h=[]
n=[0,1]+Q*[0]
while n not in h:h+=[n];n=[0]+[n[d]^n[d+2] for d in range(Q)]+[0]
print len(h)-h.index(n)

В основному такий же, як і моя відповідь Pyth , але дещо адаптував його через відсутність певних вбудованих функцій у Python.

Безгольова версія:

length = input()
history = []
new = [0] + [1] + length*[0]
while new not in history:
    history += [new]
    new = [0] + [new[cell]^new[cell+2] for cell in range(length)] + [0]
print len(history) - history.index(new)

4

Pyth, 39 36 байт

L++Zmx@bd@bhhdQZ-lJ.uyN.[,Z1ZQ)xJyeJ

Використовує функцію "кумулятивної фіксованої точки" для ітерації лише до того, як конфігурація повториться, і поверне всі проміжні конфігурації як список списків. Це працює, тому що правила детерміновані, а конфігурація наступного покоління є функцією поточної конфігурації. Це означає, що як тільки знову з'явиться однакова конфігурація, автомати завершили цикл.

Досягнувши цього (остання ітерація циклу), він останній раз викликає функцію наступного роду в останньому елементі списку "історія", щоб отримати наступну конфігурацію (яка є початковою конфігурацією циклу), і знайти свій індекс в історії. Тепер період - це просто тривалість (1 + індекс кінця циклу) мінус індекс початку циклу.

У пітонічному псевдокоді:

                  Z = 0
                  Q = eval(input())
L                 def y(b):                # generates next-gen from current(param)
  ++Zm                return [Z] + map(    # adds head zero padding
    x@bd@bhhd             lambda d: b[d] ^ b[1+ 1+ d],  # xor the states of adjacent cells
    Q                     range(Q))        # implicit range in map
    Z                     + [Z]            # adds end zero padding
-lJ.uyN.[,Z1ZQ)   J = repeatTilRecur(lambda N,Y:y(N), padRight([Z,1],Z,Q)); print( len(J) -
                  # N:value; Y:iteration count
  JxJyeJ              J.index( y( J[-1] ) ) )

Тестовий набір займає занадто багато часу, щоб сервер його убив, тому вам потрібно буде скористатися звичайною програмою і протестувати її по черзі, або встановити Pyth (якщо у вас цього немає) і використовувати сценарій для тестування.


4

Желе, 19 18 17 байт

H^Ḥ%®
2*©Ç‘СUµiḢ

Цей код обчислює перші 2 n стани, тому він не особливо швидкий і не працює в пам'яті ...

Спробуйте в Інтернеті! або перевірити перші 16 тестових випадків .

Альтернативна версія, 13 байт (неконкурентна)

Версії Jelly, які вирішують цей виклик, мають вбудоване виявлення циклу, що дозволяє вирішити і коротше, і ефективніше.

H^Ḥ%®
2*©ÇÐḶL

Це легко обробляє останній тестовий випадок. Спробуйте в Інтернеті!

Як це працює

2*©Ç‘СUµiḢ    Main link. Input: n (integer)

2*             Compute 2 ** n.
  ©            Store the result in the register.
     С        Do the following:
   Ç             Apply the helper link, which updates the state, ...
    ‘            2 ** n + 1 times.
               Collect all 2 ** n + 2 intermediate results in a list.
       U       Upend; reverse the list of results.

        µ      Begin a new, monadic chain. Argument: R (list of results)
          Ḣ    Yield and remove the first element of R (final state).
         i     Find its first index in the remainder or R.
               This is the length of the loop.

H^Ḥ%®        Helper link. Argument: s (state, integer)

H            Halve s. This yields a float, but ^ will cast to integer.
  Ḥ          Double s.
 ^           Compute (s ÷ 2) ^ (s × 2).
    ®        Retrieve the value stored in the register (2 ** n).
   %         Compute ((s ÷ 2) ^ (s × 2)) % (2 ** n).

Зауважте, що допоміжна посилання застосовується до 2 n в першій ітерації, що не кодує дійсний стан. Однак ((2 n ÷ 2) ^ (2 n × 2))% 2 n = (2 n - 1 + 2 n + 1 )% 2 n = 2 n - 1 , що є одним із можливих стартових станів.

Оскільки ми циклічаємо 2 n + 1 рази, ми гарантовано зустрінемося із станом двічі, що зробить виявлення циклу успішним.


3

CJam, 41 34 31 27 25 байт

Завдяки Деннісу за збереження 3 байтів. Позичення ідеї з його відповіді Jelly врятувало ще 4.

2ri#_){__4*^2/W$%}*]W%(#)

Перевірте це тут.

Пояснення

Я представляю дріт просто як ціле число (біти якого вказують положення електронів), а не використовувати фактичний список бітів. Стан оновлюється за допомогою досить простих розрядних обчислень.

Щоб знайти період, ми збираємо всі проміжні результати на стеку, запустимо моделювання протягом 2 n-1 +1 кроків, а потім визначимо період як кількість елементів з моменту останнього появи остаточного стану (плюс 1).

Ось розбивка коду:

2ri#   e# Compute 2^n. This has a 1 in the n+1-th bit and zeroes below it. This is
       e# itself not a valid state but will be turned into 2^(n-1) by the first
       e# update.
_)     e# Duplicate and increment to get number of steps to simulate.
{      e# Repeat that many times...
  __   e#   Duplicate the last state twice.
  4*   e#   Multiply by 4, shifting all bits to the left by two positions.
  ^    e#   XOR - we only keep keep those cells where we have exactly one 1-bit
       e#   between both copies, i.e. those that neighboured a single electron
       e#   but shifted up by one position. We don't need handle the survival rule
       e#   explicitly, since electrons can never be adjacent in the first place.
  2/   e#   Divide by 2 shifting all bits back to the right again.
  W$   e#   Copy the initial number 2^n.
  %    e#   Modulo - this simply sets the first bit to 0.
}*
]      e# Wrap all states in an array.
W%     e# Reverse it.
(      e# Pull off the latest state.
#      e# Find its position in the remainder of the array.
)      e# Increment.

2

JavaScript (ES6) 99 104

n=>eval("a=[...Array(n)];k={};for(a[0]=i=1;!k[a=a.map((v,i)=>v?0:a[i-1]^a[i+1])];k[a]=i++);i-k[a]")

Тест

f = n=>eval("a=[...Array(n)];k={};for(a[0]=i=1;!k[a=a.map((v,i)=>v?0:a[i-1]^a[i+1])];k[a]=i++);i-k[a]")

console.log = x => O.textContent += x + '\n';

;[...Array(45)].map((_, i) => console.log(++i + ' ' + f(i)))
<pre id=O></pre>


2

Haskell, 170 байт

x!pдає елемент в індексі p, якщо в межах, інше 0. nобчислює наступний крок. g iдає той iкрок. c xдає період, якщо починати з x. fзагортає все це разом.

n x|l<-length x-1=[mod(x!(p-1)+x!(p+1))2|p<-[0..l],let y!q|q<0=0|q>=l=0|1<2=y!!p]
c x=[i-j|i<-[1..],j<-[0..i-1],let g k=iterate n x!!k,g i==g j]!!0
f n=c$1:map(*0)[2..n]

.


2

MATL , 38 37 36 35 байт

1Oi(`t0Y)5BX+8L)1=vt6#Xut0)=fdt?w}A

Для цього використовується цикл, який продовжує обчислювати нові стани, поки новий стан не зрівняється з будь-яким з попередніх. Кожен стан є вектором нулів і одиниць. Ці вектори зберігаються у вигляді рядків зростаючого 2D масиву.

Обчислення кожного нового стану проводиться шляхом з'єднання поточного стану з послідовністю [1,0,1], зберігаючи лише центральну частину, і встановлюючи 0будь-який запис, який не є 1.

EDIT (13 травня 2016 р.) Код у наступному посиланні був дещо змінений відповідно до змін, внесених у мову після написання цієї відповіді.

Спробуйте в Інтернеті!

1Oi(            % create initial vector [1,0,0,...,0], with size equal to input
`               % do...while loop
  t0Y)          %   duplicate. Get last row of array: most recent vector
  5BX+8L)       %   compute new vector by convolving the most recent one
                %   with [1,0,1] and keeping only the central part
  1=            %   set ones to 1, rest to 0
  v             %   append to 2D array
  t6#Xu         %   compute vector of unique numeric labels, so that equal rows
  t0)=f         %   indices of entries whose value equals that of the last entry.
                %   This will contain the index of the last entry and possibly
                %   another index, in which case we've found a repetition
  d             %   this will either be empty (which is falsy) or contain the
                %   period, which is a nonzero number (and thus truthy)
  t?            %   duplicate. If non-empty (and nonzero)
    w           %     swap to put the 2D-array at the top of the stack. This is
                %     falsy, because it contains at least one zero, even in the
                %     n=1 case (the array is initiallized to 0 in that case)
                %     So the loop will terminate, with the period left on the stack
  }             %   else
    A           %     this transforms the empty array at the top of the stack
                %     into a true value, so that the loop will continue
                %   implicitly end if
                % implicitly end loop
                % implicitly display stack contents (period)

1

Perl 6, 81 байт

{my@h=my$w=2;@h.push($w=$w/2+^$w*2%2**$_)while 2>@h.grep($w);[R-] @h.grep($w,:k)}

Трохи розширилися і нерозберися

-> $n {
    my @history = my $wire = 2;
    while 2 > @history.grep($wire) {
        @history.push($wire = $wire/2 +^ $wire*2 % 2**$n)
    }
    [R-] @history.grep($wire,:k)
}

Трохи пояснення:

  • [op]зменшує список за допомогою оп. Наприклад [+] @list, підсумуємо@list
  • R- мета-оп, який обертає аргументи, подані до оп. Наприклад, 1 R- 3це призведе до 2.

1

C ++, 211 байт

Гольф

#include <bitset>
#include <cstdio>
#define B std::bitset<1<<10>
B m,t(1),h(2);int main() {int p,l;for(scanf("%d",&p);p--;m.set(p));
for(p=l=1;t!=h;h=(h>>1^h<<1)&m,l++)p==l?t=h,p*=2,l=0:0;return !printf("%d",l);}

З доданим пробілом для читабельності

#include <bitset>
#include <cstdio>
#define B std::bitset<1<<10>
B m,t(1),h(2);
int main() {    
    int p,l;
    for(scanf("%d",&p);p--;m.set(p));
    for(p=l=1;t!=h;h=(h>>1^h<<1)&m,l++)p==l?t=h,p*=2,l=0:0;    
    return !printf("%d",l);
}

Хороша практика для біт C ++; і чудова освіта про алгоритм виявлення циклу Brent (як його використовує @orlp)




Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.