CJam, 189 187 bytes
This one's gonna be tough to explain... Time complexity is guaranteed to be 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=},,}{!!}?
If you're brave enough, try it online. On my crappy laptop I can get up to 6 with the Java interpreter or 5 in the online interpreter.
Explanation
I don't have a big math background (just finished high school, starting CS at uni next week). So bear with me if I make mistakes, state the obvious, or do things in horribly inefficent ways.
My approach is a brute force, though I tried to make it a little more clever. The main steps are:
- Generate all the possible operands ∗ for a group of order n (i.e., enumerate all groups of order n);
- Generate all the possible bijections φ between two groups of order n;
- ใช้ผลที่ได้จากขั้นตอนที่ 1 และ 2 ที่กำหนด isomorphisms ทั้งหมดระหว่างสองกลุ่มของการสั่งซื้อ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คือองค์ประกอบตัวตน
เริ่มต้น Let 's จากขั้นตอนที่ 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
รู้ว่าตาราง Cayley ยังเป็นตารางละตินลดลง(เนื่องจากคุณสมบัติการยกเลิก) ช่วยให้การสร้างตารางที่เป็นไปได้ทีละแถว เริ่มต้นจากแถวที่สอง (ดัชนี 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!ดังนั้นวิธีแก้ปัญหาที่สั้นกว่านั้นจะต้องใช้หน่วยความจำและเวลามาก
รายการตาราง Cayley ที่ถูกต้องเป็นขั้นตอนที่ยอดเยี่ยมในการแจกแจงผู้ประกอบการ แต่เป็นโครงสร้าง 2 มิติจึงไม่สามารถตรวจสอบการเชื่อมโยงได้ซึ่งเป็นคุณสมบัติ 3 มิติ ดังนั้นขั้นตอนต่อไปคือการกรองฟังก์ชั่นที่ไม่เกี่ยวข้อง
{ }, 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 (หรือดีกว่าการดำเนินการบน - แต่ชุดได้รับการแก้ไขดังนั้นมันจึงเป็นสิ่งเดียวกัน) ขั้นตอนถัดไป: ค้นหามอร์ฟ มอร์ฟเป็น bijection ระหว่างสองกลุ่มเหล่านั้นเช่นว่าφ (x * y) = φ (x) * φ (y) การสร้าง bijections เหล่านั้นใน CJam นั้นเป็นเรื่องเล็กน้อย: Ne!
จะทำมัน เราจะตรวจสอบพวกเขาได้อย่างไร ทางออกของฉันเริ่มต้นจากสองสำเนาของตารางสำหรับเคย์ลีx * y ที่ ในหนึ่งสำเนาφจะถูกนำไปใช้กับองค์ประกอบทั้งหมดโดยไม่ต้องสัมผัสคำสั่งของแถวหรือคอลัมน์ นี้จะสร้างตารางสำหรับφ (x * y) ในอีกองค์ประกอบหนึ่งจะถูกทิ้งให้เป็นเช่นนั้น แต่แถวและคอลัมน์ถูกแมปผ่านxกลายเป็นแถว / คอลัมน์ φ นั่นคือแถว / คอลัมน์φ (x) สิ่งนี้สร้างตารางสำหรับφ (x) ∗ φ (y)(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]เป็น isomorphic จะ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, ... } ... ] ความคิดที่นี่คือว่าโดยคุณสมบัติสกรรมกริยาถ้าสองชุดใดมีองค์ประกอบอย่างน้อยหนึ่งที่เหมือนกันแล้วทุกกลุ่มในสองชุดมี isomorphic สามารถรวมเป็นชุดเดียว เนื่องจากL [X]จะไม่ปรากฏในชุดค่าผสมที่สร้างโดยL [X + ... ]หลังจากรวมชุดของมอร์ฟอร์มิซึ่มแต่ละชุดจะมีองค์ประกอบที่ไม่ซ้ำใคร ดังนั้นเพื่อให้ได้จำนวน isomorphisms ก็เพียงพอที่จะนับจำนวนกลุ่มที่ปรากฏขึ้นหนึ่งครั้งในกลุ่ม isomorphic ทุกชุด เมื่อต้องการทำสิ่งนี้ฉันถอดชุดเพื่อให้ดูเหมือน[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
นั่นคือทั้งหมดที่คน