Порахуйте вперед і назад, а потім подвойте


24

Давайте порахуємо ...

Порахуйте до 2 і назад до 1
Порахуйте до 4 і назад до 1
Порахуйте до 6 і назад до 1
... Ок, ви це отримали ...

зберіть все це разом, і ви отримаєте наступну послідовність

 {1,2,1,2,3,4,3,2,1,2,3,4,5,6,5,4,3,2,1,2,3,4,5,6,7,8,7,6,5,4,3,2,1,2,3...}

Виклик
З огляду на ціле число n>0для 1-індексованого (або n>=0для 0-індексованого), виведіть n-й член цієї послідовності

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

Input->Output  

1->1  
68->6  
668->20  
6667->63  
10000->84

Правила

Ваша програма повинна мати можливість обчислювати рішення до n = 10000 за хвилину

Це , тому найкоротший код у байтах виграє!


2
Хто вирішує, що займає хвилину? Оптимальна за часом машина Тюрінга, побудована з лего, зайняла б дуже багато часу, тоді як той самий апарат Тюрінга, який моделювався в скажімо, С, мабуть, зайняв би секунди або хвилини, залежно від того, на якому процесорі він працює. Таким чином, якщо я подаю опис зазначеної машини Тьюрінга, чи правда це?
Артур

2
@Arthur Я думаю, що ти можеш зрозуміти, чому я зробив це обмеження ... Я не хотів, щоб алгоритм "навіки" знайшов n = 10000, створивши величезний список. Більшість людей тут дали геніальні відповіді, що знаходять мільйони за секунди.

4
@BillSteihn Я думаю, що обмеження непотрібне.
Erik the Outgolfer

2
@EriktheOutgolfer відповіді на гольф можуть бути хитрими ... без обмеження відповідь, що виробляє 10 000 кортежів [1,2 ... 2n..2,1], буде дійсною. Обмеження стосується лише таких відповідей. не бачу, де проблема. Я просто хочу, щоб у вашій відповіді знаходили всі тестові справи за розумну кількість часу.

3
@StraklSeth Загальний консенсус тут полягає в тому, що він повинен працювати в теорії, а не обов'язково на практиці.
Ерік Аутгольфер

Відповіді:


16

JavaScript (ES7),  59 ... 44  43 байт

Збережено 1 байт завдяки Титу

Очікуваний вхід: 1-індексований.

n=>(n-=(r=(~-n/2)**.5|0)*r*2)<++r*2?n:r*4-n

Спочатку надихнув формулу для A004738 , яка є подібною послідовністю. Але я закінчив переписати це цілком.

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

Як?

Послідовність може бути розташована у вигляді трикутника, ліва частина - у порядку зростання, а права частина - у порядку зменшення.

Нижче представлені перші 4 ряди, що містять перші 32 терміни:

            1 | 2
        1 2 3 | 4 3 2
    1 2 3 4 5 | 6 5 4 3 2
1 2 3 4 5 6 7 | 8 7 6 5 4 3 2

Тепер введемо кілька змінних:

 row  | range   | ascending part              | descending part
 r    | x to y  | 1, 2, ..., i                | 4(r+1)-(i+1), 4(r+1)-(i+2), ...
------+---------+-----------------------------+-----------------------------------------
  0   |  1 -  2 |                           1 | 4-2
  1   |  3 -  8 |                   1   2   3 | 8-4  8-5  8-6
  2   |  9 - 18 |           1   2   3   4   5 | 12-6 12-7 12-8  12-9  12-10
  3   | 19 - 32 |   1   2   3   4   5   6   7 | 16-8 16-9 16-10 16-11 16-12 16-13 16-14

Починаємо з 2 елементів вгорі і додаємо 4 елементи в кожен новий ряд. Тому кількість елементів у 0-індексованому рядку r можна виразити як:

a(r) = 4r + 2

1-індексоване початкове положення x рядка r задається сумою всіх попередніх доданків у цьому арифметичному ряду плюс один, що призводить до:

x(r) = r * (2 + a(r - 1)) / 2 + 1
     = r * (2 + 4(r - 1) + 2) / 2 + 1
     = 2r² + 1

Взаємно, з огляду на 1-індексовану позицію n у послідовності, відповідний рядок можна знайти з:

r(n) = floor(sqrt((n - 1) / 2))

або як JS-код:

r = (~-n / 2) ** 0.5 | 0

Як тільки ми знаємо r (n) , віднімаємо вихідне положення x (r) мінус одне від n :

n -= r * r * 2

Ми порівнюємо n з a (r) / 2 + 1 = 2r + 2, щоб з'ясувати, знаходимося ми у висхідній частині чи у низхідній частині:

n < ++r * 2 ?

Якщо цей вираз вірно, повертаємо n . В іншому випадку повертаємо 4 (r + 1) - n . Але оскільки в останньому твердженні r вже посилено, це спрощується як:

n : r * 4 - n

1
Гаразд, я думаю, що зрозумів. Довжина кожної частини вниз - 2,6,10,14 ... тому сума зростає з квадратом рядка, отже, і sqrt. Дуже хороша!
JollyJoker

7

Хаскелл , 37 байт

(!!)$do k<-[1,3..];[1..k]++[k+1,k..2]

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

Нульова індексація. Генерує список та індексує його. Дякуємо Ørjan Johansen за збереження 2 байтів!


Haskell , 38 байт

(!!)[min(k-r)r|k<-[0,4..],r<-[1..k-2]]

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

Нульова індексація. Генерує список та індексує його.


Хаскелл , 39 байт

n%k|n<k=1+min(k-n)n|j<-k+4=(n-k)%j
(%2)

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

Нульова індексація. Рекурсивний метод.




3

Perl 6 , 29 байт

{({|(1...$+=2...2)}...*)[$_]}

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

На основі 0

Розширено:

{  # bare block lambda with implicit parameter 「$_」

  (
    # generate an outer sequence

    {           # bare block lambda

      |(        # flatten into outer sequence

        # generate an inner sequence

        1       # start at 1

        ...     # go (upward) towards:

        $       # an anonymous state variable (new one for each outer sequence)
          += 2  # increment by 2

        ...     # go (downward) towards:

        2       # stop at 2 (1 will come from the next inner sequence)

      )
    }

    ...         # keep generating the outer sequence until:
    *           # never stop

  )[ $_ ]       # index into outer sequence
}

Внутрішня послідовність 1...$+=2...2виробляє

(1, 2).Seq
(1, 2, 3, 4, 3, 2).Seq
(1, 2, 3, 4, 5, 6, 5, 4, 3, 2).Seq
(1, 2, 3, 4, 5, 6, 7, 8, 7, 6, 5, 4, 3, 2).Seq
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 9, 8, 7, 6, 5, 4, 3, 2).Seq
...

Щоб воно було на основі 1, додайте 0,до другого {або додайте -1після$_


3

R, 64 байти

function(n)unlist(sapply(seq(2,n,2),function(x)c(2:x-1,x:2)))[n]

Функція, яка бере аргумент n. Він створює вектор 2:nз кроком 2. Для кожного з них вектор 1:(x-1)і x:2створюється. Це в цілому буде довше, ніж n. Ми unlistце, щоб отримати вектор і взятиn -у запис.


Ви могли б зробити 1:n*2замість цього seq(2,n,2)? Це буде більше, ніж потрібно, але це повинно бути добре! Крім того, я не думаю, що це спрацювало seq(2,n,2)для n=1будь-якого випадку!
Джузеппе

2

Python 2 , 56 байт

def f(x):n=int((x/2)**.5);print 2*n-abs(2*n*n+2*n+1-x)+2

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

Це 0-індексується.

-1 байт завдяки @JustinMariner

Як це працює

Зауважимо, що 1-індексована n-на група ( 1, 2, ... 2n ..., 2, 1) відбувається від елементів, пронумерованих 0-індексованими 2(n-1)^2до 2n^2.

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

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


Я пограв у цю частину: print 2*n-abs(2*n*n+2*n+1-x)+2- 2*n*n+2*nможна 2*n*-~nі +2+2*nможна перетворити на це -~n*2, що дозволяє нам перемістити її до початку, що економить байти ( 53 байти )
Містер Xcoder

2

05AB1E , 8 байт

Код:

ÅÈ€1Ÿ¦¹è

Використовує кодування 05AB1E . Спробуйте в Інтернеті!

Пояснення:

ÅÈ           # Get all even numbers until input (0, 2, ..., input)
  €1         # Insert 1 after each element
    Ÿ        # Inclusive range (e.g. [1, 4, 1] -> [1, 2, 3, 4, 3, 2, 1])
     ¦       # Remove the first element
      ¹è     # Retrieve the element at the input index

5
Не працює правильно, якщо ви не видалите ¦ , що також економить байт ofc :)
Emigna



2

Желе , 10 , 9 байт

ḤŒḄṖµ€Fị@

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

Також 1 індексований, і закінчується досить швидко.

Один байт збережено завдяки @ErikTheOutgolfer!

Пояснення:

Гіпотетично, скажімо, вхід ( a) дорівнює 3.

    µ€      # (Implicit) On each number in range(a):
            #
Ḥ           # Double
            #   [2, 4, 6]
            #
 ŒḄ         # Convert to a range, and Bounce
            #   [[1, 2, 1], [1, 2, 3, 4, 3, 2, 1], [1, 2, 3, 4, 5, 6, 5, 4, 3, 2, 1]]
            #
   Ṗ        # Pop
            #   [[1, 2], [1, 2, 3, 4, 3, 2], [1, 2, 3, 4, 5, 6, 5, 4, 3, 2]]
            #
     F      # Flatten
            #   [1, 2, 1, 2, 3, 4, 3, 2, 1, 2, 3, 4, 5, 6, 5, 4, 3, 2]
            #
      ị@    # Grab the item[a]
            #   1
            #

Ваш код еквівалентний Ḥ€ŒḄ€Ṗ€Fị@, тому ви можете використовувати µ€для -1 (три або більше монади на початку):ḤŒḄṖµ€Fị@
Ерік Аутгольфер

Це дійсно має бути ḤŒḄṖ<newline> ½ĊÇ€Fị@на 12, щоб відповідати вимозі 10 000 (запуск 9-байтного коду локально займає близько 2:20 на моєму i7 та використовує 7 ГБ)
Джонатан Аллан

1

MATL , 15 байт

li:"@EZv4L)]vG)

На основі 1.

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

Це час виходить на найбільші тестові випадки в TIO, але закінчується вчасно на моєму настільному комп’ютері (компілятор працює на MATLAB R2017a). Щоб відобразити минулий час, додайте Z`в кінці коду.

>> matl 'li:"@EZv4L)]vG)Z`'
> 10000
84
15.8235379852476

Пояснення

Код генерує набагато більше термінів, ніж потрібно. Зокрема, вона обчислює n"шматки" послідовності, де кожен шматок є відліку вгору і назад до 1.

l       % Push 1
i       % Push input, n
:       % Range [1 2 ...n]
"       % For each k in that range
  @E    %   Push 2*k
  Zv    %   Symmetric range: [1 2 ... 2*k-1 2*k 2*k-1 ... 2 1]
  4L)   %   Remove last entry: [1 2 ... 2*k-1 2*k 2*k-1 ... 2]
]       % End
v       % Concatenate all stack contents into a column vector
G)      % Get n-th entry. Implicitly display

приємно! TIO іноді повільно ...

1
Ну, головна причина повільності тут - алгоритм (який генерує набагато більше термінів, ніж потрібно). Також компілятор MATL не особливо швидкий
Луїс Мендо

1

Лушпиння , 12 10 байт

!ṁ§¤+hḣṫİ0

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

1-індексований, працює досить швидко

Пояснення

!ṁ§¤+hḣṫİ0
 ṁ      İ0    Map the following function over the even numbers and concatenate the results together
  §   ḣṫ      Get the ranges 1-n and n-1, then... 
   ¤+h         remove the last element from both of them and concatenate them together
!             Return the element of the resulting list at the given index

Використання 8 байт
Zgarb

@ Zgarb це чудова ідея, і ви, ймовірно, повинні опублікувати її як свою відповідь :)
Лев,



1

Сітківка , 62 байти

.+
$*
^((^.|\2..)*)\1.
6$*1$2$2;1
(?=.+;(.+))\1(.+).*;\2.*
$.2

Спробуйте в Інтернеті! Посилання включає тестові випадки. Вхід 1-індексований. Перший етап - це лише десяткове до одинарне перетворення. Другий етап знаходить найбільше квадратне число sстрого менше половини n; $1є , поки $2є 2s-1. Він обчислює два значення, по-перше, кількість чисел у поточному прогоні "вгору / вниз" 4(s+1) = 4s+4 = 2$2+6, а по-друге, позиція в межах цього прогону, яка є n-2s² = n-(2$1+1)+1 = n-$&+1, яка просто вимагає 1компенсувати 1використане для забезпечення суворої нерівності. Заключний етап потім відлічується від цієї позиції до початку та кінця пробігу і отримує нижчий результат і перетворює його в десятковий.



1

Perl 5 , 43 + 1 (-p) = 44 байти

$_=($n=2*int sqrt$_/2)+2-abs$n/2*$n+$n+1-$_

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

Я працював над формулою для прямого обчислення n-го елемента. Потім я побачив, що @ fireflame241 зробив цю роботу, і я переграв його в Perl.

# Perl 5 , 50 + 1 (-n) = 51 байт

push@r,1..++$",reverse 2..++$"while@r<$_;say$r[$_]

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

Результати індексовані 0.


1

Haskell , 115 81 байт

y%x=snd(span(<x)$scanl(+)y[y+1,y+3..])!!0
g 1=1
g x|1%x>2%x=1+g(x-1)|1>0=g(x-1)-1

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

Тут відбувається якась магія. Можливо, може бути і коротше, якби я використовував звичайний підхід.

Пояснення

Спочатку визначимось %. %це функція, яка приймає дві змінні x, і y. Він створює список scanl(+)y[y+1,y+3..]і знаходить, що перший елемент цього списку перевищує x. scanl(+)просто виконує ітераційні суми, щоб отримати трикутні числа, які ми зробили б scanl(+)0[1..], щоб отримати квадратні числа, які ми зробили б scanl(+)0[1,3..]. Два списки, зокрема, які ми будемо будувати, є scanl(+)2[3,5..]іscanl(+)1[2,4..] ці точки перегину візерунка.

Тепер ми визначимо основну функцію, gяка виконує x. Якщо xце один, ми повертаємось, 1тому що це перше значення. Інакше ми перевіряємо наступні дві точки перегину, якщо нижній перегин більший, 1%x>2xми повертаємо наступника, g$x-1інакше повертаємо попередника g$x-1.

Гаразд, але чому це працює?

Перш за все "Що з тим, як ми знаходимо вершини?". Важливо відзначити відстань між послідовними вершинами одного типу. Ви помітите, що різниці зростають на 2 рази кожен раз. Це має сенс, оскільки основи трикутників з кожним разом стають ширшими на 2. Ми можемо зробити постійну різницю списку, використовуючи такий буквальний список, який [2,4..]ми використовуємоscanl(+) для перетворення цих списків у наші списки вершин, виходячи з розташування першої вершини та першої різниці.

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

Haskell , 56 51 46 байт

Ось моє краще рішення з меншою кількістю математики та меншою кількістю байтів.

d x|e<-[1..x-1]=e++map(x+1-)e
(([1..]>>=d)!!0)

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



1

C # (.NET Core) , 120 байт

Пояснення: досить простий, перший вкладений цикл піднімається до нашого максимуму, другий піднімається назад до 2. Повторення для кожного кратного 2.

x=>{var a=0;for(int i=2,j=0;j<x;i+=2){for(var b=1;b<=i&j<x;b++,j++){a=b;}for(var c=i-1;c>1&j<x;c--,j++){a=c;}}return a;}

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


1

Рубі , 78 75 байт

Збережено 1 байт завдяки кроку Хен

Збережено 1 байт завдяки панові Xcoder

->n{a=0;b=2;c=1;n.times{if a==b then c=0;b+=2;end;c=1if a<2;a+=c<1?-1:1};a}

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

Сподіваюсь, я можу отримати кілька порад щодо того, як більше знизити рахунок. Я спробував застосувати простий підхід.


Ласкаво просимо до PPCG! c=1 ifможна пограти в гольф доc=1if
Стівен

76 байт:->n{a=0;b=2;c=1;n.times{if a==b then c=0;b+=2;end;c=1if a==1;a+=c<1?-1:1};a}
Містер Xcoder

1

Java (OpenJDK 8) , 53 байти

n->{int i=2;for(;n>i;i+=4)n-=i;return n>i/2?i-n+2:n;}

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

-2 байти завдяки Невайю.

1-індексований.

TL; DR Ми розділили послідовність на зручні шматки, знайдемо шматок n, а потім знайдемо nthположення в шматку.

Тут ми можемо розділити подібну послідовність [[1,2],[1,2,3,4,3,2],[1,2,3,4,5,6,5,4,3,2],...], яка дає нам розміри розмірів 4i-2. Починаючи з i=2, ми віднімаємо iз n, по суті , рухається вгору шматок в той час. Коли ми задовольняємось n<=i, ми знаємо n, що зараз положення правильного значення в поточному фрагменті.

Після цього ми отримаємо значення, порівнюючи nз i, розміром шматка. Середина кожного куска дорівнює i/2+1; якщо nменше цього, ми просто повертаємось n. Якщо nбільше, ми повертаємось i-n+2.

Приклад

n = 16, i = 2

Is n > i? Yes, n = n - 2 = 14, i = i + 4 = 6
Is n > i? Yes, n = n - 6 = 8, i = i + 4 = 10
Is n > i? No, stop looping.
10 / 2 + 1 = 6
Is n > 6? Yes, return i - n + 2 = 8 - 6 + 2 = 4

Вам не потрібно +1, return n>i/2?i-n+2:nдостатньо.
Невай

Ага. Спасибі, ціле ділення.
Ксандерхолл



0

QBIC , 47 байт

g=q{p=p+1~p=:|_Xg\g=g+q~g=1or g>=r|r=r+1┘q=q*-1

Пояснення

g=q         var g is the current value of the sequence; set to 1 at the start
{           DO infinitely
p=p+1       raise the step counter (var p)
~p=:|_Xg    IF p equals the input term a (read from cmd line) THEN QUIT, printing g
\           ELSE
g=g+q       raise (or decrement) g by q (q is 1 at the start of QBIC)
~g=1        IF g is at the lower bound of a subsequence
    or g>=r OR g is at the upper bound (r start as 2 in QBIC)
|r=r+1      THEN increment r (this happens once on lower bound, and once on upper, 
            total of 2 raise per subsequence)
┘q=q*-1     and switch q from 1 to -1

0

Рода , 54 байти

f n{seq 1,n|{|i|seq 1,2*i;seq 2*i-1,2}_|head n+2|tail}

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

Телефонуйте за допомогою: try f(n)

Ця функція швидко повертає відповідь, але після цього проводиться кілька непотрібних обчислень і в підсумку закінчується пам'ять.

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

(У Röda функції можуть повертати значення, перш ніж вони вийдуть через паралелізм.)



0

PHP, 65 + 1 байт

for($x=$d=$z=1;--$argn;)$d=($x+=$d)>1?$x>$z?-1:$d:!!$z+=2;echo$x;

Запустити як трубу з -Rабо спробуйте його в Інтернеті (або відмініть одну з інших версій).

Порт рекурсивного JavaScript tsh займає 66 байт:

function f($n,$t=2){return$t<2*$n?$t<$n?f($n-$t,$t+4):$t-$n+2:$n;}

Порт рішення Арнальда займає 62 + 1:

$n=$argn;echo($n-=($r=(~-$n/2)**.5|0)*$r*2)<++$r*2?$n:$r*4-$n;

Гольф-порт Xanderhall's Java має найкоротший код досі (55 + 1 байт):

for($n=$argn;$n+2>$i+=4;)$n-=$i-2;echo$n*2>$i?$i-$n:$n;

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