CJam, 189 187 байт
Це пояснити буде важко ... Складність часу гарантована O(scary)
.
qi:N_3>{,aN*]N({{:L;N,X)-e!{X)_@+L@@t}%{X2+<z{_fe=:(:+}%:+!},}%:+}fX{:G;N3m*{_~{G@==}:F~F\1m>~F\F=}%:*},:L,({LX=LX)>1$f{\_@a\a+Ne!\f{\:M;~{M\f=z}2*\Mff==}:|{;}|}\a+}fX]:~$e`{0=1=},,}{!!}?
Якщо ви досить сміливі, спробуйте це в Інтернеті . На моєму шаленому ноутбуці я можу отримати до 6 з перекладачем Java або 5 в онлайн-перекладачі.
Пояснення
У мене немає великого математичного походження (щойно закінчила середню школу, наступний тиждень розпочала CS в університеті). Тож потерпіть зі мною, якщо я помиляюся, констатую очевидність або роблю речі жахливо неефективними способами.
Мій підхід є грубою силою, хоча я намагався зробити його трохи розумнішим. Основні кроки:
- Створити всі можливі операнди ∗ для групи порядку n (тобто перерахувати всі групи порядку n );
- Утворіть усі можливі біекції φ між двома групами порядку n ;
- Використовуючи результати кроків 1 та 2, визначте всі ізоморфізми між двома групами порядку n ;
- Використовуючи результат з кроку 3, підрахуйте кількість груп до ізоморфізму.
Перш ніж подивитися на те, як робиться кожен крок, давайте вийдемо з котрийсь тривіальний код:
qi:N_ e# Get input as integer, store in N, make a copy
3>{...} ? e# If N > 3, do... (see below)
{!!} e# Else, push !!N (0 if N=0, 1 otherwise)
Наведений нижче алгоритм не працює правильно з n <4 , випадки від 0 до 3 обробляються з подвійним запереченням.
Відтепер елементи групи будуть записуватися як {1, a, b, c, ...} , де 1 - ідентичний елемент. У реалізації CJam відповідними елементами є {0, 1, 2, 3, ...} , де 0 - елемент ідентичності.
Почнемо з кроку 1. Запис усіх можливих операторів для групи порядку n еквівалентно генеруванню всіх дійсних n × n таблиць Кейлі . Перший рядок і стовпець тривіальні: вони обидва {1, a, b, c, ...} (зліва направо, вгору вниз).
e# N is on the stack (duplicated before the if)
,a e# Generate first row [0 1 2 3 ...] and wrap it in a list
N* e# Repeat row N times (placeholders for next rows)
] e# Wrap everything in a list
e# First column will be taken care of later
Знаючи, що таблиця Кейлі - це також зменшений латинський квадрат (завдяки властивості скасування), дозволяє генерувати можливі таблиці по рядках. Починаючи з другого рядка (індекс 1), ми генеруємо всі унікальні перестановки для цього рядка, зберігаючи перший стовпець зафіксованим за значенням індексу.
N({ }fX e# For X in [0 ... N-2]:
{ }% e# For each table in the list:
:L; e# Assign the table to L and pop it off the stack
N, e# Push [0 ... N-1]
X) e# Push X+1
- e# Remove X+1 from [0 ... N-1]
e! e# Generate all the unique permutations of this list
{ }% e# For each permutation:
X)_ e# Push two copies of X+1
@+ e# Prepend X+1 to the permutation
L@@t e# Store the permutation at index X+1 in L
{...}, e# Filter permutations (see below)
:+ e# Concatenate the generated tables to the table list
Звичайно, не всі ці перестановки є дійсними: кожен рядок і стовпець повинні містити всі елементи рівно один раз. З цією метою використовується блок фільтру (триє значення зберігає перестановку, помилкове видаляє її):
X2+ e# Push X+2
< e# Slice the permutations to the first X+2 rows
z e# Transpose rows and columns
{ }% e# For each column:
_fe= e# Count occurences of each element
:( e# Subtract 1 from counts
:+ e# Sum counts together
:+ e# Sum counts from all columns together
! e# Negate count sum:
e# if the sum is 0 (no duplicates) the permutation is kept
e# if the sum is not zero the permutation is filtered away
Зауважте, що я фільтрую всередині циклу генерації: це робить код трохи довшим (порівняно з чіткою генерацією та фільтруванням), але значно покращує продуктивність. Кількість перестановок набору розміру n дорівнює n!, тому коротше рішення потребує багато пам'яті та часу.
Список дійсних таблиць Кейлі - це чудовий крок до перерахування операторів, але, будучи 2D структурою, він не може перевірити асоціативність, яка є властивістю 3D. Отже, наступним кроком є фільтрація неасоціативних функцій.
{ }, e# For each table, keep table if result is true:
:G; e# Store table in G, pop it off the stack
N3m* e# Generate triples [0 ... N-1]^3
{ }% e# For each triple [a b c]:
_~ e# Make a copy, unwrap top one
{ }:F e# Define function F(x,y):
G@== e# x∗y (using table G)
~F e# Push a∗(b∗c)
\1m> e# Rotate triple right by 1
~ e# Unwrap rotated triple
F\F e# Push (a∗b)∗c
= e# Push 1 if a∗(b∗c) == (a∗b)∗c (associative), 0 otherwise
:* e# Multiply all the results together
e# 1 (true) only if F was associative for every [a b c]
Фу! Багато роботи, але зараз ми перерахували всі групи порядку n (а краще, операції над цим - але набір фіксований, тож це одне і те ж). Наступний крок: знайти ізоморфізми. Ізоморфізм - бієкція між двома з таких груп, що φ (x ∗ y) = φ (x) ∗ φ (y) . Генерація цих біекцій у CJam є тривіальною: Ne!
зроби це. Як ми можемо їх перевірити? Моє рішення починається з двох копій таблиці Келей для ї * у . На одній копії φ застосовується до всіх елементів, не торкаючись порядку рядків чи стовпців. Це створює таблицю для φ (x ∗ y) . На іншому елементи залишаються такими, якими вони є, але рядки та стовпці відображаються через φ . Тобто рядок / стовпецьxстає рядком / стовпцем φ (x) . Це створює таблицю для φ (x) ∗ φ (y) . Тепер, коли у нас є дві таблиці, ми просто повинні їх порівняти: якщо вони однакові, ми виявили ізоморфізм.
Звичайно, нам також потрібно сформувати пари груп для перевірки ізоморфізму на. Нам потрібні всі 2-комбінації груп. Схоже, у CJam немає оператора для комбінацій. Ми можемо генерувати їх, приймаючи кожну групу і комбінуючи її лише з елементами, що слідують за нею у списку. Веселий факт: кількість 2-комбінацій - n × (n - 1) / 2 , що також є сумою перших n - 1 натуральних чисел. Такі числа називаються трикутними числами: спробуйте алгоритм на папері, один рядок на фіксований елемент, і ви побачите чому.
:L e# List of groups is on stack, store in L
,( e# Push len(L)-1
{ }fX e# For X in [0 ... len(L)-2]:
LX= e# Push the group L[X]
LX)> e# Push a slice of L excluding the first X+1 elements
1$ e# Push a copy of L[X]
f{...} e# Pass each [L[X] Y] combination to ... (see below)
e# The block will give back a list of Y for isomorphic groups
\a+ e# Append L[X] to the isomorphic groups
] e# Wrap everything in a list
Код вище фіксує перший елемент пари L [X] та поєднує його з іншими групами (назвемо кожну з цих Y ). Він передає пару до тестового блоку ізоморфізму, який я покажу через мить. Блок повертає список значень Y , для яких L [X] ізоморфна Y . Потім L [X] додається до цього списку. Перш ніж зрозуміти, чому списки налаштовані таким чином, давайте розглянемо тест ізоморфізму:
\_@ e# Push a copy of Y
a\a+ e# L[X] Y -> [L[X] Y]
Ne! e# Generate all bijective mappings
\f{ } e# For each bijection ([L[X] Y] extra parameter):
\:M; e# Store the mapping in M, pop it off the stack
~ e# [L[X] Y] -> L[X] Y
{ }2* e# Repeat two times (on Y):
M\f= e# Map rows (or transposed columns)
z e# Transpose rows and columns
e# This generates φ(x) ∗ φ(y)
\Mff= e# Map elements of L[X], generates φ(x ∗ y)
= e# Push 1 if the tables are equal, 0 otherwise
:| e# Push 1 if at least a mapping was isomorphic, 0 otherwise
{;}| e# If no mapping was isomorphic, pop the copy of Y off the stack
Чудово, зараз у нас є список наборів типу [{L [0], Y1, Y2, ...}, {L [1], Y1, ...}, ...] . Ідея тут полягає в тому, що за перехідною властивістю, якщо будь-які два набори мають принаймні один елемент спільного, то всі групи в двох множинах є ізоморфними. Їх можна об'єднати в один набір. Оскільки L [X] ніколи не з'явиться в комбінаціях, породжених L [X + ...] , після агрегації кожного набору ізоморфізмів буде один унікальний елемент. Отже, щоб отримати кількість ізоморфізмів, достатньо порахувати, скільки груп з’являється рівно один раз у всіх наборах ізоморфних груп. Для цього я розгортаю набори так, щоб вони виглядали як [L [0], Y1, Y2, ..., L [1], Y1, ...] , сортуйте список для створення кластерів тієї самої групи та нарешті RLE-кодуйте його.
:~ e# Unwrap sets of isomorphic groups
$ e# Sort list
e` e# RLE-encode list
{ }, e# Filter RLE elements:
0= e# Get number of occurrences
1= e# Keep element if occurrences == 1
, e# Push length of filtered list
e# This is the number of groups up to isomorphism
Це все, шановні.