Як зобразити куб Рубіка в структурі даних


58

Якщо я намагаюся імітувати кубик Рубіка , як би ви створили структуру даних для зберігання стану куба в пам'яті, з кількістю X плиток на стороні?

Що слід врахувати:

  • куб може бути будь-якого розміру
  • це кубик Рубіка, тому шари можна обертати

2
Домашнє завдання? Або проблема з реальним світом ...
sdg

4
Можливо, вас зацікавить вихідний код вирішувача Кубів Рубіка .
mouviciel

22
Я майже впевнений, що кількість кубів має бути 6
Саймон Бергот

3
Мені було б цікаво побачити модель куба Рубіка сплющеною, щоб я міг бачити всі сторони одночасно. Хм, я зараз спокусився написати це. Це може бути форма T або навіть нескінченно викладений плиткою, якщо це можливо (я не замислювався над останнім).
Лі Ковальковський

6
Мені спокушати цитувати Еріка Еванса: "Моделі не є ні правильними, ні неправильними. Вони просто більш-менш корисні" (цитата, мабуть, не на 100% правильна, як це цитується з пам'яті)
Піт

Відповіді:


37

Що не так із простим старим масивом розміру [6X][X]? Вам не потрібно знати про внутрішні міні-кубики, тому що ви їх не бачите; вони не входять до складу куба. Сховайте два потворних методу за приємним і простим у використанні інтерфейсом, тестуйте його на смерть і вуаля, ви готові!


31
навіть у справжнього куба Рубіка немає внутрішніх міні-кубів
jk.

8
Це спрацює, але ваш алгоритм, ймовірно, буде надзвичайно складним для розміщення такої простої структури даних.
maple_shaft

6
As long as you know how the six surfaces are "threaded" together Саме це дасть вам більш міцна структура даних. Я думаю, ми сперечаємось про те саме. Сторони масиву, а сторона - це масив блоків, проте є багато цікавих властивостей щодо сторін і блоків, які допомагають з'ясувати, що "нанизування" цього терміна не дуже подобається, тому що його можна переплутати з багатопотоковою ниткою; )
maple_shaft

7
@maple_shaft "Це саме те, що надасть вам більш міцна структура даних." Я не знаю про це: структура даних з більшою "структурою" до неї обов'язково спричинить додаткові випадкові складності, пов'язані з налаштуванням, підтримкою та доступом до окремих частин цієї структури. Важко сказати, що було б складніше - реалізація негарних зрушень на простому масиві з деякими кутовими справами плюс "вільна їзда" на доступ до окремих комірок, або структура з дещо менш складними зрушеннями та дещо складнішими зчитуваннями.
dasblinkenlight

16
Як хтось, хто насправді написав програми для маніпулювання кубом Рубіка, я застосував простий підхід, використовуючи шість двовимірних масивів, по одному на обличчя. Це правда, що вам доведеться здійснити деякі фундаментальні операції на кубі, які трохи дратують, але після цього ви можете забути про представлення. Це ніколи для мене не було проблемою. Я часто замислювався, як працюватимуть інші уявлення з точки зору продуктивності, але ніколи не відчував себе обтяженим з точки зору кодування.
PeterAllenWebb

39

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

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

На кубі є 3 різних типи блоків:

  1. Кутовий блок - він має три кольорові грані та три сусідні частини, з якими він зможе ділитися стороною в будь-який час.

  2. Блоковий край - має два кольорові грані та має 4 суміжні частини, з якими він зможе ділитися стороною у будь-який час. У 3х3 блоках завжди є 2 центральних частини та 2 кутових частини.

  3. Центральний блок - у кубі 3х3 цей шматок не є рухомим, проте його можна обертати. Він завжди матиме 4 сусідні крайні блоки. У більших кубиках є декілька центральних блоків, які можуть ділитися з іншим центральним блоком або краєм. Центральні блоки ніколи не примикають до кутового блоку.

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

Кожне кубичне обличчя було б представлене як унікальне обличчя.

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

EDIT: Важлива примітка, ці списки потрібно, звичайно, замовити, але я забув це згадати. Наприклад, якщо я перевертаю праву сторону, то лівий кут правого бічного блоку переміщується в правий кут правого боку і повертається за годинниковою стрілкою.


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

@Mel Це можна зробити будь-яким способом, але після розмови з dasblinkenlight я насправді думаю, що його підхід був би менш складним. Я хочу, щоб його відповідь мала більше голосів, ніж моя. Мені не все добре з алгоритмами і найефективнішим, я просто дуже люблю кубики рубіків і збираю їх (понад 40 різних видів з усього світу).
maple_shaft

Хоча відповідь dasblinknenlight є найпростішим рішенням, я нагороджую вас винагородою, тому що мені подобається, що ваша відповідь включає частину логіки, яка потрібна для такої структури даних, і різні атрибути блоку
Рейчел

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

@Ziv True, однак питання було задано про структуру даних, а не про алгоритми.
maple_shaft

13

Коли я думаю про цю проблему, я думаю про статичний куб з кольорами, що рухаються по ньому за відомими візерунками. Тому....

Об'єкт Куб містить 6 бічних об'єктів, які залишаються фіксованими з індексом 0-5. Кожна сторона містить 9 об'єктів позиції, які залишаються фіксованими з індексом 0-8. Кожна позиція містить колір.

Для простоти обробляйте кожну дію з кроком на чверть обороту. Існує 3 осі обертання, кожна в 2 можливих напрямках для загальної кількості 6 можливих дій на кубі. З цією інформацією стає досить простим завданням відобразити 6 можливих дій на кубі.

Таким чином, зелений колір в стороні 6, позиції 3, може переходити в положення 1 в сторону 1 або в сторону 2 в позицію 7, в залежності від вжитих дій. Я не вивчив цього достатньо, щоб знайти будь-які математичні переклади, але, ймовірно, з'являться шаблони, якими можна скористатися в коді.

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

Для цього ніколи не починайте з випадкового стану куба. Натомість почніть з розв’язаного стану та виконайте n дій програмно, щоб перевести куб у випадковий початковий стан. Оскільки ви вживали лише юридичних дій, щоб дійти до поточного стану, куб повинен бути вирішеним.


1
Класична порада "ти не хочеш починати звідси"!
Ergwun

8

Я знайшов систему координат xyz як простий спосіб вирішення куба Рубіка, а матриці обертання - простий, загальний спосіб здійснення обертань.

Я створив клас Piece, що містить вектор позиції (x, y, z). Шматок можна обертати, застосувавши матрицю обертання до його положення (множення матричного вектора). Шматок також відстежує кольори в кортежі (cx, cy, cz), надаючи кольори, звернені вздовж кожної осі. Невелика кількість логіки забезпечує ці кольори належним чином оновлені під час обертання: обертання на 90 градусів у площині XY означає, що ми будемо міняти значеннями cxта cy.

Оскільки вся логіка обертання інкапсульована у класі Piece, Куб може зберігати не упорядкований список Шматок, а обертання можна робити в загальному вигляді. Щоб здійснити обертання лівої грані, виділіть усі шматки з x-координатою -1 та застосуйте відповідну матрицю обертання до кожного Шматка. Щоб зробити обертання всього куба, застосуйте однакову матрицю обертання до кожного шматка.

Ця реалізація проста і має кілька приємностей:

  1. Положення об'єкта Piece зміниться, але його кольори - ні. Це означає, що ви можете попросити червоно-зелений шматок, повісити його на об'єкт, зробити кілька обертів і перевірити той самий об’єкт, щоб побачити, де опинився червоно-зелений шматок.
  2. Кожен тип Шматка (край, центр, кут) має унікальний узор координат. Для куба 3x3 кутовий шматок не має нулів у своєму векторі положення ( (-1, 1, 1)), край має рівно один нуль ( (1, 0, -1)), а центральний шматок - два нулі ( (-1, 0, 0)).
  3. Матриці обертання, які працюють для куба 3x3, будуть працювати для куба NxN.

Недоліки:

  1. Множення матричного вектора відбувається повільніше, ніж розміщення значень у масивах.
  2. Лінійні пошуки Час за позицією. Вам потрібно буде зберігати шматки у зовнішній структурі даних та оновлювати їх під час обертання для постійного пошуку за позицією. Це перетворює частину елегантності використання матриць обертання та пропускає логіку обертання у ваш клас Куб. Якби я впроваджував будь-який алго алгоритм вирішення на основі пошуку, я б застосував іншу реалізацію.
  3. Аналіз шаблону (під час вирішення) не такий приємний, як міг би бути. Piece не знає своїх сусідніх Pieces, і аналіз був би повільним через вищезазначені проблеми з ефективністю.

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

5

ви можете використовувати простий масив (кожен елемент має відображення від 1 до 1 на квадрат на обличчі) та імітувати кожне обертання з певною перестановкою

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

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


1
the most straightforward way of know whether a cube is solvable is to solve it. Ну, використовуючи запропоновану вами модель, я думаю, це правда. Але якщо ви використовуєте модель, ближчу до обертання @ maple_shaft та доріжки, ви можете швидко перевірити, чи вирішується куб 3x3x3, перевіривши суму обертів ребер mod 2 0 і кутових обертів mod 3 дорівнює 0. Потім перевіряйте парність перестановки на підраховуючи крайові свопи та кутові заміни (потрібні, щоб повернутися до вирішення), їх сума mod 2 повинна бути 0 (рівномірний паритет). Це необхідні та достатні тести, щоб довести, що куб вирішується.
jimhark

3

Перша умова, щоб це було вирішено, полягало б у тому, щоб кожен шматок був присутній і щоб кольори на кожному шматочку могли бути використані для складання «совісного» куба. Це відносно тривіальна умова, істинність якої можна визначити простим контрольним списком. Колірна гамма на "стандартному" кубі визначена , але навіть якщо ти не маєш справу зі стандартним кубом, їх всього 6! можливі комбінації розв’язаних граней.

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

Щодо структури даних ... це майже не має значення. Складна частина полягає в правильному перетворенні і можливості представити стан куба таким чином, що дозволяє акуратно працювати з доступними алгоритмами в літературі. Як вказує Кленовий вал, є три типи штук. Література про вирішення кубика Рубіка завжди стосується творів за їх типом. Трансформації також представлені загальними способами (дивіться позначення Singmaster ). Крім того, всі рішення, які я бачив, завжди посилаються на один фрагмент як на орієнтир (зазвичай ставлять білу центральну частину внизу).


1
Для точки 2 замість того, щоб починати з випадкового куба і перевіряти, чи він розв'язуваний. Я б почав з вирішеного куба і виконав би n випадкових дій на кубі, щоб перевести його у випадковий стан.
Меттью Вайнс

Так, абсолютно, це найпростіший спосіб створити конфігурацію, яку фізично можливо вирішити. Починати з довільної конфігурації та визначати, чи можна її вирішити, безумовно, є окремою, але пов'язаною з цим проблемою.
Анджело

2
Ви здогадуєтесь, що можуть бути "химерні прийоми" визначення того, чи може бути куб, який можна вирішити; насправді є. Якщо ви розбираєте кубик, але тримаєте наклейки, а потім збираєте кубик, вам не обов’язково виходить вирішуваний куб; насправді шанси становлять один-дванадцять проти того, що у вас є вирішуваний куб. Ви можете визначити, чи перебуваєте ви в розв’язуваному стані, за допомогою аналізу парності країв і кутів; вам насправді не потрібно намагатися вирішити куб.
Ерік Ліпперт

1
Ось короткий огляд трьох видів властивостей парності куба, які необхідно зберегти для вирішення куба. ryanheise.com/cube/cube_laws.html .
Ерік Ліпперт

1
Я відповідав на це питання на сайті матч stackexchange і отримав дуже хороші відповіді: math.stackexchange.com/questions/127577 / ...
Мел

3

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

Незалежно від конкретного представлення, зауважте, що лінзи - це дуже чудовий інструмент для "збільшення" на різних частинах куба. Наприклад, подивіться на функцію cycleLeftв цьому Haskell коду . Це загальна функція, яка циклічно перетворює будь-який список довжиною 4. Код для виконання руху L виглядає приблизно так:

moveL :: Aut (RubiksCube a)
moveL =
    cong cube $ cong leftCols cycleLeft
              . cong leftSide rotateSideCW

Таким чином cycleLeftдіє на точку зору, яку дає leftCols . Аналогічно, rotateSideCWщо є загальною функцією, яка бере сторону до поворотної її версії, діє на вигляд, поданий автором leftSide. Інші кроки можна реалізувати аналогічно.

Мета цієї бібліотеки Haskell - створити гарні фотографії. Я думаю, що це вдалося: Дія бібліотеки діаграм-рубіків-кубів


2

Здається, ви задаєте два окремих питання.

  1. Як зобразити куб із X кількістю сторін?

Якщо ви збираєтеся імітувати куб Рубіка в реальному світі, то всі кубики Рубіка мають 6 сторін. Я думаю, що ви маєте на увазі "кількість X плиток на розмір на стороні". Кожна сторона оригінального куба Рубіка - 3х3. Інші розміри включають 4х4 (кубик професора), 5х5 та 6х6.

Я б представляв дані з 6 сторін, використовуючи "стандартний" куб вирішення позначень:

  • ФРОНТ: обличчя, спрямоване на вирішувач
  • НАЗАД
  • ПРАВО
  • ЛІВО
  • Вгору
  • Вниз

Кожна сторона - це двовимірний масив X на X.


Ви можете придбати куб розміром 17х17 ! У нього є деякі механічні компроміси, але це ізоморфно реальній справі.
RBerteig

1

Мені подобається ідея @maple_shaft представляти різні фігури (міні-кубики) по-різному: центральні, крайні та кутові частини містять відповідно 1, 2 або 3 кольори.

Я б представляв відносини між ними як (двоспрямований) графік, причому краї з'єднують суміжні частини. Кожен шматок мав би масив прорізів для країв (з'єднань): 4 прорізи в центральних шматочках, 4 прорізи в краях, 3 прорізи в кутових шматочках. Альтернативно, центральні шматки можуть мати 4 з'єднання з крайовими деталями та 4 для кутових деталей окремо, та / або крайові частини можуть мати 2 з'єднання з центральними деталями та 2 на кутові частини окремо.

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

  • Пошук шматочків, що належать до краю, є тривіальним.
  • Пошук фрагментів, що належать до обличчя, є тривіальним.
  • Пошук облич, які в заданому напрямку до даного обличчя чи протилежного обличчя, проходить 2 або 3 чітко визначених посилання.
  • Щоб обертати обличчя, оновіть з'єднання всіх частин, підключених до центральної частини обличчя.

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


1

Як щодо вузлів та покажчиків?

Якщо припустимо, що завжди існує 6 граней, і що 1 вузол являє собою 1 квадрат на 1 грані:

r , g , b
r , g , b
r , g , b
|   |   |
r , g , b - r , g , b
r , g , b - r , g , b
r , g , b - r , g , b

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

Не забувайте, що це подвійне з'єднання, тож оновіть також щойно загострені вузли. Завжди буде висота * кількість ширини переміщених вузлів, з одним вказівником оновлюється на кожен вузол, тому має бути оновлено висоту * ширину * 2 кількість покажчиків.

Оскільки всі вузли вказують один на одного, просто об’їжджайте по колу, оновлюючи кожен вузол, коли ви заходите до нього.

Це має працювати для будь-якого розміру куба, без крайових корпусів і складної логіки. Це просто прогулянка / оновлення вказівника.


-1

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

У кубі 4 на 4 буде 12 комплектів. 6 наборів для 6 граней і 6 наборів для шести смуг, які обходять куб. У кожної грані є 16 субкубів, а в кожній смузі - 12 субкубів. Всього 56 підкубів. Кожен підкубник містить інформацію про колір та напрямок кольорів. Сам куб рубіка - це масив 4 на 4 на 4, кожен елемент має інформацію, що складається з 3-х наборів, які визначають підкуб в цьому місці.

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


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