Шестикутна примикання


28

Приклад спіраль із шестикутником

На наведеному вище зображенні відображається шестикутна сітка шестикутників. Кожній комірці в сітці присвоюється індекс, починаючи від центру і спірально проти годинникової стрілки навколо, як показано. Зауважте, що сітка продовжуватиметься нескінченно - наведена вище картинка - це просто перший розділ. Наступний шестикутник буде суміжним із 60 та 37.

Ваше завдання - визначити, чи є дві задані комірки в цій сітці сусідніми.

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

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

Пробні кейси:

0, 1
7, 18
8, 22
24, 45
40, 64
64, 65

Тести Фолсі:

6, 57
29, 90
21, 38
38, 60
40, 63
41, 39
40, 40

Це тому найкоротша відповідь у байтах виграє. Пояснення навіть для неезотеричних мов заохочуються.

Відповіді:


7

Еліксир , 263 257 264 223 214 218 214 байт

a=fn x,y->i=&(&1*(&1-1)*3+1)
[x,y]=Enum.sort [x,y]
if x<1,do: y in 1..6,else: (y-x==1||fn->a=y-trunc i.((r=(:math.sqrt(12*x-3)+3)/6)+1)
t=trunc r
a in [0,1,rem(b=x-i.(t)+1, t)<1&&b-t*6!=0&&2]||b<2&&a==-1 end.())end

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

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

def get_ring(x) do
    1/6*(:math.sqrt(12*x-3)+3)
end

def inv_get_ring(x), do: x*(x-1)*3+1

def ring_base(x), do: inv_get_ring(trunc(x))

def is_corner(x) do
    ring = trunc(get_ring(x))
    inv_ring = ring_base(ring)
    stuff = (x-inv_ring+1)
    rem(stuff, ring) == 0
end

def is_last(x),do: ring_base(get_ring(x)+1)-1 == x
def is_first(x),do: ring_base(get_ring(x)) == x

def hex_adj(x, y) do
    {x, y} = {min(x,y), max(x,y)}
    cond do 
        x == 0 ->y in 1..6      
        y-x==1 -> true
        true ->
            adj = trunc(inv_get_ring(get_ring(x)+1))
            number = if is_corner(x)&&!is_last(x), do: 2, else: 1
            if y-adj in 0..number do
                true
            else
                is_first(x) && y == adj-1
            end
    end
end
  • trunc(number) Повертає цілу частину числа
  • rem(a,b) Повертає залишок a / b
  • cond do end Це еквівалентно іншому, якщо або перемикають регістри у багатьох імперативних мовах

Пояснення

get_ring (індекс)

Обчислює "кільце" індексу.

Приклад: 1 для 1-6, 2 для 7-18 тощо.

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

inv_get_ring (дзвінок)

Обчислює обернену get_ring(index).

ring_base (кільце)

Обчислює індекс першої плитки в кільці.

is_corner (індекс)

Куточки - це плитка, яка має три суміжні плитки у зовнішньому кільці. Найпотужніше кільце повністю складається з кутів.

Приклади: 21,24,27,30,33,36

is_last (індекс)

Істинно, якщо цей показник найвищий у своєму кільці.

is_first (індекс)

Істинно, якщо це основна плитка кільця.


2
Я відредагував відповідь, щоб включити виправлення до
кращого

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

Так! Я щойно дізнався, що ви можете оголосити змінні вбудовані в Elixir. Це дало мені можливість позбутися лямбда-функцій на початку коду. Я просто перетасую змінні навколо, щоб зробити його більш ефективним.
Гаруно

5

MATL , 47 45 44 43 41 байт

s:"JH3/^6:^t5)w5:&)@qY"w@Y"]vYs0hG)d|Yo1=

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

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

Пояснення

Загальна ідея    Код спочатку будує досить велику шестикутну спіраль з одиничним кроком. Спіраль визначається як вектор складних чисел, що представляють положення клітинних центрів. Індексація у цей вектор за допомогою вхідних чисел та обчислення абсолютної різниці дає відстань між двома вказаними осередками. Клітини суміжні, якщо і лише якщо результат дорівнює 1. Однак через неточності з плаваючою комою необхідно округлення перед порівнянням з 1.

Побудова спіралі    Спіраль матиме кількість "шарів", рівних сумі двох входів. Це (набагато) більше, ніж потрібно, і гарантує, що вхідні комірки будуть присутній у спіралі.

Для побудови спіралі спочатку обчислюється комплексне число j 2/3 (де j - уявна одиниця). Підвищення цього до показників 1 до 6 дає основний набір переміщень, таким чином, щоб слідуючи цим переміщенням, щоб простежити шестикутник. Цей шестикутник формував би найпотаємніший шар спіралі, за винятком того, що він був би «закритим». Власне, ми хочемо, щоб шестикутник «зростав» на останньому кроці, а потім простежимо більший шестикутник, у два рази більше точок (вирівняних у групи по дві), щоб утворився наступний шар спіралі; див. ілюстрацію тут . Наступний шар матиме втричі більше очок, ніж перший (у групах по три); дивіться тут .

Для цього п'ятий зсув від базового набору (який вказує у південно-східному напрямку) вибирається як "зростаючий" крок. Шар k починається з цього кроку, після чого проводяться перші п’ять основних кроків, повторених k разів, а потім шостий крок (напрямок на схід), повторений k −1 раз. Це, сподіваємось, стає зрозумілішим, дивлячись на дві фігури, пов'язані вище.

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

Нарешті, початкова комірка, розташована у 0, приєднана до кінця цього вектора. Це відбувається тому, що MATL використовує модульну індексацію на основі 1, а індекс 0 посилається на останній запис масиву.

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

Коментований код

s         % Implicitly input array of two numbers. Push their sum, say S
:         % Range [1 2 ... S]
"         % For each k in [1 2 ... S]
  J       %   Push 1j
  H3/     %   Push 2, then 3, then divide: gives 2/3
  ^       %   Power
  6:      %   Push [1 2 ... 6]
  ^       %   Element-wise power. This is the array of 6 basic displacements
  t5)     %   Duplicate. Get 5th entry
  w5:&)   %   Swap. Push subarray with entries 1st to 5th, then push 6th
  @qY"    %   Repeat the 6th displacement k-1 times
  w@Y"    %   Swap. Repeat 1st to 5th displacements k times
]         % End
v         % Concatenate everything into a column vector
Ys        % Cumulative sum. This gives the cell center coordinates
0h        % Append a 0
G)        % Index by the input vector
d|        % Absolute difference
Yo        % Round to nearest integer
1=        % Does it equal 1? Implicitly display

Чи можете ви додати пояснення?
Shaggy

@Shaggy Я додав загальне пояснення. Повідомте мене, якщо це зрозуміло (важко пояснити). Я додам коментований код пізніше
Луїс Мендо

2

05AB1E (спадщина) , 30 29 27 байт

α2‹i1q}1ݹ+v12y*3-tîÌy+}Ÿ²å

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

Пояснення коду:

α2‹i1q}                     : if the absolute diff of the two number is 1 or 0 return 1
                          ²å: return that the second number is in
                         Ÿ  : range of {
       1Ý                   :  create [0, 1]
         ¹+                 :  add the first number to the elements
           v            }   :  map that list
            12y*3-tîÌy+     :  calculate the corresponding value where it's an adjacency
                                }

Пояснення математики:

Я "витрачав" близько 5 годин, роблячи цей гольф. Коротше кажучи, я почав робити 2-графічний графік вхідних даних і намалювати місця, Xде вони сусідять один з одним. Тоді я знайшов зразок. Я шукав його на OEIS та бінго! Я знайшов цю послідовність, і я використав формулу, подану на веб-сайті.


1

C (gcc) , 175 173 байт

Дякую Пітеру Тейлору за те, що зловив помилку.

Завдяки стельовій коті на -2 байти. Цей оператор продовжує залишатися моїм головним сліпою точкою.

c,r,C,L;y(a){a=a<L*2?L-a:a<L*3?-L:a<5*L?a-L*4:L;}z(a){L=ceil(sqrt(a/3.+.25)-.5);C=y(a-=3*L*~-L);L?L=y((L+a)%(L*6)):0;}f(a,b){z(a);c=C,r=L;z(b);a=a-b&&(abs(c-C)|abs(r-L))<2;}

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

Цей підхід орієнтований на пошук рядка та стовпця двох комірок та порівняння їх; будь-які сусіди не можуть мати відповідні координати, які відрізняються більш ніж на 1. Переходячи від центру назовні, ми спостерігаємо, що кожен шар має на 6 комірок більше, ніж попередній. Це означає, що найвищий "індекс" у кожному шарі L має форму 6 * (L * (L - 1) * (L - 2) ...), або C = 6 * (L 2 + L) / 2 , де C - "глобальний" номер комірки. Перемішуючи речі, ми отримуємо L 2 + L - C / 3 = 0, що дає спалахи з математики середньої школи. З цього отримуємо формулу ceil (sqrt (1/4 + C / 3) + 0,5). Підключаючи до нього глобальний індекс клітини, ми отримуємо, в якому шарі знаходиться клітина.

Оскільки перша клітина в кожному шарі природно на одну вище, ніж найвища з попереднього шару, ми знаходимо L start = (6 * (L - 1) 2 + (L - 1)) / 2, що спрощує до 3 * (L 2 - L). З цього отримуємо індекс шару L index = C - L start .

Далі ми бачимо, що кожен шар складається з шести секцій, кожна довжиною L. Починаючи з північного сходу та йдучи проти годинникової стрілки, ми бачимо, що для перших двох розділів (1 <= L індекс <= 2 * L) , ми отримаємо стовпець з L - L індексу . У наступному розділі L * 2 <L індекс <= L * 3 є всі комірки, що розділяють один стовпець -L. Два наступні розділи - L * 3 <L індекс <= L * 5 з їх стовпцями відповідно до індексу L - L * 4. І нарешті, шостий розділ має всі комірки на стовпці L. Ми можемо зрушити верхні межі на один крок уздовж щоб зберегти кілька байтів у коді.

То що ж робити з рядками? Для повторного використання коду повертаємо сітку так, щоб комірка 44 була прямо вгору. Тоді ми виконуємо ту саму логіку, що і для стовпців, але називаємо результати "рядками" цього разу навколо. Звичайно, замість того, щоб насправді повернути сітку, ми просто проходимо 1/6 коліни навколо неї.


@PeterTaylor Хороший улов, дякую!
гастропнер

1

Python 3, 150 байт

def h(a,b):
 L=[];i=1
 while len(L)<a+b:r=sum((i*[1j**(k/3)]for k in range(4,16,2)),[]);r[0]+=1;L+=r;i+=1
 return.9<abs(sum(L[min(a,b):max(a,b)]))<1.1

Моє рішення, в основному, відповідає тій же думці, що і Луїс Мендо. Якщо написано більш читабельним, код досить зрозумілий:

def h(a,b):
    L=[]
    i=1
    while len(L)<a+b:
        l=sum((i*[1j**(k/3)]for k in range(4,16,2)),[])
        l[0]+=1
        L+=l
        i+=1
return .9<abs(sum(L[min(a,b):max(a,b)]))<1.1
  1. Функція hвиконує наступні дії:
  2. Список L міститиме (складні) позиції кожного числа.
  3. i номер дзвінка.
  4. У циклі while, до кожної ітерації додається нове кільце. Замість того, щоб з'ясувати, скільки кілець нам потрібно, ми просто продовжуємо складати список, поки він не буде достатньо довгим, щоб містити + b, тоді, безумовно, достатньо довго, щоб містити будь-яке з них.
  5. 'ring-list' l- це об'єднання 6 списків len (i) разів більше крокового вектора, де крок-вектор становить 1j ** (2/3) до деякої потужності. Діапазон починається не від 0, а від 4, що викликає обертання всієї сітки. Це дозволяє мені робити:
  6. l[0]+=1 у рядку 6, який є кроком від одного кільця до іншого.
  7. L+=l поєднує повний список і список дзвінків.
  8. Список L містить лише крокові вектори, які все ще повинні бути підсумовані (інтегровані), щоб отримати позицію. Акуратною особливістю тут є те, що ми можемо просто підсумувати фрагмент від найменшого до найвищого, щоб отримати їх відстань! Через помилки округлення результат не буде рівним 1, отже .9 <... <1.1. Цікаво, що нульовий випадок h(0,0)або h (0,1) беруться неявно, тому що сума порожнього списку дорівнює нулю. Якби я міг бути впевнений, що a<b, тобто аргументи будуть набирати все більший порядок, я міг би відтерти ще 14 байт замінивши L[min(a,b):max(a,b)]на L[a:b], але на жаль!

PS: Я не знав, що це був такий старий виклик, він з’явився на вершині кілька днів тому, і з цього моменту продовжував нудити :)


Це чудова відповідь! Не хвилюйтесь з приводу пізньої відповіді, у нас насправді немає проблем з цим тут, на PPCG.
Rɪᴋᴇʀ

0

Mathematica, 111 105 104 байт

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&

Пояснення:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&визначає функцію, rяка приймає введення #та обчислює відстань (у кількості осередків) до комірки 0. Це робиться, використовуючи шаблон в останніх осередках кожної відстані / кільця: 0 = 3 (0 ^ 2 + 0), 6 = 3 (1 ^ 2 + 1), 18 = 3 (2 ^ 2 + 2), 36 = 3 (3 ^ 2 + 3), ... та інвертування формули для цього шаблону. Зауважте, що для комірки 0 вона фактично займає підлогу (1/2) + i * (sqrt (3) / 6), яке вона обчислює для компонентів, щоб отримати 0 + 0 * i = 0.

З rвизначеним, r@#є кільце для комірки #(всередині визначення іншої функції). #+3r@#-3(r@#)^2&в коді не відображається точно, але він бере число комірки і віднімає найбільшу кількість комірки в наступному внутрішньому кільці, щоб вона дала відповідь на питання "яка комірка поточного кільця це?" Наприклад, комірка 9 є третьою коміркою кільця 2, тому r[9]виводиться 2 і #+3r@#-3(r@#)^2&[9]виводиться 3.

Що ми можемо зробити з функцією, наведеною вище, це використати її для пошуку полярного кута , кута проти годинникової стрілки від променя "комірка 0, клітинка 17, клітина 58" до комірки, про яку йдеться. Остання комірка кожного кільця завжди знаходиться під кутом Pi / 6, і ми обходимо кільце з кроком Pi / (3 * кільце_ число). Отже, теоретично нам потрібно обчислити щось на зразок Pi / 6 + (котрий_cell_of_the_current_ring) * Pi / (3 * ring_number). Однак обертання зображення нічого не впливає, тому ми можемо відкинути частину Pi / 6 (щоб зберегти 6 байт). Поєднуючи це з попередньою формулою та спрощуючи, ми отримуємоPi(#/(3r@#)+1-r@#)&

На жаль, це не визначено для клітинки 0, оскільки її номер дзвінка дорівнює 0, тому нам потрібно обійти це питання. Природним рішенням було б щось подібне t=If[#==0,0,Pi(#/(3r@#)+1-r@#)]&. Але оскільки нас не хвилює кут для комірки 0 і тому r@#, що повторюється, ми можемо зберегти тут байтt=Limit[Pi(#/(3x)+1-x),x->r@#]&

Тепер, коли у нас є номер кільця і ​​кут, ми можемо знайти положення комірки (центру), щоб ми могли перевірити наявність суміжності. Виявлення фактичної позиції дратує, оскільки кільця є шестикутними, але ми можемо просто зробити вигляд, що кільця є ідеальними колами, так що ми розглянемо номер кільця як відстань до центру комірки 0. Це не буде проблемою, оскільки наближення досить закрити. Використовуючи полярну форму комплексного числа , ми можемо зобразити це приблизне положення у складній площині простою функцією:p = r@#*Exp[I*t@#] &;

Відстань між двома комплексними числами на складній площині задається абсолютним значенням їх різниці, і тоді ми можемо округлити результат, щоб переконатися в будь-яких помилках з наближення, і перевірити, чи дорівнює це 1. Функція, яка нарешті чи ця робота не має імені, але є Round@Abs[p@#-p@#2]==1&.


Ви можете спробувати його в Інтернеті у пісочниці Wolfram Cloud , вставивши такий код, як наступний і натиснути Gear -> "Оцінити клітинку" або натиснувши Shift + Enter або numpad Enter:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&[24,45]

Або для всіх тестових випадків:

r=Floor[(1+Sqrt[(4#-1)/3])/2]&;t=Limit[Pi(#/(3x)+1-x),x->r@#]&;p=r@#*Exp[I*t@#]&;Round@Abs[p@#-p@#2]==1&//MapThread[#,Transpose[{{0,1},{7,18},{8,22},{24,45},{40,64},{64,65},{6,57},{29,90},{21,38},{38,60},{40,63},{41,39},{40,40}}]]&
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.