หมุนเพื่อดูทุกด้าน!


10

สมมติว่าคุณมีคนตาย 20 ด้าน คุณเริ่มหมุนแม่พิมพ์นั้นและต้องหมุนสองสามครั้งก่อนที่คุณจะหมุนค่าทั้งหมด 20 คุณสงสัยว่าฉันต้องม้วนจำนวนเท่าไรก่อนที่ฉันจะได้รับโอกาส 50% ในการเห็นค่าทั้งหมด 20 ตัว และnฉันต้องม้วนตายกี่ม้วนก่อนจะกลิ้งทุกnด้าน?

หลังจากการวิจัยบางอย่างคุณพบว่ามีสูตรสำหรับการคำนวณโอกาสในการหมุนnค่าทั้งหมดหลังจากrม้วน

P(r, n) = n! * S(r, n) / n**r

โดยที่S(a, b)หมายถึงตัวเลข Stirling ของชนิดที่สองจำนวนวิธีที่จะแบ่งพาร์ติชันชุดของวัตถุ n (แต่ละม้วน) เป็น k ย่อยที่ไม่ว่างเปล่า (แต่ละด้าน)

คุณยังพบลำดับ OEISที่เราจะเรียกR(n)ซึ่งสอดคล้องกับลำดับที่เล็กที่สุดrที่P(r, n)อย่างน้อย 50% ความท้าทายคือการคำนวณnคำที่ลำดับของลำดับนี้โดยเร็วที่สุด

ความท้าทาย

  • ได้รับnหาที่เล็กที่สุด rที่P(r, n)มากกว่าหรือเท่ากับ0.5หรือ 50%
  • รหัสของคุณในทางทฤษฎีควรจะจัดการกับจำนวนเต็มไม่เป็นลบnเป็น input 1 <= n <= 1000000แต่เราจะได้รับการทดสอบรหัสของคุณในช่วงของ
  • สำหรับการให้คะแนนเราจะใช้เวลาทั้งหมดที่จำเป็นในการทำงานR(n)เกี่ยวกับปัจจัยการผลิตผ่าน110000
  • เราจะตรวจสอบว่าการแก้ปัญหาของคุณถูกต้องโดยใช้รุ่นของเราR(n)ในการส่งออกของคุณเพื่อดูว่าP(your_output, n) >= 0.5และP(your_output - 1, n) < 0.5เช่นว่าการส่งออกของคุณเป็นจริงที่เล็กที่สุดสำหรับที่กำหนดrn
  • คุณสามารถใช้คำจำกัดความใด ๆS(a, b)ในการแก้ปัญหาของคุณ Wikipediaมีคำจำกัดความหลายประการที่อาจเป็นประโยชน์ที่นี่
  • คุณสามารถใช้บิวด์อินในโซลูชันรวมถึงที่คำนวณS(a, b)หรือแม้แต่ที่คำนวณP(r, n)โดยตรง
  • คุณสามารถฮาร์โค้ดได้สูงสุด 1,000 ค่าR(n)และหนึ่งล้านสเตอร์ลิงแม้ว่าจะไม่มีขีด จำกัด เหล่านี้และสามารถเปลี่ยนแปลงได้หากคุณสามารถโต้แย้งเพื่อโน้มน้าวหรือลดระดับ
  • คุณไม่จำเป็นต้องตรวจสอบเป็นไปได้ทุกrระหว่างnและrเรากำลังมองหา แต่คุณไม่จำเป็นที่จะหาที่เล็กที่สุดrและไม่เพียงใดที่rP(r, n) >= 0.5
  • โปรแกรมของคุณต้องใช้ภาษาที่รันได้อย่างอิสระบน Windows 10

i7 4790k, 8 GB RAMข้อกำหนดของคอมพิวเตอร์ที่จะทดสอบการแก้ปัญหาของคุณ ขอบคุณ@DJMcMayhem ที่ให้คอมพิวเตอร์ของเขาสำหรับการทดสอบ อย่าลังเลที่จะเพิ่มเวลาที่ไม่เป็นทางการของคุณเองสำหรับการอ้างอิง แต่จะมีการกำหนดเวลาอย่างเป็นทางการในภายหลังเมื่อดีเจสามารถทดสอบได้

กรณีทดสอบ

n       R(n)
1       1
2       2
3       5
4       7
5       10
6       13
20      67       # our 20-sided die
52      225      # how many cards from a huge uniformly random pile until we get a full deck
100     497
366     2294     # number of people for to get 366 distinct birthdays
1000    7274
2000    15934
5000    44418
10000   95768
100000  1187943
1000000 14182022

แจ้งให้เราทราบหากคุณมีคำถามหรือข้อเสนอแนะ ขอให้โชคดีและการเพิ่มประสิทธิภาพที่ดี!


1
@JanathanAllan รู้ไหมฉันควรจะเลือกใช้ถ้อยคำอื่น ขอบคุณสำหรับหัวขึ้น.
Sherlock9

คำตอบ:


7

Python + NumPy, 3.95 วินาที

from __future__ import division
import numpy as np

def rolls(n):
    if n == 1:
        return 1
    r = n * (np.log(n) - np.log(np.log(2)))
    x = np.log1p(np.arange(n) / -n)
    cx = x.cumsum()
    y = cx[:-1] + cx[-2::-1] - cx[-1]
    while True:
        r0 = np.round(r)
        z = np.exp(y + r0 * x[1:])
        z[::2] *= -1
        r = r0 - (z.sum() + 0.5) / z.dot(x[1:])
        if abs(r - r0) < 0.75:
            return np.ceil(r).astype(int)

for n in [1, 2, 3, 4, 5, 6, 20, 52, 100, 366, 1000, 2000, 5000, 10000, 100000, 1000000]:
    print('R({}) = {}'.format(n, rolls(n)))

import timeit
print('Benchmark: {:.2f}s'.format(timeit.timeit(lambda: sum(map(rolls, range(1, 10001))), number=1)))

ลองออนไลน์!

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

สิ่งนี้ใช้ชุดรูปแบบปิดสำหรับP ( r , n ) และอนุพันธ์ของมันเทียบกับrได้รับการจัดเรียงใหม่เพื่อเสถียรภาพเชิงตัวเลขและ vectorization เพื่อทำการค้นหาวิธีของนิวตันสำหรับrที่P ( r , n ) = 0.5, การปัดเศษrเป็นจำนวนเต็มก่อนแต่ละขั้นตอนจนกว่าขั้นตอนจะย้ายrน้อยกว่า 3/4 ด้วยการคาดเดาเริ่มต้นที่ดีมักใช้การวนซ้ำเพียงหนึ่งหรือสองครั้ง

x i = บันทึก (1 - i / n ) = log (( n - i ) / n ) cx n ) r 1 + ∑ z i = n! ⋅ฉัน + 1) ⋅ (( n - i - 1) /
i = บันทึก ( n ! / (( n - i - 1)) ⋅ n ฉัน + 1 )
y i = cx i + cx n - i - 2 - cx n - 1 = log binom ( n , i + 1)
z i = (-1) i + 1 ⋅ binom ( n ,
S ( r , n ) / n r = P ( r , n )
z ix i + 1 = (-1) i + 1 ⋅ binom ( n , i + 1) ⋅ (( n - i - 1) / n ) r log (( n - i - 1) / n)
z ฉันx i + 1 = d / d r P ( r , n )


1
ทำงานได้ดีในคำตอบทั้งหมด! ก่อนอื่นฉันควรตระหนักว่านั่น0.366512เป็นlogช่วงเวลาที่ผ่านมาแล้ว จะใช้-log(log(2)ในการทำซ้ำครั้งต่อไปของฉัน ประการที่สองแนวคิดในการใช้วิธีการของนิวตันนั้นฉลาดมากและฉันดีใจที่เห็นว่ามันใช้งานได้ดี ประการที่สามฉันเกือบจะขโมยแน่นอนexp(log(binom(n, i+1)) + r * log((n-i-1)/n)): P Kudos ด้วยคำตอบที่ยอดเยี่ยม! : D
Sherlock9

1
ฉันได้เพิ่มช่วงเวลาอย่างเป็นทางการแล้ว! คำตอบที่ดี BTW :)
James

2
ฉันสับสนจริงๆ ฉันเปลี่ยนnumpyการนำเข้าเป็นfrom numpy import *และด้วยเหตุผลบางอย่างเวลาที่ลดลงเป็น 0 โดยทั่วไปลองออนไลน์หรือไม่
notjagan

@notjagan กดปุ่มแคชอาจจะ?
NoOne อยู่ที่นี่

1
ฉันต้องการขอโทษหลายสิ่ง: 1) การคัดลอกผลงานคำตอบของคุณเมื่อฉันพยายามค้นหาการปรับปรุง; 2) ไม่ได้เป็นเจ้าของอย่างถูกต้องและพยายามแก้ไขคำตอบของฉัน; 3) คำขอโทษนี้ใช้เวลานานมาก ฉันรู้สึกละอายใจที่ตอนแรกฉันเพิ่งละทิ้งความท้าทายนี้ ในความพยายามเพียงเล็กน้อยในการชดใช้ฉันคิดว่ามันยุติธรรมที่จะบอกคุณว่าการปรับปรุงหลักของฉันในคำตอบนี้เปลี่ยนจากวิธีของนิวตันเป็นการเพิ่มขึ้นrเนื่องจากการประมาณเริ่มแรกของคุณค่อนข้างดีอยู่แล้ว หวังว่าจะได้พบคุณใน PPCG อีกครั้งและขออภัยในทุกสิ่ง
Sherlock9
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.