CJam, 189 187 byte
Yang ini akan sulit dijelaskan ... Kompleksitas waktu dijamin 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=},,}{!!}?
Jika Anda cukup berani, cobalah secara online . Di laptop jelek saya, saya bisa mendapatkan hingga 6 dengan juru bahasa Java atau 5 di juru bahasa online.
Penjelasan
Saya tidak memiliki latar belakang matematika yang besar (baru lulus SMA, mulai CS di universitas minggu depan). Jadi bersabarlah jika saya melakukan kesalahan, nyatakan yang jelas, atau lakukan hal-hal dengan cara yang sangat tidak efisien.
Pendekatan saya adalah kekuatan kasar, meskipun saya mencoba membuatnya sedikit lebih pintar. Langkah-langkah utamanya adalah:
- Hasilkan semua operan yang mungkin ∗ untuk sekelompok urutan n (yaitu, hitung semua kelompok pesanan n );
- Hasilkan semua kemungkinan bijections φ antara dua kelompok urutan n ;
- Dengan menggunakan hasil dari langkah 1 dan 2, tentukan semua isomorfisme antara dua kelompok orde n ;
- Dengan menggunakan hasil dari langkah 3, hitung jumlah kelompok hingga isomorfisma.
Sebelum melihat bagaimana setiap langkah dilakukan, mari kita buat beberapa kode sepele:
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)
Algoritma berikut tidak bekerja dengan benar dengan n <4 , kasing dari 0 ke 3 ditangani dengan negasi ganda.
Mulai sekarang, elemen-elemen grup akan ditulis sebagai {1, a, b, c, ...} , di mana 1 adalah elemen identitas. Dalam implementasi CJam, elemen yang sesuai adalah {0, 1, 2, 3, ...} , di mana 0 adalah elemen identitas.
Mari kita mulai dari langkah 1. Menulis semua operator yang memungkinkan untuk sekelompok pesanan n setara dengan menghasilkan semua tabel Cayley n × n yang valid . Baris dan kolom pertama sepele: keduanya {1, a, b, c, ...} (kiri-ke-kanan, atas-ke-bawah).
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
Mengetahui bahwa tabel Cayley juga merupakan kotak Latin yang diperkecil (karena properti pembatalan) memungkinkan untuk menghasilkan tabel yang mungkin baris-demi-baris. Mulai dari baris kedua (indeks 1), kami menghasilkan semua permutasi unik untuk baris itu, menjaga kolom pertama tetap pada nilai indeks.
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
Tidak semua permutasi itu valid, tentu saja: setiap baris dan kolom harus berisi semua elemen tepat satu kali. Blok filter digunakan untuk tujuan ini (nilai kebenaran menjaga permutasi, yang salah menghapusnya):
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
Perhatikan bahwa saya memfilter di dalam loop generasi: ini membuat kode sedikit lebih lama (dibandingkan dengan generasi berbeda dan pemfilteran), tetapi sangat meningkatkan kinerja. Jumlah permutasi dari satu set ukuran n adalah n! , jadi solusi yang lebih pendek tentu membutuhkan banyak memori dan waktu.
Daftar tabel Cayley yang valid adalah langkah bagus untuk menghitung operator, tetapi sebagai struktur 2D, ia tidak dapat memeriksa asosiatif, yang merupakan properti 3D. Jadi langkah selanjutnya adalah menyaring fungsi non-asosiatif.
{ }, 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]
Fiuh! Banyak pekerjaan, tetapi sekarang kami telah menghitung semua kelompok pesanan n (atau lebih baik, operasi di atasnya - tetapi set tetap, jadi itu adalah hal yang sama). Langkah selanjutnya: temukan isomorfisma. Isomorfisme adalah suatu ikatan antara dua kelompok tersebut sehingga φ (x ∗ y) = φ (x) ∗ φ (y) . Membuat bijections di CJam itu sepele: Ne!
akan melakukannya. Bagaimana kita memeriksanya? Solusi saya mulai dari dua salinan tabel Cayley selama x ∗ y . Pada satu salinan, φ diterapkan ke semua elemen, tanpa menyentuh urutan baris atau kolom. Ini menghasilkan tabel untuk φ (x ∗ y) . Di sisi lain elemen dibiarkan apa adanya, tetapi baris dan kolom dipetakan melalui φ . Yaitu, baris / kolom x menjadi baris / kolomφ (x) . Ini menghasilkan tabel untuk φ (x) ∗ φ (y) . Sekarang kita memiliki dua tabel, kita hanya harus membandingkannya: jika mereka sama, kita telah menemukan isomorfisme.
Tentu saja, kita juga perlu membuat pasangan kelompok untuk menguji isomorfisme. Kami membutuhkan semua 2 kombinasi grup. Sepertinya CJam tidak memiliki operator untuk kombinasi. Kita dapat menghasilkan mereka dengan mengambil setiap grup dan menggabungkannya hanya dengan elemen yang mengikutinya dalam daftar. Fakta menyenangkan: jumlah 2-kombinasi adalah n × (n - 1) / 2 , yang juga merupakan jumlah dari n - 1 bilangan asli. Angka tersebut disebut angka segitiga: coba algoritme di atas kertas, satu baris per elemen tetap, dan Anda akan tahu alasannya.
: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
Kode di atas memperbaiki elemen pertama dari pasangan, L [X] , dan menggabungkannya dengan kelompok lain (sebut saja masing-masing dari mereka Y ). Ini melewati pasangan ke blok uji isomorfisma yang akan saya tunjukkan sebentar lagi. Blok memberikan kembali daftar nilai dari Y yang L [X] adalah isomorfik ke Y . Kemudian L [X] ditambahkan ke daftar ini. Sebelum memahami mengapa daftar diatur sedemikian rupa, mari kita lihat tes isomorfisme:
\_@ 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
Hebat, sekarang kami memiliki daftar set seperti [{L [0], Y1, Y2, ...}, {L [1], Y1, ...}, ...] . Idenya di sini adalah, dengan properti transitif, jika ada dua set yang memiliki setidaknya satu elemen yang sama maka semua kelompok dalam dua set adalah isomorfik. Mereka dapat dikumpulkan menjadi satu set. Karena L [X] tidak akan pernah muncul dalam kombinasi yang dihasilkan oleh L [X + ...] , setelah menggabungkan setiap rangkaian isomorfisma akan memiliki satu elemen unik. Jadi, untuk mendapatkan jumlah isomorfisma, cukup untuk menghitung berapa banyak kelompok yang muncul tepat sekali dalam semua kelompok kelompok isomorfik. Untuk melakukan ini, saya membuka bungkusan set sehingga terlihat seperti [L [0], Y1, Y2, ..., L [1], Y1, ...] , urutkan daftar untuk membuat kelompok dari kelompok yang sama dan akhirnya RLE-encode itu.
:~ 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
Itu saja, semuanya.