CJam, 189 187 byte
Điều này sẽ khó giải thích ... Sự phức tạp về thời gian được đảm bảo 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=},,}{!!}?
Nếu bạn đủ can đảm, hãy thử trực tuyến . Trên máy tính xách tay xảo quyệt của tôi, tôi có thể có tới 6 trình thông dịch Java hoặc 5 trong trình thông dịch trực tuyến.
Giải trình
Tôi không có nền tảng toán học lớn (vừa học xong trung học, bắt đầu CS vào tuần tới). Vì vậy, hãy chịu đựng nếu tôi phạm sai lầm, nói rõ ràng, hoặc làm mọi thứ theo cách không hiệu quả khủng khiếp.
Cách tiếp cận của tôi là một lực lượng vũ phu, mặc dù tôi đã cố gắng làm cho nó thông minh hơn một chút. Các bước chính là:
- Tạo ra tất cả các toán hạng có thể * cho một nhóm trật tự n (ví dụ, liệt kê tất cả các nhóm trật tự n );
- Tạo ra tất cả các bijections thể φ giữa hai nhóm về trật tự n ;
- Sử dụng kết quả từ bước 1 và 2, xác định tất cả các đẳng cấu giữa hai nhóm thứ tự n ;
- Sử dụng kết quả từ bước 3, đếm số lượng nhóm lên đến đẳng cấu.
Trước khi xem cách từng bước được thực hiện, hãy lấy một số mã tầm thường ra khỏi đường đi:
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)
Thuật toán sau không hoạt động chính xác với n <4 , các trường hợp từ 0 đến 3 được xử lý với phủ định kép.
Từ giờ trở đi, các phần tử của một nhóm sẽ được viết là {1, a, b, c, ...} , trong đó 1 là phần tử nhận dạng. Trong triển khai CJam, các phần tử tương ứng là {0, 1, 2, 3, ...} , trong đó 0 là phần tử nhận dạng.
Hãy bắt đầu từ bước 1. Viết tất cả các toán tử có thể cho một nhóm đơn hàng n tương đương với việc tạo tất cả các bảng Cayley n × n hợp lệ . Hàng và cột đầu tiên là tầm thường: cả hai đều là {1, a, b, c, ...} (từ trái sang phải, từ trên xuống).
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
Biết rằng bảng Cayley cũng là một hình vuông Latin giảm (do thuộc tính hủy) cho phép tạo các bảng có thể theo từng hàng. Bắt đầu từ hàng thứ hai (chỉ mục 1), chúng tôi tạo ra tất cả các hoán vị duy nhất cho hàng đó, giữ cho cột đầu tiên cố định với giá trị của chỉ mục.
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
Tất nhiên, không phải tất cả các hoán vị đều hợp lệ: mỗi hàng và cột phải chứa tất cả các phần tử chính xác một lần. Một khối bộ lọc được sử dụng cho mục đích này (một giá trị trung thực giữ cho hoán vị, một khối giả sẽ loại bỏ nó):
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
Lưu ý rằng tôi đang lọc bên trong vòng lặp thế hệ: điều này làm cho mã dài hơn một chút (so với việc tạo và lọc riêng biệt), nhưng cải thiện hiệu năng rất nhiều. Số lượng hoán vị của một tập hợp kích thước n là n! , vì vậy giải pháp ngắn hơn sẽ đòi hỏi nhiều bộ nhớ và thời gian.
Danh sách các bảng Cayley hợp lệ là một bước tuyệt vời để liệt kê các toán tử, nhưng là một cấu trúc 2D, nó không thể kiểm tra tính kết hợp, là một thuộc tính 3D. Vì vậy, bước tiếp theo là lọc ra các chức năng không liên kết.
{ }, 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]
Phù! Rất nhiều công việc, nhưng bây giờ chúng tôi đã liệt kê tất cả các nhóm thứ tự n (hoặc tốt hơn, các thao tác trên nó - nhưng tập hợp đã được sửa, vì vậy đó là điều tương tự). Bước tiếp theo: tìm đẳng cấu. Một đẳng cấu là một sự chọn lọc giữa hai trong số các nhóm đó sao cho φ (x ∗ y) = φ (x) ∗ (y) . Tạo ra những điều đó trong CJam là chuyện nhỏ: Ne!
sẽ làm được. Làm thế nào chúng ta có thể kiểm tra chúng? Giải pháp của tôi bắt đầu từ hai bản sao của bảng Cayley cho x ∗ y . Trên một bản sao, φ được áp dụng cho tất cả các yếu tố, mà không cần chạm thứ tự của hàng hoặc cột. Điều này tạo ra bảng cho φ (x ∗ y) . Trên kia các yếu tố còn lại như họ đang có, nhưng các hàng và cột được ánh xạ qua φ . Đó là, hàng / cộtx trở thành hàng / cột φ (x) . Điều này tạo ra bảng cho φ (x) ∗ (y) . Bây giờ chúng ta có hai bảng, chúng ta chỉ cần so sánh chúng: nếu chúng giống nhau, chúng ta đã tìm thấy một đẳng cấu.
Tất nhiên, chúng ta cũng cần tạo các cặp nhóm để kiểm tra đẳng cấu trên. Chúng tôi cần tất cả sự kết hợp 2 của các nhóm. Có vẻ như CJam không có toán tử cho các kết hợp. Chúng ta có thể tạo chúng bằng cách lấy từng nhóm và chỉ kết hợp nó với các phần tử theo sau nó trong danh sách. Thực tế thú vị: số kết hợp 2 là n × (n - 1) / 2 , cũng là tổng của n - 1 số tự nhiên đầu tiên . Các số như vậy được gọi là số tam giác: thử thuật toán trên giấy, một hàng cho mỗi phần tử cố định và bạn sẽ thấy lý do tại sao.
: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
Đoạn mã trên sửa lỗi phần tử đầu tiên của cặp, L [X] và kết hợp nó với các nhóm khác (hãy gọi mỗi một trong số Y đó ). Nó chuyển cặp đến một khối kiểm tra đẳng cấu mà tôi sẽ hiển thị trong giây lát. Khối cung cấp cho trở lại một danh sách các giá trị của Y mà L [X] là đẳng cấu với Y . Sau đó L [X] được thêm vào danh sách này. Trước khi hiểu lý do tại sao các danh sách được thiết lập theo cách như vậy, chúng ta hãy xem xét nghiệm đẳng cấu:
\_@ 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
Tuyệt vời, bây giờ chúng tôi có một danh sách các bộ như [{L [0], Y1, Y2, ...}, {L [1], Y1, ...}, ...] . Ý tưởng ở đây là, bởi tính chất bắc cầu, nếu bất kỳ hai tập hợp nào có ít nhất một phần tử chung thì tất cả các nhóm trong hai bộ đều là đẳng cấu. Chúng có thể được tổng hợp thành một bộ duy nhất. Vì L [X] sẽ không bao giờ xuất hiện trong các kết hợp được tạo bởi L [X + ...] , sau khi tổng hợp, mỗi bộ đẳng cấu sẽ có một yếu tố duy nhất. Vì vậy, để có được số lượng đồng phân, đủ để đếm có bao nhiêu nhóm xuất hiện chính xác một lần trong tất cả các nhóm của các nhóm đẳng cấu. Để làm điều này, tôi mở khóa các tập hợp để chúng trông giống như [L [0], Y1, Y2, ..., L [1], Y1, ...] , sắp xếp danh sách để tạo các cụm của cùng một nhóm và cuối cùng RLE mã hóa nó.
:~ 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
Đó là tất cả mọi người.