Підрахунок груп заданого розміру


21

Групи

В абстрактній алгебрі група - кортеж (G,) , де G - множина, а - функція G×GG таким чином, що має місце наступне:

  • Для всіх x,y,z в G , (xy)z=x(yz) .

  • Існує елемент e в G такий, що для всіх x в G , xe=x .

  • Для кожного x у G існує елемент y в G такий, що xy=e .

Порядок групи визначається як число елементів G .(G,)G

Для кожного строго додатного цілого числа існує принаймні одна група порядку n . Так , наприклад, ( С п , + п ) є така група, де З п = { 0 , . . . , n - 1 } і .nn(Cn,+n)Cn={0,...,n1}x+ny=(x+y)modn

Ізоморфні групи

Нехай і визначимо по . Тоді і .G:={1,2}xy=(x×у)мод311=1=2212=2=21

Так само і0+20=0=1+210+21=1=1+20 .

Хоча елементи та операції груп та мають різні назви, групи мають однакову структуру.( C 2 , + 2 )(Г,)(С2,+2)

Дві групи та вважаються ізоморфними, якщо існує біекція така, що для всіх в .( G 2 , 2 )(Г1,1)(Г2,2)ϕ:Г1Г2ϕ(х1у)=ϕ(х)2ϕ(у)х,уГ1

Не всі групи одного порядку є ізоморфними. Наприклад, група Кляйна - це група порядку 4, яка не є ізоморфною .(С4,+4)

Завдання

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

Тестові кейси

Input   Output
0       0
1       1
2       1
3       1
4       2
5       1
6       2
7       1
8       5
9       2
10      2
11      1
12      5
13      1
14      2
15      1
16      14
17      1
18      5
19      1
20      5

(взято з OEIS A000001 )

Додаткові правила

  • Не існує обмежень щодо часу виконання або використання пам'яті.

  • Вбудовані модулі, які реалізують це завдання, такі як Mathematica FiniteGroupCount, заборонені.

  • Діють стандартні правила .


14
Звичайно, Mathematica має для цього вбудований. : /
Олексій А.

1
Цитуючи Пітера (з коментаря до пісочного повідомлення " Еволюція OEIS" ): "Якщо ви подивитесь на розділи" формули "та" програми ", наприклад, A000001 , A000003, A000019, тоді відповідь, в якій не використовуються спеціалізовані вбудовані програми, вимагатиме: багато досліджень ". (Наголос мій.);)
Мартін Ендер

12
Деякі кажуть, що вбудованої математики немає , але це все ще підлягає дослідженню. Інші міфи говорять, що Mathematica створює вбудовані, читаючи розум програмістів , але це теж ще не підтверджено.
flawr

1
@flawr недокументований monkeys_on_typewritersвбудований документ охоплює все, що не охоплено документально вбудованими документами.
Рівень Рівер Сент

Чому (1 + 1)% 3 не 2?
Cabbie407

Відповіді:


16

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 в університеті). Тож потерпіть зі мною, якщо я помиляюся, констатую очевидність або роблю речі жахливо неефективними способами.

Мій підхід є грубою силою, хоча я намагався зробити його трохи розумнішим. Основні кроки:

  1. Створити всі можливі операнди для групи порядку n (тобто перерахувати всі групи порядку n );
  2. Утворіть усі можливі біекції φ між двома групами порядку n ;
  3. Використовуючи результати кроків 1 та 2, визначте всі ізоморфізми між двома групами порядку n ;
  4. Використовуючи результат з кроку 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

Це все, шановні.


2
Це одне чорт пояснення. Приємно.
The_Basset_Hound

1
@The_Basset_Hound ... aaaand це вже закінчено;)
Andrea Biondo

Я вважаю свою власну відповідь неконкурентоспроможною, тому прийняв цю.
Денніс

4

CJam, 73 байти

0ri:Re!Rm*{:Tz0=R,=[R,Te_]m!{~ff{T==}e_}/=&},{:T,e!{:PPff{T==P#}}%$}%Q|,+

Часові складності вищевказаного коду гірші, ніж O (n! N ) .

Введення n = 4 - це вже багато для онлайн-перекладача .

За допомогою інтерпретатора Java введення n = 5 може бути можливим, якщо у вас достатньо оперативної пам’яті та терпіння.

Пошук груп

Враховуючи групу (G, ∗) порядку n , ми можемо вибрати довільну біекцію φ: G -> C n таку, що φ (e) = 0 .

φ стане ізоморфізмом (G, ∗) і (C n , ∗ '), якщо визначити ∗' по x ∗ 'y = φ (φ -1 (x) ∗ φ -1 (y)) .

Це означає, що достатньо вивчити всі оператори групи в C n таким чином, що 0 є нейтральним елементом.

Представимо груповий оператор в C n прямокутним масивом T розмірами n × n таким, що T [x] [y] = x ∗ y .

Щоб створити такий масив, ми можемо почати з підбору перестановки C n для кожного з його n рядів.

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

Ми можемо виправити е = 0 , вимагаючи , що перший стовпець з Т дорівнює С н . Зокрема, буде виконуватися друга умова (існування нейтрального елемента).

Щоб переконатися, що T відповідає оператору групи, все, що залишилося зробити, - це перевірити, чи виконується перша умова (асоціативність). Це можна зробити вичерпно, перевіривши, що T [T [x] [y]] [z] == T [x] [T [y] [z]] для всіх x, y, z в C n .

Підрахунок неізоморфних груп

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

Це може бути досягнуто шляхом ітерації над усіма бісекціями φ: C n -> C n та визначенням асоційованого масиву T defined , визначеного Tφ [x] [y] = φ -1 (T [φ (x)] [φ (y )]) .

Залишилося лише підрахувати кількість різних сімей.

Що робить код

0         e# Push 0. For input 0, the remaining code will crash, leaving
          e# this 0 on the stack.
ri:R      e# Read an integer from STDIN and save it in R.
e!        e# Push all permutations of [0 ... R-1].
Rm*       e# Push all arrays of 6 permutations of [0 ... R-1].
{         e# Filter; for each array:
  :T      e#   Save it in T.
  z0=R,=  e#   Check if the first column equals [0 ... R-1].
  [R,Te_] e#   Push [0 ... R-1] and a flattened T.
  m!{     e#   For both pairs (any order):
    ~     e#     Unwrap the pair.
    ff{   e#     For each X in the first: For each Y in the second:
      T== e#       Push T[X][Y].
    }     e#
  }/      e#
  =       e#   Check for equality, i.e., associativity.
  &       e#   Bitwise AND with the previous Boolean
},        e# Keep T iff the result was truthy.
{         e# For each kept array:
  :T      e#   Save it in T
  ,e!     e#   Push all permutations of [0 ... R-1].
  {       e#   For each permutation:
    :PP   e#     Save it in P. Push a copy.
    ff{   e#     For each X in P: For each Y in P:
      T== e#       Push T[X][Y].
      P#  e#       Find its index in P.
    }     e#
  }%      e#
  $       e#   Sort the results.
}%        e#
Q|,       e# Deduplicate and count.
+         e# Add the result to the 0 on the stack.

Приємно. Я намагався "німий" грубий, але до 5 було важко, тому я торгував байтами для швидкості.
Андреа Біондо

1

Python 2 , 515 507 байт

  • Збережено вісім байтів завдяки Деннісу .
def F(n):
 def f(k,*s):n==len(set(s))and S.add(s);{k and f(~-k,j,*s)for j in I}
 def c(k,*G):k and{s in G or c(~-k,s,*G)for s in S}or(I in G)&all((o(x,y)in G)&any(I==o(z,x)for z in G)for x in G for y in G)and A.add(G)
 S=set();A=S-S;I=tuple(range(n));o=lambda x,y:tuple(y[x[j]]for j in I);i=lambda G,H:any(all(o(H[B[i]],H[B[j]])==H[B[[k for k in I if G[k]==o(G[i],G[j])][0]]]for i in I for j in I)for B in S);f(n);c(n);K=list(A);[G in K and{G!=H and i(G,H)and K.remove(H)for H in K}for G in A];return len(K)

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


н Σнн

Посилання на багатослівну версію .


Чи виконують накази sі мають Gзначення? Якщо ні, ви можете використовувати def f(k,*s):...f(~-k,j,*s)...і def c(k,*G):...c(~-k,s,*G).....
Денніс

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