Sắp xếp bong bóng


26

Lưu ý, thử thách được sao chép từ câu hỏi được hỏi tại math.stackexchange .

Gần đây, tôi đã đạt được một số kỹ năng thổi bong bóng. Đầu tiên tôi sẽ thổi bong bóng như thế này: nhập mô tả hình ảnh ở đây

Nhưng rồi mọi thứ bắt đầu trở nên kỳ lạ:

nhập mô tả hình ảnh ở đây

Sau một thời gian, tôi đã thổi một số bong bóng khá kỳ lạ:

nhập mô tả hình ảnh ở đây

Sau khi thổi hàng trăm, thậm chí hàng ngàn bong bóng như vậy, trán tôi đột nhiên nhăn lại với câu hỏi: Cho n bong bóng, bạn có thể sắp xếp chúng bao nhiêu cách khác nhau? Ví dụ: nếu n = 1, chỉ có 1 sắp xếp. Nếu n = 2, có 2 cách sắp xếp. Nếu n = 3, có 4 cách sắp xếp. Nếu n = 4, có 9 cách sắp xếp.

Dưới đây là 9 cách sắp xếp của 4 bong bóng:
nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Sau khi thổi tất cả những bong bóng kỳ diệu này, tôi quyết định rằng tôi nên chia sẻ niềm vui khi đếm sự sắp xếp của chúng với bạn. Vì vậy, đây là nhiệm vụ của bạn:


Mục tiêu

Viết chương trình, hàm hoặc tương tự đếm số cách bạn có thể sắp xếp nbong bóng.


Đầu vào

n, số lượng bong bóng. n> 0


Đầu ra

Số cách bạn có thể sắp xếp các bong bóng này.


Tiêu chí chiến thắng

Sẽ thật tuyệt nếu chúng ta có thể thổi bong bóng xung quanh mã của bạn. Mã của bạn càng nhỏ, mã càng dễ thực hiện. Vì vậy, người tạo mã có số byte ít nhất sẽ giành chiến thắng trong cuộc thi.


Thông tin bổ sung

OEIS


5
Nếu các bong bóng có thể giao nhau thì đó là một vấn đề mở , với 173 giải pháp cho n = 4 .
orlp

@orlp May mắn thay, những bong bóng này không giao nhau.
TheNumberOne

1
0một đầu vào hợp lệ?
Martin Ender

@ KenY-N Có. Đã có một liên kết OEIS ở phía dưới
Roman Gräf

Rất tiếc! Xóa thời gian bình luận ngu ngốc ...
Ken YN

Câu trả lời:


12

Python 2, 92 87 byte

a=lambda n:n<2or sum((k%d<1)*d*a(d)*a(n-k)for k in range(1,n)for d in range(1,1+k))/~-n

Trong tiếng Anh đơn giản: để tính toán, a(n)chúng tôi tính toán d*a(d)*a(n-k)cho mọi ước dcủa mọi số nguyên dương knhỏ hơn hoặc bằng n, tổng tất cả các số này và chia cho n-1.

Để làm cho nó chạy nhanh hơn, hãy chạy trong Python 3 (thay thế /bằng //chức năng trên) và ghi nhớ:

import functools
a = functools.lru_cache(None)(a)

Nếu bạn làm điều này, nó sẽ tính toán a(50) = 425976989835141038353ngay lập tức.


Wow nó thật tuyệt. Tôi giả sử rằng lru_cache()ghi nhớ các chức năng?
Patrick Roberts

@PatrickRoberts Yep, thường được sử dụng như một công cụ trang trí chức năng, nhưng bạn cũng có thể áp dụng nó một cách thủ công cho một chức năng.
orlp

@PatrickRoberts Đây là tài liệu cholru_cache .
PM 2Ring

Hàm này trả về Truecho n<2. Tôi đoán điều đó ổn n=1, vì trong Python Trueđánh giá là 1 trong bối cảnh số, nhưng a(0)sẽ trả về 0. Bạn có thể sửa nó bằng n<2 and n or sum...nhưng có thể có một cách nhỏ gọn hơn.
PM 2Ring

Tôi đoán có thể đưa ra một lập luận rằng có một cách để sắp xếp các bong bóng bằng không, nhưng điều đó không phù hợp với A000081. OTOH, nếu chúng ta chỉ cần giải quyết cho tích cực nthì chúng ta có thể bỏ qua trường hợp góc này một cách an toàn, vì nó không ảnh hưởng đến các cuộc gọi đệ quy cho cao hơn n.
PM 2Ring

10

GNU Prolog, 98 byte

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.
c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

Câu trả lời này là một ví dụ tuyệt vời về cách Prolog có thể đấu tranh với các định dạng I / O đơn giản nhất. Nó hoạt động theo phong cách Prolog thực sự thông qua việc mô tả vấn đề, thay vì thuật toán để giải quyết nó: nó chỉ định những gì được tính là sắp xếp bong bóng hợp pháp, yêu cầu Prolog tạo ra tất cả các sắp xếp bong bóng đó, sau đó đếm chúng. Thế hệ có 55 ký tự (hai dòng đầu tiên của chương trình). Việc đếm và I / O lấy 43 khác (dòng thứ ba và dòng mới tách hai phần). Tôi cá rằng đây không phải là vấn đề mà OP dự kiến ​​sẽ khiến các ngôn ngữ phải vật lộn với I / O! (Lưu ý: Đánh dấu cú pháp của Stack Exchange làm cho việc này khó đọc hơn, không dễ hơn, vì vậy tôi đã tắt nó).

Giải trình

Hãy bắt đầu với phiên bản mã giả của một chương trình tương tự không thực sự hoạt động:

b(Bubbles,Count) if map(b,Bubbles,BubbleCounts)
                and sum(BubbleCounts,InteriorCount)
                and Count is InteriorCount + 1
                and is_sorted(Bubbles).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

Một điều khá rõ ràng là cách thức bhoạt động: chúng tôi biểu diễn các bong bóng thông qua các danh sách được sắp xếp (đó là một cách thực hiện đơn giản của nhiều trang khiến các đa số bằng nhau để so sánh bằng nhau) và một bong bóng duy nhất []có số lượng là 1, với một bong bóng lớn hơn có số đếm bằng tổng số bong bóng bên trong cộng với 1. Với số lượng 4, chương trình này sẽ (nếu nó hoạt động) tạo ra các danh sách sau:

[[],[],[],[]]
[[],[],[[]]]
[[],[[],[]]]
[[],[[[]]]]
[[[]],[[]]]
[[[],[],[]]]
[[[],[[]]]]
[[[[],[]]]]
[[[[[]]]]]

Chương trình này không phù hợp như một câu trả lời vì nhiều lý do, nhưng điều cấp bách nhất là Prolog không thực sự có một mapvị ngữ (và viết nó sẽ mất quá nhiều byte). Vì vậy, thay vào đó, chúng tôi viết chương trình như thế này:

b([], 0).
b([Head|Tail],Count) if b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and Count is HeadCount + TailCount + 1
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

Vấn đề lớn khác ở đây là nó sẽ đi vào một vòng lặp vô hạn khi chạy, vì cách thức hoạt động của trật tự đánh giá của Prolog. Tuy nhiên, chúng ta có thể giải quyết vòng lặp vô hạn bằng cách sắp xếp lại chương trình một chút:

b([], 0).
b([Head|Tail],Count) if Count #= HeadCount + TailCount + 1
                    and b(Head,HeadCount)
                    and b(Tail,TailCount)
                    and is_sorted([Head|Tail]).
c(Count,NPossibilities) if listof(Bubbles,b(Bubbles,Count),List)
                       and length(List,NPossibilities).

Đây có thể trông khá lạ - chúng tôi đang thêm cùng nhau đếm trước khi chúng ta biết chúng là gì - nhưng GNU Prolog của #=là khả năng xử lý mà loại số học noncausal, và bởi vì đó là dòng đầu tiên của b, và HeadCountTailCountphải cả hai được ít hơn Count(được biết đến), nó phục vụ như một phương pháp giới hạn tự nhiên bao nhiêu lần thuật ngữ đệ quy có thể khớp, và do đó làm cho chương trình luôn luôn chấm dứt.

Bước tiếp theo là đánh golf này xuống một chút. Xóa khoảng trắng, sử dụng tên biến ký tự đơn, sử dụng các chữ viết tắt như :-for if,for and, bằng cách sử dụng setofchứ không phải listof(nó có tên ngắn hơn và tạo ra kết quả tương tự trong trường hợp này) và sử dụng sort0(X,X)chứ không phải is_sorted(X)(vì is_sortedthực sự không phải là hàm thực, Tôi làm ra nó):

b([],0).
b([H|T],N):-N#=A+B+1,b(H,A),b(T,B),sort0([H|T],[H|T]).
c(X,Y):-setof(A,b(A,X),L),length(L,Y).

Điều này khá ngắn, nhưng nó có thể làm tốt hơn. Cái nhìn sâu sắc quan trọng [H|T]là thực sự dài dòng như cú pháp danh sách đi. Như các lập trình viên Lisp sẽ biết, một danh sách về cơ bản chỉ được tạo từ các ô khuyết điểm, về cơ bản chỉ là các bộ dữ liệu và hầu như không có phần nào của chương trình này sử dụng các danh sách dựng sẵn. Prolog có một số cú pháp tuple rất ngắn (yêu thích của tôi là A-B, nhưng yêu thích thứ hai của tôi là A/B, tôi đang sử dụng ở đây vì nó tạo ra đầu ra gỡ lỗi dễ đọc hơn trong trường hợp này); và chúng ta cũng có thể chọn một ký tự nilriêng cho cuối danh sách, thay vì bị mắc kẹt với hai ký tự [](tôi đã chọn x, nhưng về cơ bản mọi thứ đều hoạt động). Vì vậy, thay vì [H|T], chúng ta có thể sử dụng T/Hvà nhận đầu ra từb trông giống như thế này (lưu ý rằng thứ tự sắp xếp trên các bộ dữ liệu hơi khác so với thứ tự trong danh sách, vì vậy chúng không theo thứ tự như trên):

x/x/x/x/x
x/x/x/(x/x)
x/(x/x)/(x/x)
x/x/(x/x/x)
x/(x/x/x/x)
x/x/(x/(x/x))
x/(x/x/(x/x))
x/(x/(x/x/x))
x/(x/(x/(x/x)))

Điều này khá khó đọc hơn các danh sách lồng nhau ở trên, nhưng nó có thể; bỏ qua tinh thần xvà giải thích /()như một bong bóng (hoặc chỉ đơn giản /là một bong bóng thoái hóa không có nội dung, nếu không có() nó sau đó) và các yếu tố có sự tương ứng 1-1 (nếu bị rối loạn) với phiên bản danh sách được hiển thị ở trên .

Tất nhiên, đại diện danh sách này, mặc dù ngắn hơn nhiều, có một nhược điểm lớn; nó không được tích hợp vào ngôn ngữ, vì vậy chúng tôi không thể sử dụng sort0để kiểm tra xem danh sách của chúng tôi có được sắp xếp hay không. sort0Tuy nhiên, khá dài dòng, do đó, việc thực hiện bằng tay không phải là một mất mát lớn (trên thực tế, thực hiện bằng tay trên [H|T]biểu diễn danh sách có cùng số byte). Cái nhìn sâu sắc quan trọng ở đây là chương trình dưới dạng văn bản kiểm tra xem danh sách đã được sắp xếp chưa, nếu đuôi của nó được sắp xếp, nếu đuôi của nó được sắp xếp, v.v. có rất nhiều kiểm tra dư thừa, và chúng ta có thể khai thác điều đó. Thay vào đó, chúng tôi sẽ kiểm tra để đảm bảo rằng hai yếu tố đầu tiên được xếp theo thứ tự (điều này đảm bảo rằng danh sách sẽ kết thúc được sắp xếp một khi danh sách đó và tất cả các hậu tố của nó được kiểm tra).

Yếu tố đầu tiên có thể dễ dàng truy cập; đó chỉ là người đứng đầu danh sách H. Tuy nhiên, phần tử thứ hai khá khó truy cập và có thể không tồn tại. May mắn thay, xít hơn tất cả các bộ dữ liệu mà chúng tôi đang xem xét (thông qua toán tử so sánh tổng quát của Prolog @>=), vì vậy chúng tôi có thể xem xét "yếu tố thứ hai" của danh sách đơn xvà chương trình sẽ hoạt động tốt. Đối với việc thực sự truy cập vào phần tử thứ hai, phương thức khó nhất là thêm một đối số thứ ba (một đối số out) vào b, trả về xtrong trường hợp cơ sở và Htrong trường hợp đệ quy; điều này có nghĩa là chúng ta có thể lấy phần đầu của đuôi như một đầu ra từ lệnh gọi đệ quy thứ hai đến B, và tất nhiên phần đầu của phần đuôi là phần tử thứ hai của danh sách. Vì vậy, btrông như thế này bây giờ:

b(x,0,x).
b(T/H,N,H):-N#=A+B+1,b(H,A,_),b(T,B,J),H@>=J.

Trường hợp cơ sở là đủ đơn giản (danh sách trống, trả về số 0, "phần tử đầu tiên" của danh sách trống là x). Trường hợp đệ quy bắt đầu giống như trước đây (chỉ với T/Hký hiệu chứ không phải [H|T], và Hnhư là một đối số phụ); chúng tôi bỏ qua đối số phụ từ cuộc gọi đệ quy trên đầu, nhưng lưu trữ nó trong Jcuộc gọi đệ quy ở đuôi. Sau đó, tất cả những gì chúng ta phải làm là đảm bảo Hlớn hơn hoặc bằng J(nghĩa là "nếu danh sách có ít nhất hai phần tử, phần tử đầu tiên lớn hơn hoặc bằng phần thứ hai) để đảm bảo rằng danh sách kết thúc được sắp xếp.

Thật không may, setofsẽ phù hợp nếu chúng ta cố gắng sử dụng định nghĩa trước đó ccùng với định nghĩa mới này b, bởi vì nó xử lý các tham số không được sử dụng theo cách tương tự như SQL GROUP BY, hoàn toàn không phải là điều chúng ta muốn. Có thể cấu hình lại nó để làm những gì chúng ta muốn, nhưng việc cấu hình lại đó có chi phí cho các ký tự. Thay vào đó, chúng tôi sử dụng findall, có hành vi mặc định thuận tiện hơn và chỉ dài hơn hai ký tự, cho chúng tôi định nghĩa về c:

c(X,Y):-findall(A,b(A,X,_),L),length(L,Y).

Và đó là chương trình hoàn chỉnh; Tạo ra các mẫu bong bóng một cách chặt chẽ, sau đó dành toàn bộ tải byte để đếm chúng (chúng ta cần một khoảng thời gian khá dài findallđể chuyển đổi trình tạo thành danh sách, sau đó không may được đặt tên rõ ràng lengthđể kiểm tra độ dài của danh sách đó, cộng với bảng kê khai để khai báo hàm).


"Prolog không thực sự có một vị từ bản đồ" : Prolog không có maplist/2-8vị ngữ , mặc dù tôi không chắc điều này sẽ khiến mọi thứ ngắn hơn ở đây.
Gây tử vong

@Firthize: Huh, có vẻ như đã được thêm vào trong phiên bản mới hơn. Nó không có trong tài liệu cho bản cài đặt mà tôi có và nó không hoạt động trong thực tế:| ?- maplist(reverse,[A,B]). uncaught exception: error(existence_error(procedure,maplist/2),top_level/0)

Điều đó thực sự kỳ lạ; maplistlà một vị từ được sử dụng rất phổ biến được cung cấp trong các bản phân phối Prolog chính (như SWI-Prolog và SiCStus)
Fatalize

10

Toán học, 68 byte

Tôi cá là điều này có thể bị đánh bại (ngay cả trong Mathicala) với cách triển khai từ đầu, nhưng đây là phiên bản dựng sẵn:

<<NumericalDifferentialEquationAnalysis`
Last@ButcherTreeCount[#+1]&

ButcherTreeCountdo đó được lập chỉ mục 0, do đó [#+1], và trả về một danh sách tất cả các giá trị theo đối số của nó, do đó Last@. Nhưng nếu không, nó chỉ là nội dung cho chức năng này. Tuy nhiên, nó yêu cầu tải một gói, đó là những gì dòng đầu tiên làm.


8
"Tất nhiên Mathicala có một nội dung cho điều đó."
orlp
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.