1. Định lý Gale-Ryser
Định lý Gale-Ryser là một trong những định lý cổ điển của toán Tổ Hợp. Định lý này trả lời câu hỏi sau đây:
Cho trước hai vectors và gồm các số nguyên dương. Có tồn tại một ma trận nhị phân gồm hàng và cột, sao cho tổng hàng thứ của là và tổng cột thứ của là . Câu hỏi này tương đương với câu hỏi liệu có tồn tại đồ thị hai phần (bipartite graph) cho trước bậc của các đỉnh.
Ta có thể thấy ngay vài quan sát đơn giản để tránh các trường hợp ngớ ngẩn:
- Tất nhiên ta cần có
- Ta có thể giả sử và
Nói cách khác, ta chỉ cần trả lời câu hỏi trên khi mà và là hai phân hoạch nguyên (integer partition) của cùng một số nguyên dương . Một cách trực quan để hình dùng một phân hoạch nguyên là ta dùng sơ đồ Ferrers (Ferrers diagram). Ví dụ, sơ đồ Ferrers của phân hoạch có thể minh hoạ bằng sơ đồ Ferrrers như sau:
sage: print Partition([6,3,1]).ferrers_diagram() ****** *** *
(Ở trên ta dùng sage, một phần mềm tính toán tuyệt vời, miễn phí, thay vì đám Matlab, Maple, Mathematica.) Khi ta lật cái sơ đồ Ferrers này theo trục chéo chính thì ta có một phân hoạch liên hợp (conjugate partition) của phân hoạch cho trước. Phân hoạc liên hợp của là phân hoạch .
sage: print Partition([6,3,1]).conjugate().ferrers_diagram() *** ** ** * * *
Cho một phân hoạch nguyên thì phân hoạch liên hợp của nó ký hiệu là . Cho hai phân hoạch của cùng một số nguyên thì áp đảo (dominate), ký hiệu nếu và chỉ nếu , với mọi . (Ở đây, nếu không tồn tại giá trị hay thì ta định nghĩa hay bằng .)
Bài tập: Chứng minh rằng nếu và chỉ nếu .
Đến đây ta đã đủ khái niệm để phát biểu định lý Gale-Ryser.
Định Lý Gale-Ryser: cho trước hai phân hoạch và của cùng một số nguyên dương . Tồn tại một ma trận nhị phân kích thước với là vector các tổng hàng và là vector các tổng cột nếu và chỉ nếu
Chứng minh. Có nhiều cách chứng minh định lý này. Một cách đơn giản là dùng định lý luồng cực đại, cắt cực tiểu (max-flow, min-cut). Ta xây dựng một mạng luồng (flow network) với nguồn (source), điểm thoát (sink), đỉnh trung gian và đỉnh trung gian khác . Các cạnh có dung lượng (capacity) đúng bằng . Các cạnh có dung lượng đúng bằng . Và các cạnh có dung lượng bằng .
Dễ thấy rằng tồn tại ma trận cần tìm nếu và chỉ nếu tồn tại một luồng từ đến với tổng dung lượng bằng . Mà điều này xảy ra nếu và chỉ nếu nhát cắt cực tiểu có dung lượng đúng bằng . Do đó ta chỉ cần chứng minh rằng nếu và chỉ nếu dung lượng cắt cực tiểu bằng .
Gọi , và . Một nhát cắt bất kỳ sẽ chia ra phía nguồn là tập và phía thoát , trong đó là một phân hoạch của tập và là một phân hoạch của tập . Nhát cắt này có dung lượng bằng
Giả sử dung lượng cắt cực tiểu bằng . Khi đó, với một nhát cắt tuỳ ý thì dung lượng của nó ít nhất là . Nói cách khác, với mọi phân hoạch và , ta có
Xét một số nguyên dương tuỳ ý. Chọn , và là tập tất cả các sao cho . Khi đó, nếu là phân hoạch liên hợp của thì dễ thấy rằng . Vì thế .
Chiều nghịch cũng không khó khăn gì. Giả sử , ta phải chứng minh rằng với mọi phân hoạch và . Cái tổng lớn nhất nếu mà là tập . Và cái tổng nhỏ nhất nếu là tập . Khi đó, bất đẳng thức cần chứng minh ta "moi" ra từ định nghĩa của sự áp đảo.
QED.
2. Xây dựng một ma trận nhị phân với các vector tổng hàng và tổng cột cho trước
Định lý Gale-Ryser không cho ta thuật toán trực tiếp xây dựng ma trận . Tất nhiên, ta có thể giải bài toán luồng cực đại nhưng như vậy có vẻ giết gà bằng dao mổ trâu. Vấn đề kế tiếp ta muốn giải quyết là, cho trước , xây dựng ma trận với vector tổng hàng là và vector tổng cột là .
Ta giải quyết bài toán này bằng một thuật toán tham lam như sau. (Thuật toán này chứng minh một chiều của định lý Gale-Ryser mà không dùng định lý luồng cực đại.) Ta cần xác định xem đặt các số vào đâu trong cột thứ nhất của , rồi dùng đệ qui để giải quyết phần còn lại cho các cột từ đến . Để đệ quy đơn giản, ta cho phép các số là không âm thay vì dương. Do , ta biết . Mà chính là tổng số các giá trị sao cho .
Cách đơn giản là đặt tổng cộng số ở các hàng mà , tính từ lớn nhất lên đến nhỏ nhất. Sau khi đã có các số này ở cột thứ nhất, thì ta giải quyết bài toán cho các cột còn lại. Tổng các cột bây giờ là , còn tổng các hàng là
Dễ thấy rằng điều kiện Gale-Ryser vẫn thoả mãn cho bài toán đệ qui. (Cần một ít trí tưởng tượng, nhưng thật sự là “dễ” thấy.) Thuật toán này thể hiện bằng Python như sau:
# ************************************************************************** # assume r^* dominates c, and r and c are partitions of the same positive # integer B. Returns a matrix A whose row sums are r and column sums are c # ************************************************************************** def gale_ryser(r, c): n = len(c); m = len(r) A = [ [0]*n + [r[i], i] for i in range(m) ] for j in range(n): if (c[j] == 0): break for i in range(c[j]): A[i][n] = A[i][n]-1 A[i][j] = 1 # sort A in terms of r, the row with the largest r_i comes first A.sort(key = lambda row: row[n], reverse = True) A.sort(key = lambda row: row[n+1]) # the original row order return [ row[0:n] for row in A ] # simple test case r = [3, 2, 2, 2, 1, 0, 0] c = [3, 3, 3, 1, 0] for row in gale_ryser(r, c): print row
Chạy thử
$ python gale-ryser.py [1, 1, 1, 0, 0] [1, 1, 0, 0, 0] [1, 0, 1, 0, 0] [0, 1, 1, 0, 0] [0, 0, 0, 1, 0] [0, 0, 0, 0, 0] [0, 0, 0, 0, 0]
3. Một bài toán khó … chịu
Thế nếu ta muốn in tất cả các ma trận cho trước hai vectors tổng hàng và cột, thì làm sao cho hiệu quả? Đây là một bài toán khó.