การจัดเรียงฟอง


26

หมายเหตุ: ความท้าทายคัดลอกมาจากคำถามที่ถามที่math.stackexchange

เมื่อเร็ว ๆ นี้ฉันมีทักษะในการเป่าฟองสบู่ ตอนแรกฉันจะเป่าฟองสบู่แบบนี้ ป้อนคำอธิบายรูปภาพที่นี่

แต่สิ่งต่าง ๆ เริ่มแปลกประหลาด:

ป้อนคำอธิบายรูปภาพที่นี่

หลังจากผ่านไปครู่หนึ่งฉันก็เป่าฟองแปลก ๆ

ป้อนคำอธิบายรูปภาพที่นี่

หลังจากเป่าไปหลายร้อยฟองอาจจะถึงพันแล้วหน้าผากของฉันก็เหี่ยวย่นกับคำถาม: เมื่อได้รับฟอง n คุณสามารถจัดเรียงพวกเขาได้ในหลายวิธี ตัวอย่างเช่นถ้า n = 1 จะมีเพียง 1 การจัดเรียงเท่านั้น ถ้า n = 2 จะมีการจัดเรียง 2 แบบ ถ้า n = 3 มีการจัดเรียง 4 รายการ ถ้า n = 4 มีการจัดเรียง 9 รายการ

ต่อไปนี้คือการจัดเรียง 9 ฟอง 4 รายการ:
ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่

หลังจากเป่าฟองสบู่มหัศจรรย์ทั้งหมดนี้ฉันตัดสินใจว่าฉันควรแบ่งปันความสุขในการนับการจัดการกับพวกเขา ดังนั้นนี่คืองานของคุณ:


เป้าหมาย

เขียนโปรแกรมฟังก์ชั่นหรือสิ่งที่คล้ายกันซึ่งนับจำนวนวิธีที่คุณสามารถจัดเรียงnฟอง


อินพุต

nจำนวนฟองอากาศ n> 0


เอาท์พุต

จำนวนวิธีที่คุณสามารถจัดเรียงฟองเหล่านี้


เกณฑ์การชนะ

มันจะเจ๋งจริง ๆ ถ้าเราสามารถเป่าฟองรอบรหัสของคุณ ยิ่งคุณสร้างโค้ดให้เล็กลงเท่าไหร่ก็ยิ่งง่ายเท่านั้น ดังนั้นผู้ที่สร้างรหัสด้วยจำนวนไบต์น้อยที่สุดจะเป็นผู้ชนะการแข่งขัน


ข้อมูลเพิ่มเติม

OEIS


5
หากฟองอาจจะตัดมันเป็นปัญหาเปิดด้วย173 โซลูชั่นสำหรับ n = 4
orlp

@ หรือ lp โชคดีที่ฟองเหล่านี้ไม่ได้ตัดกัน
TheNumberOne

1
คือ0การป้อนข้อมูลที่ถูกต้อง?
Martin Ender

@ KenY-N ใช่ มีลิงก์ OEIS ที่ด้านล่างแล้ว
Roman Gräf

อ๊ะ! ลบความคิดเห็นที่โง่เวลา ...
Ken YN

คำตอบ:


12

Python 2, 92 87 ไบต์

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

ในภาษาอังกฤษธรรมดา: การคำนวณa(n)เราคำนวณd*a(d)*a(n-k)สำหรับหารทุกdของทุกจำนวนเต็มบวกkที่มีขนาดเล็กกว่าหรือเท่ากับสรุปทั้งหมดเหล่านี้และหารด้วยnn-1

เพื่อให้ทำงานได้เร็วขึ้นให้รันใน Python 3 (แทนที่/ด้วย//ฟังก์ชันด้านบน) และบันทึกความจำ:

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

หากคุณทำสิ่งนี้มันจะคำนวณa(50) = 425976989835141038353ทันที


ว้าวมันเยี่ยมมาก ฉันถือว่าlru_cache()ฟังก์ชันนี้จดจำได้หรือไม่
Patrick Roberts

@PatrickRoberts ใช่แล้วโดยปกติมันจะใช้เป็นมัณฑนากรฟังก์ชั่น แต่คุณยังสามารถใช้มันด้วยตนเองกับฟังก์ชั่น
orlp

@PatrickRoberts นี่คือเอกสารสำหรับ lru_cache
PM 2Ring

ฟังก์ชั่นนี้ผลตอบแทนสำหรับTrue n<2ฉันเดาว่าไม่เป็นไรn=1เนื่องจากใน Python Trueประเมินเป็น 1 ในบริบทตัวเลข แต่a(0)ควรส่งคืน 0 คุณสามารถแก้ไขได้ด้วยn<2 and n or sum...แต่อาจมีวิธีที่กระชับกว่า
PM 2Ring

ฉันคิดว่าการโต้แย้งสามารถทำได้ว่ามีวิธีหนึ่งในการจัดเรียงศูนย์ฟอง แต่ไม่สอดคล้องกับ A000081 OTOH ถ้าเราจะต้องแก้ปัญหาสำหรับบวกnแล้วเราสามารถละเว้นกรณีมุมนี้เพราะมันไม่ได้ส่งผลกระทบต่อการโทร recursive nสูงขึ้น
PM 2Ring

10

Prolog ของ GNU ขนาด 98 ไบต์

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).

คำตอบนี้เป็นตัวอย่างที่ดีของวิธีที่ Prolog สามารถต่อสู้กับรูปแบบ I / O ที่ง่ายที่สุด มันทำงานในสไตล์ Prolog จริงผ่านการอธิบายปัญหาแทนที่จะใช้อัลกอริทึมในการแก้ปัญหา: มันระบุสิ่งที่นับว่าเป็นการจัดเรียงตามกฎหมายฟองถาม Prolog เพื่อสร้างการจัดเรียงฟองเหล่านั้นทั้งหมดแล้วนับพวกเขา การสร้างใช้เวลา 55 ตัวอักษร (สองบรรทัดแรกของโปรแกรม) การนับและ I / O ใช้อีก 43 (บรรทัดที่สามและบรรทัดใหม่ที่แยกสองส่วน) ฉันเดิมพันว่านี่ไม่ใช่ปัญหาที่ OP คาดว่าจะทำให้ภาษาต้องต่อสู้กับ I / O! (หมายเหตุ: การเน้นไวยากรณ์ของ Stack Exchange ทำให้การอ่านยากขึ้นไม่ใช่เรื่องง่ายดังนั้นฉันจึงปิด)

คำอธิบาย

เริ่มด้วยโปรแกรม pseudocode รุ่นที่คล้ายกันซึ่งใช้งานไม่ได้:

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).

มันควรจะค่อนข้างชัดเจนว่าวิธีการbทำงาน: เราแสดงฟองผ่านรายการที่เรียงลำดับ (ซึ่งเป็นการนำ multisets ไปใช้งานง่ายซึ่งทำให้ multisets เท่ากันเพื่อเปรียบเทียบเท่ากัน) และฟองเดียว[]มีจำนวน 1 ด้วยฟองที่มีขนาดใหญ่กว่าที่มีการนับ เท่ากับจำนวนรวมของฟองสบู่ที่อยู่ภายในบวก 1 สำหรับจำนวน 4 โปรแกรมนี้จะ (หากใช้งานได้) จะสร้างรายการต่อไปนี้:

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

โปรแกรมนี้ไม่เหมาะกับคำตอบด้วยเหตุผลหลายประการ แต่สิ่งที่เร่งด่วนที่สุดคือ Prolog ไม่มีภาคmapแสดง (และการเขียนมันจะใช้จำนวนไบต์มากเกินไป) ดังนั้นเราจึงเขียนโปรแกรมแบบนี้มากขึ้น:

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).

ปัญหาสำคัญอื่น ๆ ที่นี่คือมันจะเข้าสู่วงวนไม่สิ้นสุดเมื่อทำงานเนื่องจากวิธีการสั่งซื้อการประเมินผลของ Prolog อย่างไรก็ตามเราสามารถแก้ปัญหาลูปไม่สิ้นสุดโดยจัดเรียงโปรแกรมใหม่เล็กน้อย:

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).

นี้อาจมีลักษณะค่อนข้างแปลก - เรากำลังเพิ่มร่วมกันนับก่อนที่เราจะรู้ว่าสิ่งที่พวกเขา - แต่ GNU อารัมภบทของ#=ความสามารถในการจัดการการจัดเรียงของเลขคณิต noncausal นั้นและเพราะมันเป็นบรรทัดแรกมากbและHeadCountและTailCountทั้งสองจะต้องน้อยกว่าCount(ซึ่งเป็นที่รู้จักกัน) มันทำหน้าที่เป็นวิธีการ จำกัด โดยธรรมชาติกี่ครั้งที่คำ recursive สามารถจับคู่ได้และทำให้โปรแกรมหยุดทำงานเสมอ

ขั้นต่อไปคือการตีกอล์ฟลงเล็กน้อย การลบช่องว่างออกโดยใช้ชื่อตัวแปรอักขระเดียวโดยใช้ตัวย่อเช่น:-for ifและ,for แทนที่จะandใช้(มีชื่อที่สั้นกว่าและให้ผลลัพธ์ที่เหมือนกันในกรณีนี้) และใช้แทน(เพราะไม่ใช่ฟังก์ชันจริง ฉันทำมันขึ้นมา):setoflistofsort0(X,X)is_sorted(X)is_sorted

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).

นี่ค่อนข้างสั้น แต่ก็เป็นไปได้ที่จะทำได้ดีกว่า ความเข้าใจที่สำคัญคือที่[H|T]จริง verbose เป็นรายการไวยากรณ์ไป ดังที่โปรแกรมเมอร์ Lisp รู้แล้วรายชื่อนั้นทำจากเซลล์ข้อเสียซึ่งโดยทั่วไปเป็นเพียงสิ่งอันดับและแทบจะทุกส่วนของโปรแกรมนี้ใช้ลิสต์ในตัว Prolog มีซินแท็กซ์ tuple สั้น ๆ หลายตัว (สิ่งที่ฉันชอบคือA-B, แต่สิ่งที่ฉันชอบอันดับสองคือA/B, ซึ่งฉันใช้ที่นี่เพราะมันให้ผลลัพธ์การดีบักที่อ่านง่ายขึ้นในกรณีนี้) และเรายังสามารถเลือกอักขระเดี่ยวของเราเองnilสำหรับตอนท้ายของรายการแทนที่จะติดกับอักขระสองตัว[](ฉันเลือกxแต่โดยพื้นฐานแล้วอะไรก็ได้ที่ใช้ได้) ดังนั้นแทนที่จะ[H|T]เราสามารถใช้T/Hและรับผลลัพธ์จากb ที่มีลักษณะเช่นนี้ (โปรดทราบว่าลำดับการเรียงใน tuples นั้นแตกต่างจากรายการในรายการเล็กน้อยดังนั้นสิ่งเหล่านี้จะไม่อยู่ในลำดับเดียวกันกับด้านบน):

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)))

นี่เป็นการยากที่จะอ่านมากกว่ารายการที่ซ้อนอยู่ด้านบน ใจข้ามxs และตีความ/()เป็นฟอง (หรือเพียงแค่ธรรมดา/เป็นฟองเสื่อมสภาพที่ไม่มีเนื้อหาถ้าไม่มี()หลังจากนั้น) และองค์ประกอบมีการติดต่อ 1 ต่อ 1 (ถ้ายุ่งเหยิง) กับรายการรุ่นที่แสดงข้างต้น .

แน่นอนว่าการเป็นตัวแทนรายการนี้แม้จะสั้นกว่ามาก แต่ก็มีข้อเสียเปรียบครั้งใหญ่ มันไม่ได้สร้างขึ้นในภาษาดังนั้นเราจึงไม่สามารถใช้sort0เพื่อตรวจสอบว่ารายการของเราเรียงลำดับแล้วหรือไม่ sort0อย่างไรก็ตามการทำด้วยมือไม่ใช่การสูญเสียครั้งใหญ่ (ในความเป็นจริงการทำด้วยมือในการ[H|T]แสดงรายการจะมีจำนวนไบต์เท่ากันทุกประการ) ความเข้าใจที่สำคัญที่นี่คือโปรแกรมที่ตรวจสอบเป็นลายลักษณ์อักษรเพื่อดูว่ารายการจะถูกจัดเรียงถ้าหางของมันจะถูกจัดเรียงถ้าหางของมันจะถูกจัดเรียงและอื่น ๆ ; มีการตรวจสอบซ้ำซ้อนมากมายและเราสามารถใช้ประโยชน์จากสิ่งนั้นได้ แต่เราจะตรวจสอบเพื่อให้แน่ใจว่าองค์ประกอบสองรายการแรกนั้นเป็นไปตามลำดับ (ซึ่งทำให้แน่ใจได้ว่ารายการจะสิ้นสุดเมื่อเรียงลำดับรายการเองและตรวจสอบส่วนต่อท้ายทั้งหมด)

องค์ประกอบแรกสามารถเข้าถึงได้ง่าย Hนั่นเป็นเพียงหัวของรายการ องค์ประกอบที่สองค่อนข้างเข้าถึงได้ยากและอาจไม่มีอยู่จริง โชคดีที่xน้อยกว่าสิ่งอันดับทั้งหมดที่เรากำลังพิจารณา (ผ่านตัวดำเนินการเปรียบเทียบทั่วไปของ Prolog @>=) ดังนั้นเราจึงสามารถพิจารณา "องค์ประกอบที่สอง" ของรายการซิงเกิลตันที่จะเป็นxและโปรแกรมจะทำงานได้ดี สำหรับการเข้าถึงองค์ประกอบที่สองอย่างแท้จริงเมธอด tersest คือการเพิ่มอาร์กิวเมนต์ที่สาม (อาร์กิวเมนต์ออก) ไปbที่ส่งคืนxในกรณีพื้นฐานและHในกรณีซ้ำ นี่หมายความว่าเราสามารถคว้าหัวของหางเป็นเอาท์พุทจากการเรียกซ้ำสองครั้งไปBและแน่นอนว่าหัวของหางนั้นเป็นองค์ประกอบที่สองของรายการ ดังนั้นbดูเหมือนตอนนี้:

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

กรณีพื้นฐานนั้นง่ายพอ (รายการว่างส่งคืนจำนวน 0 ซึ่งเป็น "องค์ประกอบแรก" ของรายการที่ว่างเปล่าx) เคสแบบเรียกซ้ำเริ่มต้นด้วยวิธีเดียวกันกับที่เคยทำมาก่อน (ด้วยT/Hสัญกรณ์มากกว่า[H|T]และHเป็นอาร์กิวเมนต์พิเศษ) เราไม่สนใจอาร์กิวเมนต์พิเศษจากการเรียกซ้ำบนหัว แต่เก็บไว้Jในการเรียกซ้ำบนหาง สิ่งที่เราต้องทำคือให้แน่ใจว่าHมีค่ามากกว่าหรือเท่ากับJ(เช่น "ถ้ารายการมีองค์ประกอบอย่างน้อยสองรายการตัวแรกจะมากกว่าหรือเท่ากับที่สอง) เพื่อให้แน่ใจว่ารายการเรียงลำดับแล้ว

แต่น่าเสียดายที่setofเราไม่ได้ใช้ถ้าเราพยายามที่จะใช้คำจำกัดความก่อนหน้านี้cพร้อมกับคำจำกัดความใหม่ของbมันเพราะมันปฏิบัติต่อพารามิเตอร์ที่ไม่ได้ใช้ในแบบเดียวกับ SQL GROUP BYซึ่งมากกว่าหรือน้อยกว่าที่เราต้องการ เป็นไปได้ที่จะกำหนดค่าใหม่เพื่อทำสิ่งที่เราต้องการ แต่ตัวละครนั้นต้องเสียค่าใช้จ่าย แต่เราใช้findallซึ่งมีพฤติกรรมเริ่มต้นที่สะดวกกว่าและมีความยาวเพียงสองตัวอักษรเท่านั้นทำให้เราได้นิยามc:

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

และนั่นคือโปรแกรมที่สมบูรณ์ สร้างรูปแบบบับเบิ้ลให้แน่นขึ้นจากนั้นใช้จำนวนไบต์ทั้งหมดในการนับ (เราต้องใช้เวลาค่อนข้างนานfindallในการแปลงเครื่องกำเนิดไฟฟ้าไปยังรายการจากนั้นมีชื่อที่โชคร้ายlengthมาก


"Prolog ไม่มีแผนที่แสดงจริง" : Prolog มีภาคmaplist/2-8แสดงแม้ว่าฉันไม่แน่ใจว่าสิ่งนี้จะทำให้สิ่งต่าง ๆ สั้นลงที่นี่
ทำให้เสียชีวิต

@Falize: อืมดูเหมือนว่ามันจะถูกเพิ่มเข้าไปในเวอร์ชั่นที่ใหม่กว่า มันไม่ได้อยู่ในเอกสารประกอบสำหรับการติดตั้งที่ฉันมีและมันใช้งานไม่ได้ในทางปฏิบัติ:| ?- maplist(reverse,[A,B]). uncaught exception: error(existence_error(procedure,maplist/2),top_level/0)

มันแปลกจริงๆ maplistเป็นเพรดิเคตที่ใช้กันอย่างแพร่หลายซึ่งมีให้ในการแจกแจงโปรล็อกหลัก (เช่น SWI-Prolog และ SiCStus)
Fatalize

10

Mathematica, 68 ไบต์

ฉันเดิมพันนี้สามารถตี (แม้ใน Mathematica) กับการดำเนินการตั้งแต่เริ่มต้น แต่นี่เป็นรุ่น builtin:

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

ButcherTreeCountคือ 0 การจัดทำดัชนีจึงและกลับรายการของค่าทั้งหมดขึ้นไปโต้แย้งจึง[#+1] Last@แต่อย่างอื่นมันเป็นเพียง builtin สำหรับฟังก์ชั่นนี้ อย่างไรก็ตามมันต้องโหลดแพคเกจซึ่งเป็นสิ่งที่บรรทัดแรกทำ


8
"แน่นอนว่า Mathematica มีสิ่งปลูกสร้างสำหรับสิ่งนั้น"
orlp
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.