คำนวณสัมประสิทธิ์ multinomial


27

ถึงเวลาสำหรับความท้าทายง่ายๆที่ทุกคนสามารถเข้าร่วม!

ทฤษฎีบทพหุนามกล่าวว่า สูตรสำหรับการคำนวณกำลังที่ n ของ Multinomial

การแสดงออกในวงเล็บคือสัมประสิทธิ์พหุนามซึ่งกำหนดเป็น:

สัมประสิทธิ์พหุนาม

การอนุญาตให้เงื่อนไขk i อยู่ในช่วงของพาร์ติชันจำนวนเต็มทั้งหมดของnทำให้ระดับn -th ของm -simplex ของปาสคาล งานของคุณคือการคำนวณค่าสัมประสิทธิ์นี้

งาน

เขียนโปรแกรมหรือฟังก์ชั่นซึ่งใช้ตัวเลขm , n , k 1 , k 2 , ... , k m-1และเอาท์พุทหรือส่งกลับค่าสัมประสิทธิ์พหุนามที่สอดคล้องกัน โปรแกรมของคุณอาจเลือกที่จะใช้mเป็นอาร์กิวเมนต์เพิ่มเติมหากจำเป็น โปรดทราบว่าk mไม่ได้อยู่ในอินพุต

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

  • รูปแบบผลลัพธ์มีความยืดหยุ่นในทำนองเดียวกัน

  • รหัสทั้งหมดควรทำงานในเวลาน้อยกว่าหนึ่งนาทีสำหรับnและmสูงถึง 1,000

  • ไม่ต้องกังวลเกี่ยวกับจำนวนเต็มล้น

  • ไม่สามารถใช้งานบิวด์อินที่ออกแบบมาเพื่อคำนวณสัมประสิทธิ์พหุนาม

  • ช่องโหว่มาตรฐานใช้

เกณฑ์การให้คะแนน

นี่คือรหัสกอล์ฟ: ทางออกที่สั้นที่สุดในการชนะไบต์

กรณีทดสอบ

Input: 3, [2, 0]
Output: 3

Input: 3, [1, 1]
Output: 6

Input: 11, [1, 4, 4]
Output: 34650

Input: 4, [1,2]
Output: 12

Input: 15, [5,4,3,2]
Output: 37837800

Input: 95, [65,4,4]
Output: 1934550571913396675776550070308250

Input: 32, [2,2,2,2,2,2,2,2,2,2,2,2,2,2,2]
Output: 4015057936610313875842560000000

Input: 15, [3,3,3,3]
Output: 168168000

Input: 1000, [10,10,10,10,10,10,10,10,10,10,100,100,100,100,100,100,100,100]
Output: 1892260836114766064839886173072628322819837473493540916521650371620708316292211493005889278395285403318471457333959691477413845818795311980925098433545057962732816261282589926581281484274178579110373517415585990780259179555579119249444675675971136703240347768185200859583936041679096016595989605569764359198616300820217344233610087468418992008471158382363562679752612394898708988062100932765563185864346460326847538659268068471585720069159997090290904151003744735224635733011050421493330583941651019570222984959183118891461330718594645532241449810403071583062752945668937388999711726969103987467123014208575736645381474142475995771446030088717454857668814925642941036383273459178373839445456712918381796599882439216894107889251444932486362309407245949950539480089149687317762667940531452670088934094510294534762190299611806466111882595667632800995865129329156425174586491525505695534290243513946995156554997365435062121633281021210807821617604582625046557789259061566742237246102255343862644466345335421894369143319723958653232683916869615649006682399919540931573841920000000000000

Input: 33, [17]
Output: 1166803110

Input: 55, [28]
Output: 3824345300380220

เราสามารถมีข้อผิดพลาดที่ไม่แน่นอนได้หรือไม่ คือแทนที่จะ1934550571913396675776550070308250เราสามารถส่งออก1.9345505719133966e+33?
Conor O'Brien

@ CᴏɴᴏʀO'Bʀɪᴇɴหากคุณใช้โฟลตแบบ 64 บิตคุณจะไม่สามารถแทนค่าอินพุต[1000 {999 ones}]ได้เลยเพราะเลขชี้กำลังเป็นวิธีที่เกินกว่าจำนวนโฟลตแบบ 64 บิต (128 บิตลอยอาจจะพอเพียง แต่ฉันสมมติว่าคุณต้องการที่จะใช้ชนิดจำนวนพื้นเมืองของ JavaScript?)
มาร์ตินเอนเดอร์

@ MartinBüttnerใช่ว่าเป็นข้อสมมติฐานที่ถูกต้อง
Conor O'Brien

2
@quintopia "ถึงเวลาสำหรับความท้าทายง่ายๆที่ทุกคนสามารถเข้าร่วมได้!" ทุกคนยกเว้นฉัน! (เนื่องจากฉันไม่รู้ว่า Pascals simplex และ multinomials คือ D :) LOL
Ashwin Gupta

@AshwinGupta ไม่ต้องกังวลกับมัน คุณแค่คำนวณการแสดงออกในภาพที่สองแล้วคุณก็ไปได้! 👍
quintopia

คำตอบ:


21

เยลลี่ , 7 6 ไบต์

;_/!:/

ดูสิไม่มี Unicode! โปรแกรมนี้ใช้รายการเดียวเป็นอินพุตโดยมีnที่ดัชนีแรก

ลองออนไลน์! หรือตรวจสอบกรณีทดสอบทั้งหมดในครั้งเดียว

มันทำงานอย่างไร

;_/!:/ Input: A (list)

 _/    Reduce A by subtraction. This subtracts all other elements from the first.
;      Concatenate A with the result to the right.
   !   Apply factorial to all numbers in the resulting list.
    :/ Reduce the result by division. This divides the first element by the others.

นี่เป็นอัลกอริทึมที่ฉันมีอยู่ในใจว่าเรียบง่ายที่สุด
quintopia

9

CJam, 11 ไบต์

l~_:-+:m!:/

ป้อนข้อมูลเป็นรายการเดียวโดยมีรายการnแรก:

[95 65 4 4]

นี้จัดการอินพุตได้ถึง1,000 nและmสวยมากทันที

ทดสอบที่นี่

คำอธิบาย

l~  e# Read a line of input and evaluate it.
_   e# Duplicate.
:-  e# Fold subtraction over the list. A fold is essentially a foreach loop that starts
    e# from the second element. Hence, this subtracts all the k_i from n, giving k_m.
+   e# Append k_m to the list.
:m! e# Compute the factorial of each element in the list.
:/  e# Fold division over the list. Again, this divides n! by each of the k_i!.

ดูเหมือนว่าคุณจะแพ้การแข่งขันแบบนับไบต์ แต่ฉันต้องบอกว่าฉันประทับใจกับความวิกลจริตของ CJam
phord

@phord Well CJam ไม่ตรงกับ Jelly (หรือ Pyth สำหรับเรื่องนั้น) แต่ฉันค่อนข้างประหลาดใจกับตัวเองว่าขนาดกะทัดรัดลงเอยอย่างไร วิธีแก้ปัญหาแรกของฉันมี 21 ไบต์และในขณะที่มันดูไม่ดีที่สุดฉันไม่คิดว่าฉันจะตัดได้ครึ่งหนึ่ง
Martin Ender

4

MATL , 21 15ไบต์

มาใส่ฟังก์ชั่น log-gammaเพื่อการใช้งานที่ดี สิ่งนี้หลีกเลี่ยงการล้นภายในโดยการทำงานกับลอการิทึมของแฟคทอเรียลไม่ใช่กับแฟ็กทอเรียลเอง

1+ZgiO$Gs-h1+Zgs-ZeYo

สามารถใช้งานได้ในเวอร์ชั่นปัจจุบัน (9.2.2)ของภาษา / คอมไพเลอร์ซึ่งเร็วกว่าความท้าทายนี้

อินพุตคือ: ตัวเลขตัวแรกจากนั้นเวกเตอร์ที่เป็นตัวเลข ผลที่ตามมาคือการผลิตเป็นdoubleที่ จำกัด 2^52การส่งออกสูงสุดให้กับบางรอบ

ตัวอย่าง

>> matl 1+ZgiO$Gs-h1+Zgs-ZeYo
> 15
> [5 4 3 2]
37837800

คำอธิบาย

1+       % implicit input (number). Add 1
Zg       % log-gamma function
i        % input (numeric vector).
0$G      % push both inputs
s-       % sum the second input (vector) and subtract from first
h1+      % append to vector. Add 1
Zg       % log-gamma function, element-wise on extended vector
s        % sum of results
-        % subtract from previous result of log-gamma
Ze       % exponential
Yo       % round. Implicit display

4
ลองออนไลน์! ขณะนี้มีการสนับสนุนMATLทดลอง: matl.tryitonline.net/…ยินดีรับข้อเสนอแนะ
เดนนิส

1
@ เดนนิสเฮ้! ช่างน่าประหลาดใจ!!! ฉันจะขอบคุณได้อย่างไร? ฉันมีข้อเสนอแนะ: ถ้าคุณเคยมาที่มาดริดฉันเป็นหนี้คุณเป็นอาหารมื้อเย็นที่ดีและเครื่องดื่มบางอย่าง
Luis Mendo

ฉันรู้สึกขอบคุณจริงๆ มันยอดเยี่ยมมากที่มีมันออนไลน์ เราจะจัดการแก้ไขอย่างไร ฉันยังคงปรับปรุงอย่างต่อเนื่องภาษาที่คุณรู้ว่า ...
หลุยส์ Mendo

สำหรับตอนนี้ฉันกำลังอัปเดตล่ามด้วยตนเอง หากคุณอัปเดตเพียงแค่ ping ฉันใน The Nineteenth Byte และฉันจะดึงมันโดยเร็ว - ฉันจะต้องไปที่มาดริดในอนาคตอันใกล้ดังนั้นฉันจะจำข้อเสนอของคุณ ;)
Dennis

@Dennis Great! ด้วยวิธีนี้เราสามารถพบกันในคน!
Luis Mendo

4

PowerShell, 91 74 ไบต์

แอ่ว! คำตอบที่ 100 ของฉันใน PPCG!

param($n,$k)(1..$n-join'*'|iex)/(($k|%{$n-=$_;1..$_})+(1..$n)-join'*'|iex)

ต๊าย จะไม่ชนะรหัสที่สั้นที่สุดแน่นอน แม้ว่าจะใช้อุบายเรียบร้อยคู่กับพิสัย และนี่อาจเป็นความหมายที่สมบูรณ์สำหรับทุกคนที่ไม่คุ้นเคยกับ PowerShell

คำอธิบาย

ครั้งแรกที่เราจะใช้การป้อนข้อมูลด้วยparam($n,$k)และคาดว่าจะเป็นอาร์เรย์เช่น$k.\compute-the-multinomial-coefficient.ps1 11 @(1,4,4)

เราจะเริ่มด้วยตัวเศษ (ทุกอย่างทางด้านซ้าย/) นั่นเป็นเพียงช่วง1..$nที่ได้รับการ-joinแก้ไขพร้อมกับการ*ประเมินแล้วiexเพื่อคำนวณแฟคทอเรียล (เช่น, 1*2*3*...*$n)

ต่อไปเราวนซ้ำ$k|%{...}และการวนซ้ำแต่ละครั้งที่เราลบค่าปัจจุบัน$_จาก$n(ซึ่งเราไม่สนใจอีกต่อไป) เพื่อกำหนดใน$k_mภายหลัง นอกจากนี้เรายังสร้างช่วง1..$k_iการวนซ้ำแต่ละครั้งซึ่งจะถูกทิ้งไว้บนไปป์ไลน์ วัตถุท่อผู้ที่ได้รับการตัดแบ่งอาร์เรย์มีการแสดงออกที่สองช่วง1..$n(ซึ่งเป็น$k_mที่จุดนี้) ในที่สุดทั้งหมดนั้นก็จะถูก-joinรวมเข้ากับ*และประเมินผลiexคล้ายกับตัวเศษ (ซึ่งใช้ได้เพราะx! * y! = 1*2*3*...*x * 1*2*3*...*yเราไม่สนใจการสั่งซื้อรายบุคคล)

ในที่สุด/เหตุการณ์ที่เกิดขึ้นตัวเศษจะถูกหารด้วยตัวส่วนและเอาท์พุท

จัดการเอาต์พุตอย่างถูกต้องสำหรับตัวเลขที่มีขนาดใหญ่กว่าเนื่องจากเราไม่ได้ทำการคัดเลือกตัวแปรใด ๆ อย่างชัดเจนเป็นประเภทข้อมูลใด ๆ ดังนั้น PowerShell จะทำการส่งข้อมูลประเภทต่าง ๆ ในทันทีตามที่ต้องการ สำหรับตัวเลขที่มีขนาดใหญ่กว่าผลลัพธ์ที่ออกมาผ่านสัญกรณ์ทางวิทยาศาสตร์เพื่อการรักษาตัวเลขที่มีนัยสำคัญได้ดีที่สุดเนื่องจากประเภทข้อมูลได้รับการส่งซ้ำอีกครั้ง ยกตัวอย่างเช่นออกจะ.\compute-the-multinomial-coefficient.ps1 55 @(28) 3.82434530038022E+15ฉันคิดว่าสิ่งนี้จะตกลงเนื่องจาก"รูปแบบเอาต์พุตมีความยืดหยุ่นคล้ายกัน"ถูกระบุในความท้าทายและความคิดเห็นของ quintopia "หากผลลัพธ์สุดท้ายสามารถพอดีกับประเภทจำนวนเต็มที่ได้รับการสนับสนุนแบบดั้งเดิมผลลัพธ์จะต้องแม่นยำหากไม่สามารถมี ไม่มีข้อ จำกัด เกี่ยวกับสิ่งที่อาจเป็นผลลัพธ์ "


อีกทางเลือกหนึ่ง

ขึ้นอยู่กับการตัดสินใจในการจัดรูปแบบผลลัพธ์ต่อไปนี้ที่92 ไบต์

param($n,$k)((1..$n-join'*'|iex)/(($k|%{$n-=$_;1..$_})+(1..$n)-join'*'|iex)).ToString('G17')

ซึ่งเหมือนกับข้างต้นเพียงใช้การจัดรูปแบบผลลัพธ์ที่ชัดเจนด้วย.ToString('G17')เพื่อให้ได้ตัวเลขตามที่ต้องการ สำหรับ55 @(28)สิ่งนี้จะเอาท์พุท3824345300380220.5


Edit1 - บันทึก 17 bytes โดยกำจัด$dและเพียงแค่คำนวณโดยตรงและกำจัดการคำนวณ$k_mโดย stringing มันในขณะที่เราวนรอบ$k
Edit2 - เพิ่มรุ่นอื่นที่มีการจัดรูปแบบที่ชัดเจน


3

APL (Dyalog Extended)ขนาด 9 ไบต์

×/2!/+\⍛,

ลองออนไลน์!

การใช้ความคิดจากคำตอบ APL ของฉันในความท้าทายอื่นที่เกี่ยวข้องกับมัลติมิโน

ฟังก์ชั่นโดยปริยายที่มีอาร์กิวเมนต์ซ้ายคือรายการของ k และอาร์กิวเมนต์ที่ถูกต้องคือ n กรณีทดสอบตรวจสอบว่าเห็นด้วยกับวิธีแก้ปัญหาของอดัมด้วยการโต้แย้งซ้ายและขวาพลิก

มันทำงานอย่างไร

×/2!/+\⍛,
     +\     Cumulative sum of k's (up to m-1'th element)
       ⍛,   Append n (sum of k_1 to k_m)
  2!/       Binomial of consecutive pairs
×/          Product

(k1+k2++kม.)!k1!k2!kม.!=(k1+k2)!k1!k2!×(k1+k2++kม.)!(k1+k2)!k3!kม.!

=(k1+k2)!k1!k2!×(k1+k2+k3)!(k1+k2)!k3!×(k1+k2++kม.)!(k1+k2+k3)!kม.!

==(k1+k2k1)(k1+k2+k3k1+k2)(k1++kม.k1++kม.-1)


2

Mathematica ขนาด 26 ไบต์

#!/Times@@({#-+##2,##2}!)&

ตัวอย่าง:

In[1]:= #!/Times@@({#-+##2,##2}!)&[95,65,4,4]

Out[1]= 1934550571913396675776550070308250

2

Python 3, 93 91

ขอขอบคุณที่เดนนิสและFryAmTheEggman

f=lambda x:0**x or x*f(x-1)
def g(n,k):
    r=f(n)
    for i in k:r//=f(i)
    return r//f(n-sum(k))

nเป็นจำนวนเต็มสามารถทำkซ้ำได้

Ungolfed:

import functools #cache

@functools.lru_cache(maxsize=None) #cache results to speed up calculations
def factorial(x):
    if x <= 1: return 1
    else: return x * factorial(x-1)

def multinomial(n, k):
    ret = factorial(n)
    for i in k: ret //= factorial(i)
    km = n - sum(k)
    return ret//factorial(km)

1
คุณสามารถใช้ช่องว่างเดียวแทนที่จะเป็นสี่สำหรับบิตช่องว่างแบบไดนามิก
Conor O'Brien

ฉันใช้แท็บพวกเขาถูกแทนที่ในโพสต์นี้ จำนวนไบต์น่าจะตกลง ฉันไม่แน่ใจเกี่ยวกับผลการลอยและการไหลล้นที่เป็นไปได้
ตรัง Oul

2
1. 95, [65, 4, 4]ผลิตไม่ถูกต้องสำหรับ โปรดทราบว่าการป้อนข้อมูลที่ไม่ได้มีk_m 2. คุณดูเหมือนจะไม่ได้ใช้from functools import*งานเลย
Dennis

2
1. reduceรหัสแข็งแรงเล่นกอล์ฟของคุณไม่ใช้ 2. import math;f=math.factorialบันทึกไบต์ 3. งูหลาม 2 จะช่วยให้คุณได้รับการกำจัดที่สองใน/ //
เดนนิส

1
กำหนดfด้วยตัวคุณเองจะช่วยประหยัดไบต์บางส่วนf=lambda x:0**x or x*f(x-1) :
FryAmTheEggman

2

APL (Dyalog Unicode) , 16 ไบต์SBCS

ทั้งหมดขึ้นอยู่กับทักษะทางคณิตศาสตร์ของเพื่อนร่วมงานของฉันมาร์แชลล์

ฟังก์ชั่นมัดไม่ระบุชื่อ รับkเป็นอาร์กิวเมนต์ที่ถูกต้องและnเป็นอาร์กิวเมนต์ซ้าย

{×/⍵!⍺-+10,⍵}

ลองออนไลน์!

{} แลมบ์ดานิรนาม เป็นอาร์กิวเมนต์ที่เหลือ ( n ) และเป็นอาร์กิวเมนต์ที่ถูกต้อง ( k )

0,⍵ เติมศูนย์ถึงk

¯1↓ ปล่อยรายการสุดท้ายจากนั้น

+\ ผลรวมสะสมของ

⍺- ลบออกจากn

⍵! ( k )

×/ ผลิตภัณฑ์ของที่


1

PARI / GP, 43 ไบต์

ตรงไปตรงมาสวย; นอกเหนือจากการจัดรูปแบบแล้วเวอร์ชันที่ไม่ดีอาจจะเหมือนกัน

m(n,v)=n!/prod(i=1,#v,v[i]!)/(n-vecsum(v))!

1

Matlab 48 ไบต์

คุณจะต้องตั้งformatไปlongล่วงหน้าเพื่อให้ได้ความแม่นยำสูง จากนั้นมันค่อนข้างตรงไปตรงมา:

@(n,k)factorial(n)/prod(factorial([k,n-sum(k)]))

ans(95, [65,4,4])
ans =

 1.934550571913395e+33

1

Pyth, 10 ไบต์

/F.!MaQ-FQ

ลองใช้งานออนไลน์: การสาธิต

คำอธิบาย:

/F.!MaQ-FQ   implicit: Q = input list
       -FQ   reduce Q by subtraction
     aQ      append the result to Q
  .!M        compute the factorial for each number
/F           reduce by division

1

J, 16 ไบต์

[(%*/)&:!],(-+/)

การใช้

สำหรับค่าที่มากขึ้นคำต่อท้ายของxจะใช้เพื่อแสดงถึงจำนวนเต็มความแม่นยำแบบขยาย

   f =: [(%*/)&:!],(-+/)
   11 f 1 4 4
34650
   15x f 5 4 3 2
37837800

คำอธิบาย

[(%*/)&:!],(-+/)  Input: n on LHS, A on RHS
             +/   Reduce A using addition
            -     Subtract that sum from n, this is the missing term
         ]        Get A
          ,       Append the missing term to A to make A'
[                 Get n
      &:!         Take the factorial of n and each value in A'
   */             Reduce using multiplication the factorials of A'
  %               Divide n! by that product and return

1

05AB1E , 8 ไบต์

Ƹ«!R.«÷

ลองออนไลน์! คำอธิบาย:

Æ           Subtract all the elements from the first
 ¸«         Append to the original list
   !        Take the factorial of all the elements
    R.«÷    Reduce by integer division

ฉันไม่สามารถหาวิธีที่ดีกว่าในการปฏิบัติตามขั้นตอนที่ 2 หรือขั้นตอนที่ 4


1

APL (Dyalog Unicode)ขนาด 17 ไบต์

⊢∘!÷(×/∘!⊣,⊢-1⊥⊣)

ลองออนไลน์!

ฟังก์ชั่นมัดเงียบ (ขอบคุณ @ Adámสำหรับ 2 ไบต์ที่บันทึกไว้)


APL (Dyalog Unicode)ขนาด 19 ไบต์

{(!⍺)÷×/!(⍺-+/⍵),⍵}

ลองออนไลน์!

ใส่กุญแจ

ฟังก์ชันทั้งสองข้างต้นคำนวณสูตรที่กำหนด



0

Clojure 70 ไบต์

#(let[a apply](a /(map(fn[x](a *(map inc(range x))))(conj %(a - %)))))

สร้างฟังก์ชั่นที่ไม่ระบุชื่อโดยรับอาร์กิวเมนต์ทั้งหมดเป็นรายการเดียวโดยnเริ่มแรก

30 ตัวอักษรเป็น "ที่สูญเปล่า" เพียงกำหนดฟังก์ชั่นแฟคทอเรียล โอ้ดี


0

Perl 6 ,  52  50 ไบต์

->\n,\k{[*](1..n)div[*] ([*] 1..$_ for |k,[-] n,|k)}

ทดสอบมัน

->\n,\k{[*](1..n)/[*] ([*] 1..$_ for |k,[-] n,|k)}

ทดสอบมัน (ผลลัพธ์คือ Rational พร้อมตัวส่วนเป็น 1)

ขยาย:

->     # pointy block lambda
  \n,
  \k
{
    [*]( 1 .. n )   # factorial of 「n」

  /                 # divide (produces Rational)

    [*]             # reduce the following using &infix:«*»

      (
          [*] 1..$_ # the factorial of

        for         # each of the following

          |k,       # the values of 「k」 (slipped into list)
          [-] n,|k  # 「n」 minus the values in 「k」
      )
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.