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


10

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

Існує nxnxn кубічна решітка, наприклад, якщо n = 2 це складається з (0,0,0), (0,1,0), (1,0,0), (1,1,0), (0, 1,1), (0,0,1), (1,0,1), (1,1,1).

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

solve(set_of_points) {
     if set_of_points.size = m, finish

     do some useful computation here

     for each point in lattice not in set_of_points:
         solve(set_of_points + new_point);
}

Потім це можна назвати, починаючи з порожнього set_of_points.

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

Наприклад, візьміть куб 2х2х2 і припустимо, що ми хочемо, щоб усі множини по 1 балу. За базовим алгоритмом, наведеним вище, існує 8 різних наборів з 1 балом.

Однак, використовуючи симетрію куба, ми можемо зменшити це до 1 унікального набору в 1 бал, оскільки всі початкові 8 є еквівалентними за симетрією куба (у цьому випадку вони всі "кути").

Якщо куб 2х2х2 і m = 2, в базовому алгоритмі є 28 множин, але це зменшується до симетрики лише на 3 (наприклад, {(0,0,0), (1,0,0)}, {(0 , 0,0), (1,1,0)}, {(0,0,0), (1,1,1)})

Очевидно, що обчислення на 3 наборах точок набагато краще, ніж 28, тому моє питання полягає в тому, як я можу не сформувати набори точок, які симетрично еквівалентні вже створеному набору? Або якщо це неможливо, як я можу принаймні трохи зменшити кількість наборів.

(Примітка - якщо m = 1, це відносно просто - просто виберіть точки, ближчі до (0,0,0), ніж будь-які інші вершини, з невеликим розтушовуванням меж. Це для m> 1 це отримує бути справжньою проблемою)


1
До симетричного еквівалента ви включаєте, які операції: Дзеркальні площини через центр? Точка-Інверсія через центр? Всі три 4-осі обертання через центр?
BmyGuest

Будь-яка ізометрія зробить трюк
rbennett485

Якщо ви все ще поруч, чи дозволено повторення в m-множині балів? Наприклад, при m = 3 вважається {(0,0,0), (1,1,1), (0,0,0)} одним дійсним виділенням?
чорніє

@blackpen немає, це повинно бути 3 унікальних точки
rbennett485

Відповіді:


1

Основна концепція:

(1) Ми можемо розглядати точку (0,0,0) просто як 000. Кожна точка в решітці тепер належить до простої послідовності. Перша точка - 000, потім 001, потім 010 011 100 101 110 і 111. Це порядок, який ви спробуєте додати їх до набору точок.

(2) Аналогічно, набір {(0,0,0), (0,0,1), (0,1,0)} можна просто розглядати як 000001010, а набір {(0,0,0) , (0,1,0), (0,0,1)} просто можна розглядати як 000010001. Два різних набори не можуть мати однакову послідовність, і ви можете легко переглядати 000001010 як чисельні, так і в алфавітному порядку менше 000010001. Назвемо це заданим значенням. Кожен можливий набір з N точок тепер має встановлене значення, і всі можливі набори N точок тепер потрапляють у простий добре упорядкований список.

(3) Кожна ізоморфна група точкових множин має рівно одного члена, який матиме найменше встановлене значення. Це єдині, де ми насправді робимо «корисні обчислення».

(4) Ось частина, яка потребує значної роботи. Перш ніж запустити рішення (set_of_points + new_point), ви хочете побачити, чи який-небудь ізоморфізм знизить встановлене значення для set_of_points + new_point. Якщо будь-який ізоморфізм знизив би встановлене значення, то це НЕ найменший член ізоморфного набору. Ми пропускаємо будь-яку роботу з цієї нової точки. Ми також пропускаємо всю рекурсивну роботу, яку ми виконали б у цьому вирішенні (set_of_points, kandidat_point).

solve(set_of_points,new_point) {
 set_of_points = set_of_points + new_point
 do some useful computation here
 if set_of_points.size = m, compute how many isomophisms exist, apply that multiple, finish
 for(candidate_point = new_point+1 to last_point) { /skips point-permutations for free!/
  if ISOMORPH_TESTS_CANNOT_LOWER_VALUE_OF(set_of_points+candidate_point) {
   solve(set_of_points,candidate_point);
  }
 }
}

1

взявши позначення відповіді вище.

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

рішення:

(1) створити хеш для всіх наборів перестановки з прапорцем = 0 на кожному. наприклад для n = 2, m = 2 000,001 = 0 000,010 = 0 000,011 = 0 ect '...

(2) починати з набору init, наприклад i = 000,001

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

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

пояснення: будь-яке число 1-6 може бути перед вами, і кожне число можна обертати 4 рази, тому 6 * 4 = 24

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

(5) оновити i до наступного набору, наприклад i = 000,010

(6) якщо множина i у хеші вже позначена, перейдіть до (5), інакше перейдіть до (3)

ми робимо, коли весь хеш позначений як 1.


Мені дуже подобається такий підхід, але він не був би таким корисним для початкової проблеми (не для того, щоб я сказав вам, що це було!). Причина полягає в тому, що для цього все-таки потрібно генерувати кожен набір очок, і робота, яку я повинен був виконати з кожним набором, була дуже маленькою, так що це, ймовірно, додасть стільки накладних витрат, скільки заощадило. Для додатків із великою кількістю обчислень для кожного набору це було б зручно, хоча
rbennett485

1

Примітка. Я думаю лише про дзеркальну симетрію, а не обертальну симетрію.

Припустимо, у нас (гіпер) куб d розмірів, кожна n одиниця завдовжки (куб Рубіка буде d = 3, n = 3 ).

Наївний алгоритм генерував би n ^ d комбінації точок і перевіряв би кожну суперечність симетрії з усіма іншими.

Якщо ми представляємо комбінацію точок як бітовий вектор n ^ d бітів, ми можемо використовувати карту (бітовий вектор -> булева), щоб позначити всі симетрії бітового вектора справжніми . Потім ми можемо пропустити комбінацію, якщо вона вже позначена на карті.

Цей підхід дуже неефективний у просторі: він займає карту з 2 ^ (n ^ d) записами, тобто растрову карту з цією кількістю біт. (Для куба Рубіка це було б 2 ^ 27 = 128 Мбіт = 16 Мбайт.)

Ми можемо згадати лише канонічні уявлення, тобто такі бітові вектори, які мають найменше ціле значення, якщо вони представлені як n ^ d -бітне підписане слово. Коли ми генеруємо нову перестановку точок, ми генеруємо всі її симетрії і лише перевіряємо, чи бачили ми симетрію з найменшим числовим значенням. Це дозволить нам зберігати карту з лише 2 ^ n бітами (всього 1 байт для куба Рубіка), оскільки у нас є 2 ^ d симетрії. Це змушує нас генерувати ці 2 ^ d симетрії на кожному кроці, проте ми витрачаємо час O (2 ^ (d ^ n + d)) = O (2 ^ (d ^ n) * 2 ^ d) . Ще бідний.

Ми можемо застосувати ідею з попереднього абзацу до одновимірного випадку. Щоб генерувати всі комбінації у векторі довжини d , ми можемо просто збільшити двійкове число d біт у довжину, починаючи з усіх 0s. Давайте розділимо наш вектор на два d / 2- довгі сегменти, наприклад лівий і правий. Ми можемо помітити, що для кожного 1біта в лівому сегменті нам потрібно бачити лише комбінації, які мають 1біт у симетричному положенні правого розділу. В іншому випадку ми вже створили б симетричну комбінацію раніше, коли позиції бітів були помінені, а позиції 0перед - 1. Таким чином, для кожного положення бітів у правій половині (r) та симетричному положенні в лівій половині(л) нам потрібно створити лише 3 комбінації: (l = 0, r = 0); (l = 1, r = 1); (l = 1, r = 0) . Таким чином, нам потрібно лише генерувати 2 ^ (d / 2) перестановки вектора довжиною d , отримуючи 3 комбінації для кожної перестановки.

Куб d розмірів може бути побудований з n ^ (d-1) векторів. Наголошений трюк дає нам вектори дешевше, ніж наївний підхід. Для генерації куба нам потрібен час O (n ^ (d-1) * 2 ^ (d / 2)) .

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

Тепер , якщо ми подивимося через цей аспект, ми можемо використовувати той же трюк.

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

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

Хоча це не повний алгоритм, я сподіваюся, що це допомагає.

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