สุ่มกอล์ฟประจำวัน # 3: ฉากกั้นระหว่างฉาก


19

เกี่ยวกับซีรี่ส์

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

แม้ว่าฉันจะมีไอเดียมากมายสำหรับชุด แต่ความท้าทายในอนาคตยังไม่เกิดขึ้น หากคุณมีข้อเสนอแนะใด ๆ โปรดแจ้งให้เราทราบเกี่ยวกับการโพสต์ Sandbox ที่เกี่ยวข้อง

Hole 3: Integer Partitions

ใช้เวลาในการเพิ่มความยากลำบากเล็กน้อย

พาร์ทิชันของจำนวนเต็มบวกnถูกกำหนดให้เป็น MultiSet nของจำนวนเต็มบวกซึ่งรวมไป ตัวอย่างเช่นถ้าn = 5พาร์ติชันต่อไปนี้มีอยู่:

{1,1,1,1,1}
{2,1,1,1}
{2,2,1}
{3,1,1}
{3,2}
{4,1}
{5}

หมายเหตุว่าเหล่านี้เป็นมัลติจึงมีคำสั่งไม่ให้พวกเขา{3,1,1}, {1,3,1}และ{1,1,3}ได้รับการพิจารณาทั้งหมดเหมือนกัน

งานของคุณจะได้รับการสร้างพาร์ทิชันแบบสุ่มของn nนี่คือกฎรายละเอียด:

  • การกระจายตัวของพาร์ทิชันที่ผลิตจะต้องมีเครื่องแบบ นั่นคือในตัวอย่างข้างต้นแต่ละพาร์ติชันควรส่งคืนด้วยความน่าจะเป็น 1/7

    แน่นอนเนื่องจากข้อ จำกัด ทางเทคนิคของ PRNGs ความสม่ำเสมอที่สมบูรณ์แบบจะเป็นไปไม่ได้ เพื่อวัตถุประสงค์ในการประเมินความสม่ำเสมอของการส่งของคุณการดำเนินการต่อไปนี้จะถือว่าเป็นการให้การแจกแจงที่สมบูรณ์แบบ:

    • การรับตัวเลขจาก PRNG (มากกว่าช่วงใด ๆ ) ซึ่งได้รับการบันทึกไว้ว่าเป็นชุด (โดยประมาณ)
    • การแม็พการกระจายแบบสม่ำเสมอบนชุดของตัวเลขที่มีขนาดใหญ่ลงบนเซตที่เล็กกว่าผ่านโมดูโลหรือการคูณ (หรือการดำเนินการอื่น ๆ ที่กระจายค่าอย่างสม่ำเสมอ) ชุดที่ใหญ่กว่าจะต้องมีค่าที่เป็นไปได้อย่างน้อย 1024 เท่าของชุดที่เล็กกว่า
  • เนื่องจากพาร์ติชั่นนั้นเป็นหลายชุดคุณอาจส่งคืนพาร์ติชั่นเหล่านั้นในลำดับใดก็ได้และคำสั่งนี้ไม่จำเป็นต้องสอดคล้องกัน อย่างไรก็ตามสำหรับจุดประสงค์ของการแจกแจงแบบสุ่มคำสั่งนั้นจะถูกละเว้น นั่นคือในตัวอย่างข้างต้น{3,1,1}, {1,3,1}และ{1,1,3} ร่วมกันต้องมีความน่าจะเป็น 1/7 ของการถูกส่งกลับ

  • อัลกอริทึมของคุณต้องมีรันไทม์ที่กำหนด nโดยเฉพาะอย่างยิ่งคุณไม่สามารถสร้างมัลติสุ่มและปฏิเสธพวกเขาหากพวกเขาไม่ได้สรุปผลการ
  • nเวลาซับซ้อนขั้นตอนวิธีการของคุณจะต้องเป็นพหุนามใน โดยเฉพาะอย่างยิ่งคุณไม่สามารถสร้างพาร์ติชันทั้งหมดและเลือกพาร์ติชันแบบสุ่มได้ (เนื่องจากจำนวนพาร์ติชันเพิ่มขึ้นแบบทวีคูณด้วยn) คุณอาจสมมติว่า PRNG ที่คุณใช้อยู่สามารถส่งคืนค่าที่กระจายแบบสม่ำเสมอใน O (1) ต่อค่า
  • คุณต้องไม่ใช้ฟังก์ชันในตัวที่สามารถแก้ไขงานนี้ได้

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

คุณอาจคิดว่าn ≤ 65(เช่นจำนวนพาร์ติชันน้อยกว่า 2 21 ) ผลลัพธ์อาจอยู่ในรูปแบบรายการหรือรูปแบบสตริงที่สะดวกและชัดเจน

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

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

ลีดเดอร์บอร์ด

โพสต์แรกของซีรีส์สร้างกระดานผู้นำ

เพื่อให้แน่ใจว่าคำตอบของคุณปรากฏขึ้นโปรดเริ่มต้นทุกคำตอบด้วยบรรทัดแรกโดยใช้เทมเพลต Markdown ต่อไปนี้:

# Language Name, N bytes

ที่Nมีขนาดของส่งของคุณ หากคุณปรับปรุงคะแนนของคุณคุณสามารถเก็บคะแนนเก่าไว้ในพาดหัว ตัวอย่างเช่น

# Ruby, <s>104</s> <s>101</s> 96 bytes

(ภาษาไม่ปรากฏในปัจจุบัน แต่ตัวอย่างข้อมูลต้องการและแยกวิเคราะห์และฉันอาจเพิ่มกระดานผู้นำแบบแยกภาษาในอนาคต)

คำตอบ:


8

Python 2, 179 ไบต์

from random import*
m=r=input();i=q=r+1;h=[1]+[0]*q*q;exec"h[i]=h[i+~q]+h[i-i%q*q];i+=1;"*r*q
while r:
 x=random()*sum(h[r*q:r*q-~m]);m=0
 while x>0:m+=1;x-=h[r*q+m]
 print m;r-=m

ฉันใช้สูตร (39) จากสารสกัด Knuth นี้ซึ่งให้จำนวนพาร์ติชั่นของชิ้นnนั้นที่มีmชิ้นส่วนอย่างแน่นอน สิ่งนี้เกิดขึ้นเท่ากับจำนวนพาร์ติชันnที่มีmเป็นองค์ประกอบสูงสุดซึ่งเป็นการตีความที่ฉันใช้ องค์ประกอบของพาร์ติชันถูกสร้างจากมากไปน้อย ในแต่ละขั้นตอนสูตรจะถูกนำมาใช้ซ้ำกับส่วนที่เหลือปัจจุบันnและองค์ประกอบที่อนุญาตสูงสุด


5

Dyalog APL, 67 59 51 ไบต์

p←{⍵,⊂1,⍨+/¨⌽⍵↑¨⍨⌽⍳⍴⍵}⍣⎕⊢⍬⋄f←{⍵=0:⍬⋄a,a∇⍵-a←{1++/(?+/⍵)>+\⍵}⍺↑⍵⊃p}⍨ (67 ไบต์)

pเป็นเวกเตอร์ของเวกเตอร์ซึ่งp[n][k]เป็นจำนวนของพาร์ทิชันของnเข้าksummands kหรือเท่าจำนวนของพาร์ทิชันที่มีตัวตั้งที่ยิ่งใหญ่ที่สุด เราสร้างpโดยเริ่มจากเวกเตอร์เปล่าการอ่านn( อินพุตอ่าน) และใช้สิ่งต่อไปนี้ซ้ำ ๆ :

{⍵,⊂1,⍨+/¨⌽⍵↑¨⍨⌽⍳⍴⍵}
                 ⍴⍵   ⍝ the current length, initially 0
                ⍳⍴⍵   ⍝ 1 2 ... length
               ⌽⍳⍴⍵   ⍝ length ... 2 1
           ⍵↑¨⍨       ⍝ take length elements from p[1], length-1 from p[2], etc
                      ⍝ padded with 0-s, e.g. if p was (,1)(1 1)(1 1 1)(1 2 1 1)(1 2 2 1 1):
                      ⍝ we get:     (1 0 0 0 0)(1 1 0 0)(1 1 1)(1 2)(,1)
          ⌽           ⍝ reverse it: (,1)(1 2)(1 1 1)(1 1 0 0)(1 0 0 0 0)
       +/¨            ⍝ sum each:   1 3 3 2 1
    1,⍨               ⍝ append 1:   1 3 3 2 1 1
 ⍵,⊂                  ⍝ append the above to the vector of vectors

หลังจากnการใช้งาน ( ⍣⎕) pเราได้สร้างขึ้น

fเลือกพาร์ติชันแบบสุ่ม n f kเป็นพาร์ทิชันแบบสุ่มของที่มากที่สุด k summands เป็นf nn f n

{⍵=0:⍬⋄a,a∇⍵-a←{1++/(?+/⍵)>+\⍵}⍺↑⍵⊃p}⍨
                                     ⍨ ⍝ "selfie" -- use n as k if no k is provided
 ⍵=0:⍬                                 ⍝ if n=0 return empty
                                 ⍵⊃p   ⍝ pick the n-th element of p
                               ⍺↑      ⍝ take k elements from that
               {1++/(?+/⍵)>+\⍵}        ⍝ use them as weights to pick a random number 1...k
               {           +\⍵}        ⍝   partial sums of weights
               {    (?+/⍵)    }        ⍝   a random number 1...sum of weights
               {    (?+/⍵)>+\⍵}        ⍝   which partial sums is it greater than?
               {  +/          }        ⍝   count how many "greater than"-s
               {1+            }        ⍝   we're off by one
             a←                        ⍝ this will be the greatest number in our partition
         a∇⍵-a                         ⍝ recur with n1=n-a and k1=a
       a,                              ⍝ prepend a

การปรับปรุงบางอย่าง:

  • แบบอินไลน์pที่ราคาแย่ลงเล็กน้อย (แต่ก็ยังดีพอ)

  • ในการคำนวณของ pจัดเรียงใหม่และ1,เพื่อบันทึกอักขระ

  • กลับ {1++/(?+/⍵)>+\⍵}เป็นรถไฟ1+ด้านหน้า1+(+/(?+/)>+\)

  • ทำให้fฟังก์ชั่นและอุปทานที่ไม่ระบุชื่อ (อินพุต eval'ed) เป็นอาร์กิวเมนต์เพื่อรับโปรแกรมที่สมบูรณ์

{⍵=0:⍬⋄a,a∇⍵-a←1+(+/(?+/)>+\)⍺↑⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬}⍨⎕ (59 ไบต์)

ทดสอบกับ n = 5

ทดสอบกับ n = 65

และลิงค์ต่อไปนี้จะทำงาน n = 5 พันครั้งและรวบรวมสถิติเกี่ยวกับความถี่ของแต่ละพาร์ติชัน: ⎕rl←0 ⋄ {⍺,⍴⍵}⌸ {⍵=0:⍬⋄a,a∇⍵-a←1+(+/(?+/)>+\)⍺↑⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬}⍨ ¨10000⍴5


การปรับปรุงเพิ่มเติมด้วยความช่วยเหลือจากRoger Hui :

  • แทนที่ด้วย{⍵=0:A⋄B} {×⍵:B⋄A}Signum ( ×⍵) ผลตอบแทนจริงสำหรับและเท็จ⍵>0⍵=0

  • แทนที่(+/(?+/)>+\)ด้วย+/b<?⊃⌽b←+\มันจะบันทึกตัวละคร

  • ใช้เมทริกซ์แทนเวกเตอร์ของเวกเตอร์เพื่อคำนวณp: แทนที่ด้วย⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬⊃↓(0,⍨⊢⍪⍨1 1⍉+\)⍣⍵⍪1

{×⍵:a,a∇⍵-a←1++/b<?⊃⌽b←+\⍺↑⊃↓(0,⍨⊢⍪⍨1 1⍉+\)⍣⍵⍪1⋄⍬}⍨ (51 ไบต์)

ทดสอบ n = 5 ; ทดสอบ n = 65 ; สถิติ freq


2
ใครจะได้รับความช่วยเหลือจาก Roger Hui
FUZxxl

5
เขียนล่าม APL ของเล่นเพื่อรับตัวคุณเองใน บริษัท เดียวกันกับเขา ก่อให้เกิดการแสดงออกข้างต้นเป็นความท้าทายสัญญาเบียร์ไพน์สำหรับตัวละครทุกตัวที่เขาออก จากนั้นกำไร: ตัวละครน้อยลงและเหล้ามากขึ้นเนื่องจากเขาไม่ดื่มเบียร์
ngn

1
ฉันเห็น. นั่นเป็นกลยุทธ์ที่เรียบร้อยเรามาดูกันว่าฉันจะทำซ้ำได้ไหม…คุณถามเขาได้ไหมว่า Dyalog APL กำลังจะได้รับอะไรบางอย่างที่เหมือนกับ J ในu/\. yไม่ช้านี้ไหม?
FUZxxl

สำหรับบันทึก: twitter.com/FUZxxl/status/572377068555644929
ngn

ขอบคุณที่ถามเขา ตอนนี้ฉันสงสัยว่ามันเป็นไปได้ในเวลาเชิงเส้นด้วย
FUZxxl

4

GolfScript, 90 ไบต์

~[[[1.]]]\({..[[{{(\{)}%+}%1$,1$,-=}%[1,]@0=+{1+}%]zip{{(\.,/*~}%.,.rand@=+}:^%]\+}*0=^(;`

การสาธิตออนไลน์

นี่คือการปรับรหัสการนับพาร์ติชันของฉัน (ง่ายกว่า)ซึ่งแทนที่จะเพียงแค่ติดตามการนับการติดตามทั้งการนับและการเลือกหนึ่งในองค์ประกอบที่นับอย่างสม่ำเสมอ

การเปรียบเทียบทั้งสองแบบเคียงข้างกัน:

~[[[1.]]]\({..[[{{(\{)}%+}%1$,1$,-=}%[1,]@0=+{1+}%]zip{{(\.,/*~}%.,.rand@=+}:^%]\+}*0=^(;`
 [[ 1  ]]\({..[[{          1$,1$,-=}%  0 @0=+     ]zip{{+}*                }:^%]\+}*0=^

แตกต่าง:

  • เริ่มต้น~เป็นเพราะนี่เป็นโปรแกรมแทนที่จะเป็นข้อมูลโค้ด
  • การ[1.]แทนที่1สอดคล้องกับการเปลี่ยนแปลงในสิ่งที่ถูกติดตาม
  • การ{(\{)}%+}%เพิ่มเพิ่มเติมแต่ละองค์ประกอบในพาร์ติชันนั้นและการ{1+}%เพิ่ม1ไปยังพาร์ติชัน
  • 0กลายเป็น[0](golfed ถึง1,) เป็นส่วนหนึ่งของการเปลี่ยนแปลงในสิ่งที่ถูกติดตาม แต่เนื่องจากมันจำเป็นต้องคงอาร์เรย์เมื่อผนวกกับอีกอันหนึ่งจึงต้องการส่วนเพิ่มเติม[ ]แต่เพราะความต้องการที่จะยังคงอาร์เรย์เมื่อใช้ได้กับอีกคนหนึ่งก็ต้องพิเศษ
  • ผลรวมที่เรียบง่าย {+}*จะกลายเป็นการเลือกที่มีน้ำหนักจากพาร์ติชันรวมกับการรวมจำนวนของพวกเขา
  • การ(;`ลบการนับจากผลลัพธ์และทำให้พาร์ติชันเป็นรูปแบบที่ดี

กรอบการทดสอบ

;7000,{;
  '5'

  ~[[[1.]]]\({..[[{{(\{)}%+}%1$,1$,-=}%[1,]@0=+{1+}%]zip{{(\.,/*~}%.,.rand@=+}:^%]\+}*0=^(;`

}%
:RESULTS
.&${
  RESULTS.[2$]--,' '\n
}/

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


3

Java, 285 267 ไบต์

int[][]p;void p(int n){p=new int[n+1][n+1];int a=n,b=k(n,a),c,d;for(b*=Math.random();n>0;System.out.print(c+" "),n-=a=c)for(c=0;c++<(a<n?a:n)&b>=(d=k(n-c,c));b-=d);}int k(int n,int k){if(p[n][k]<1)for(int a=0,b=0;b<k&b++<n;p[n][k]=a)a+=k(n-b,b);return n>0?p[n][k]:1;}

นี่เป็นวิธีการเดียวกับคำตอบของ TheBestOne แต่จะใช้อาเรย์อย่างง่ายแทนแผนที่ นอกจากนี้แทนที่จะส่งคืนพาร์ติชันแบบสุ่มเป็นListจะพิมพ์เหล่านั้นไปยังคอนโซล

ด้านล่างนี้เป็นโปรแกรมทดสอบที่ใช้งานได้ 100,000 ครั้ง สำหรับตัวอย่างn=5ชุดทั้งหมดอยู่ในระยะ 0.64% จาก 1/7 ที่สมบูรณ์แบบในการวิ่งครั้งสุดท้ายของฉัน

public class Partition {
    public static void main(String[] args) {
        Partition p = new Partition();
        for(int i=0;i<100000;i++){
            p.p(5);
            System.out.println();
        }
    }

    int[][]p;

    void p(int n){
        p=new int[n+1][n+1];
        int a=n,b=k(n,a),c,d;
        for(b*=Math.random();n>0;System.out.print(c+" "),n-=a=c)
            for(c=0;c++<(a<n?a:n)&b>=(d=k(n-c,c));b-=d);
    }

    int k(int n,int k){
        if(p[n][k]<1)
            for(int a=0,b=0;b<k&b++<n;p[n][k]=a)
                a+=k(n-b,b);
        return n>0?p[n][k]:1;
    }

}

3
แม้ว่าคุณจะได้แข็งแรงเล่นกอล์ฟMath.minโทรลงไป(k<n?k:n)คุณสามารถไปต่อไปโดย ditching b<k&b++<nมันทั้งหมดและเพียงแค่การทำสองการตรวจสอบ: นอกจากนี้คุณยังสามารถทิ้งn>0ส่วนของเงื่อนไขลูปได้อย่างง่ายดาย(เนื่องจากn>0&b<nจะลดลงb<nเมื่อbรับประกันว่าไม่เป็นลบ)
Peter Taylor

@ PeterTaylor ขอบคุณ ลองดูอีกครั้งให้ฉันกำจัดข้อความสั่งคืนพิเศษและintประกาศแยกต่างหากด้วย
Geobits

3

CJam, 64 56 ไบต์

ri_L{_0>{\,f{)_@1$-j+}{)@)2$+:Umr@<@@?U+}*}{!a\;}?}2j);p

คุณสามารถทดสอบด้วยสคริปต์นี้:

ria100*{_L{_0>{\,f{)_@1$-j+}{)@)2$+:Umr@<@@?U+}*}{!a\;}?}2j);}%__|\f{_,\2$a-,-}2/p

คำอธิบาย

ri_                  " Read an integer and duplicate. ";
L{                   " Create a memoized function of the maximum and the sum, which returns
                       a random partition, and the total number of partitions as the last item. ";
    _0>              " If sum > 0: ";
    {
        \,f{         " For I in 0..max-1: ";
            )_@1$-   " Stack: I+1 I+1 sum-I-1 ";
            j+       " Recursively call with the two parameters, and prepend I+1. ";
        }
        {            " Reduce on the results: ";
            )@)2$+   " Stack: partition1 total1 partition2 total1+total2 ";
            :Umr     " U = total1+total2, then generate a random number smaller than that. ";
            @<@@?    " If it is <total1, choose partition1, else choose partition2. ";
            U+       " Append the total back to the array. ";
        }*
    }
    {!a\;}?          " Else return [0] if negative, or [1] if zero. ";
}2j
);p                  " Discard the total and print. ";

2
คุณควรลบไม่ถูกต้อง "ไม่แข็งแรงเล่นกอล์ฟได้เป็นอย่างดี" ส่วนหนึ่งของคำตอบของคุณ;)
anatolyg

@anatolyg ถูกลบแล้ว แต่ฉันเชื่อว่ายังคงเป็นไปได้ที่จะลบบางไบต์ ฉันแค่ขี้เกียจเกินไปที่จะทำเช่นนั้น
jimmy23013

3

Pyth, 64 ไบต์

ใช้/programming//a/2163753/4230423ยกเว้นว่า a) ไม่มีแคชเนื่องจาก Pyth จะจดจำโดยอัตโนมัติ b) พิมพ์แต่ละรายการแทนการต่อท้ายรายการและ c) แปลเป็น Pyth

M?smg-Gddr1hhS,GHG1Akd,QOgQQWQFNr1hhS,QkKg-QNNI<dKB-=dK)N=kN-=QN

ฉันจะโพสต์คำอธิบายนี้เมื่อฉันมีเวลา แต่นี่คือรหัสหลามที่สอดคล้องกัน:

g=lambda G,H: sum(map(lambda d:g(G-d, d), range(1, (H if H<G else G) + 1))) if G else 1
Q=input()
k,d = Q,random.randrange(g(Q, Q))
while Q:
    for N in range(1, min(k, Q) + 1):
        K = g(Q-N, N)
        if d < K:
            break
        d -= K
    print N
    k=N
    Q -= N

แก้ไข:ฉันได้รอบสุดท้ายเพื่อทำคำอธิบาย:

M                Lambda g(G,H)
 ?         G     If G truthy
  s              Sum
   m             Map
    g            Recursive call
     -Gdd        G-d,d
    r            Range
     1           1 to
     h           +1
      hS         First element of sorted (does min)
       ,GH       From G and H
   1             Else 1
A                Double assign
 kd              Vars k and d
 ,               To vals
  Q              Q (evaled input)
  O              Randrange 0 till val
   gQQ           Call g(Q, Q)
WQ               While Q is truthy
 FN              For N in
  r              Range
   1             From one
   h             Till +1
    hS,QK        Min(Q,K)
  Kg             K=g(
   -QN           Q-N
   N             N
  I<dK           If d<k
   B             Break (implicit close paren)
  -=dk           Subtracts d-=k
 )               Close out for loop
 N               Prints N
 =kN             Set k=N
 -=QN            Subtracts Q-=N

2

อ็อกเทฟ 200

function r=c(m)r=[];a=eye(m);a(:,1)=1;for(i=3:m)for(j=2:i-1)a(i,j)=a(i-1,j-1)+a(i-j,j);end;end;p=randi(sum(a(m,:)));while(m>0)b=a(m,:);c=cumsum(b);x=min(find(c>=p));r=[r x];p=p-c(x)+b(x);m=m-x;end;end

Ungolfed:

function r=c(m)
  r=[];
  a=eye(m);
  a(:,1)=1;
  for(i=3:m)
    for(j=2:i-1)
      a(i,j)=a(i-1,j-1)+a(i-j,j);
    end;
  end;
  p=randi(sum(a(m,:)));
  while(m>0)
    b=a(m,:);
    c=cumsum(b);
    x=min(find(cumsum(b)>=p));
    r=[r x];
    p=p-c(x)+b(x);
    m=m-x;
  end
end

สร้างตารางเมทริกซ์ที่แต่ละเซลล์ (m, n) แสดงจำนวนพาร์ติชันmที่มีจำนวนมากที่สุดnตาม Knuth แยก @feersum ดังนั้นโปรดอ้างถึง ยกตัวอย่างเช่น5,2จะช่วยให้เรา 2 เพราะมีสองพาร์ทิชันที่ถูกต้องและ2,2,1 จะช่วยให้เรา 3 , และ2,1,1,16,33,1,1,13,2,13,3และ

ตอนนี้เราสามารถหาพาร์ติชั่น p'th ได้อย่างแน่นอน ที่นี่เรากำลังสร้างpเป็นตัวเลขสุ่ม แต่คุณสามารถแก้ไขสคริปต์เล็กน้อยดังนั้นจึงpเป็นพารามิเตอร์:

function r=c(m,p)
  r=[];
  a=eye(m);
  a(:,1)=1;
  for(i=3:m)
    for(j=2:i-1)
      a(i,j)=a(i-1,j-1)+a(i-j,j);
    end;
  end;
  while(m>0)
    b=a(m,1:m);
    c=cumsum(b);
    x=min(find(c>=p));
    r=[r x];
    p=p-c(x)+b(x);
    m=m-x;
  end
end

ตอนนี้เราสามารถแสดงให้เห็นอย่างชัดเจนว่าแต่ละผลลัพธ์นั้นขึ้นอยู่กับ p:

octave:99> for(i=1:7)
> c(5,i)
> end
ans =

   1   1   1   1   1

ans =

   2   1   1   1

ans =

   2   2   1

ans =

   3   1   1

ans =

   3   2

ans =

   4   1

ans =  5

ดังนั้นกลับไปที่ต้นฉบับโดยที่ p ถูกสร้างแบบสุ่มเราสามารถมั่นใจได้ว่าผลลัพธ์แต่ละรายการมีโอกาสเท่ากัน


ฉันไม่แน่ใจเกี่ยวกับตัวอย่าง 5,2 ของคุณ พาร์ติชันทั้งสองไม่ควรเป็น(2,2,1)และ(2,1,1,1,1)(เนื่องจากทั้งสองรายการที่คุณระบุไว้มีจำนวนมากกว่า2)
Martin Ender

คุณพูดถูกฉันได้รับสิ่งที่บิด 2มีสองพาร์ทิชันที่มีสองส่วนประกอบและสองพาร์ทิชันเริ่มต้นด้วยการเป็น ฉันหมายถึงหลัง
dcsohl

2

R, 198 ไบต์

function(m){r=c();a=diag(m);a[,1]=1;for(i in 3:m)for(j in 2:(i-1))a[i,j]=a[i-1,j-1]+a[i-j,j];p=sample(sum(a[m,]),1);while(m>0){b=a[m,];c=cumsum(b);x=min(which(c>=p));r=c(r,x);p=p-c[x]+b[x];m=m-x};r}

Ungolfed:

f <- function(m) {
    r <- c()
    a <- diag(m)
    a[, 1] <- 1
    for (i in 3:m)
        for (j in 2:(i-1))
            a[i, j] <- a[i-1, j-1] + a[i-j, j]
    p <- sample(sum(a[m, ]), 1)
    while (m > 0) {
        b <- a[m, ]
        c <- cumsum(b)
        x <- min(which(c >= p))
        r <- c(r, x)
        p <- p - c[x] + b[x]
        m <- m - x
    }
    return(r)
}

มันเป็นไปตามโครงสร้างเดียวกันกับโซลูชันที่ยอดเยี่ยมของ @ dcsohl ใน Octaveและยังขึ้นอยู่กับสารสกัด Knuthโพสต์โดย @feersum

ฉันจะแก้ไขสิ่งนี้ในภายหลังหากฉันสามารถหาวิธีแก้ปัญหาที่สร้างสรรค์มากขึ้นในอาร์ในระหว่างนี้ยินดีรับอินพุตใด ๆ


1

Java, 392 ไบต์

import java.util.*;Map a=new HashMap();List a(int b){List c=new ArrayList();int d=b,e=b(b,d),f=(int)(Math.random()*e),g,i;while(b>0){for(g=0;g++<Math.min(d, b);f-=i){i=b(b-g,g);if(f<i)break;}c.add(g);d=g;b-=g;}return c;}int b(int b,int c){if(b<1)return 1;List d=Arrays.asList(b,c);if(a.containsKey(d))return(int)a.get(d);int e,f;for(e=f=0;f++<Math.min(c, b);)e+=b(b-f,f);a.put(d,e);return e;}

a(n)โทรหา ส่งคืนListของIntegers

เยื้อง:

import java.util.*;

Map a=new HashMap();

List a(int b){
    List c=new ArrayList();
    int d=b,e=b(b,d),f=(int)(Math.random()*e),g,i;
    while(b>0){
        for(g=0;g++<Math.min(d, b);f-=i){
            i=b(b-g,g);
            if(f<i)
                break;
        }
        c.add(g);
        d=g;
        b-=g;
    }
    return c;
}

int b(int b,int c){
    if(b<1)
        return 1;
    List d=Arrays.asList(b,c);
    if(a.containsKey(d))
        return(int)a.get(d);
    int e,f;
    for(e=f=0;f++<Math.min(c, b);)
        e+=b(b-f,f);
    a.put(d,e);
    return e;
}

ดัดแปลงมาจาก/programming//a/2163753/4230423และ golfed

วิธีการทำงาน:เราสามารถคำนวณจำนวนพาร์ติชันของจำนวนเต็มn ที่มีในเวลา O ( n 2 ) ในฐานะที่เป็นผลข้างเคียงสิ่งนี้จะสร้างตารางขนาด O ( n 2 ) ซึ่งเราสามารถใช้เพื่อสร้างพาร์ติชันที่kของnสำหรับจำนวนเต็มใด ๆk , ใน O ( n )

ดังนั้นขอให้รวม = จำนวนพาร์ติชัน เลือกหมายเลขสุ่มkจาก 0 ถึงทั้งหมด - 1. สร้างพาร์ติชันk th

ตามปกติข้อเสนอแนะยินดีต้อนรับ :)


1

Python 2, 173 ไบต์

from random import*
N,M=input__
R=67;d=[(0,[])]*R*R
for k in range(R*R):p,P=d[k+~R];q,Q=d[k-k%R*R];d[k]=p+q+0**k,[[x+1 for x in Q],[1]+P][random()*(p+q)<p]
print d[N*R+M][1]

สร้างพจนานุกรมแบบเรียกซ้ำโดยใช้dกุญแจที่kแสดงถึงคู่(n,m)โดยk=67*n+m(ใช้การรับประกันn<=65) รายการเป็น tuple ของจำนวนพาร์ติชันของnเป็นmส่วนและสุ่มพาร์ติชันดังกล่าว จำนวนจะคำนวณโดยสูตรคำนวณซ้ำ (ขอบคุณ feersum สำหรับการชี้ให้เห็น)

f(n,m) = f(n-1,m-1) + f(n,n-m),

และพาร์ติชันแบบสุ่มได้รับการปรับปรุงโดยเลือกหนึ่งในสองของสาขาที่มีความน่าจะเป็นสัดส่วนกับจำนวน การอัปเดตจะกระทำโดยการเพิ่มจะทำโดยการผนวก a 1สำหรับสาขาแรกและการเพิ่มทุกองค์ประกอบสำหรับวินาที

ฉันมีปัญหาในการรับค่านอกขอบเขตmและnให้นับศูนย์ ตอนแรกฉันใช้พจนานุกรมที่มีค่าเริ่มต้นเป็น 0 และรายการที่ว่างเปล่า ที่นี่ฉันใช้รายการแล้วเติมด้วยรายการเริ่มต้นแทน ดัชนีเชิงลบสาเหตุรายการที่จะอ่านจากจุดสิ้นสุดของมันซึ่งจะช่วยให้รายการเริ่มต้นอะไรที่ใกล้จะจบเช่นเคยถึงและ wraparounds m>nเพียงสัมผัสภูมิภาคที่


1

รหัสเครื่อง 80386, 105 ไบต์

Hexdump ของรหัส:

60 8b fa 81 ec 00 41 00 00 33 c0 8b f4 33 d2 42
89 14 06 42 33 ed 8b d8 03 2c 1e 2a fa 73 f9 83
c6 04 89 2c 06 42 3b d1 76 ea fe c4 3a e1 76 db
33 d2 0f c7 f0 f7 f5 86 e9 85 d2 74 1b 33 c0 8d
34 0c 39 14 86 77 03 40 eb f8 2b 54 86 fc 40 89
07 83 c7 04 2a e8 77 e1 42 89 17 83 c7 04 fe cd
7f f7 4a b6 41 03 e2 61 c3

ในฐานะที่เป็นฟังก์ชั่น void random_partition(int n, int result[]);C: มันจะส่งกลับผลเป็นรายการของตัวเลขในบัฟเฟอร์ที่ให้มา; มันไม่ได้ทำเครื่องหมายจุดสิ้นสุดของรายการในทางใดทางหนึ่ง แต่ผู้ใช้สามารถค้นพบจุดสิ้นสุดโดยการสะสมตัวเลข - รายการจะสิ้นสุดลงเมื่อผลรวมเท่ากับnรายการสิ้นสุดลงเมื่อผลรวมมีค่าเท่ากับ

วิธีใช้ (ใน Visual Studio):

#include <stdio.h>

__declspec(naked) void __fastcall random_partiton(int n, int result[])
{
#define a(byte) __asm _emit 0x ## byte
a(60) a(8b) a(fa) a(81) a(ec) a(00) a(41) a(00) a(00) a(33) a(c0) a(8b) a(f4) a(33) a(d2) a(42)
a(89) a(14) a(06) a(42) a(33) a(ed) a(8b) a(d8) a(03) a(2c) a(1e) a(2a) a(fa) a(73) a(f9) a(83)
a(c6) a(04) a(89) a(2c) a(06) a(42) a(3b) a(d1) a(76) a(ea) a(fe) a(c4) a(3a) a(e1) a(76) a(db)
a(33) a(d2) a(0f) a(c7) a(f0) a(f7) a(f5) a(86) a(e9) a(85) a(d2) a(74) a(1b) a(33) a(c0) a(8d)
a(34) a(0c) a(39) a(14) a(86) a(77) a(03) a(40) a(eb) a(f8) a(2b) a(54) a(86) a(fc) a(40) a(89)
a(07) a(83) a(c7) a(04) a(2a) a(e8) a(77) a(e1) a(42) a(89) a(17) a(83) a(c7) a(04) a(fe) a(cd)
a(7f) a(f7) a(4a) a(b6) a(41) a(03) a(e2) a(61) a(c3)
}

void make_stack() // see explanations about stack below
{
    volatile int temp[65 * 64];
    temp[0] = 999;
}

int main()
{
    int result[100], j = 0, n = 64, counter = n;
    make_stack(); // see explanations about stack below

    random_partiton(n, result);

    while (counter > 0)
    {
        printf("%d ", result[j]);
        counter -= result[j];
        ++j;
    }
    putchar('\n');
}

ตัวอย่างเอาต์พุต (ด้วย n = 64):

21 7 4 4 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1

สิ่งนี้ต้องการคำอธิบายมากมาย ...

แน่นอนฉันใช้อัลกอริทึมที่คนอื่นใช้เช่นกัน ไม่มีทางเลือกใดกับความต้องการเกี่ยวกับความซับซ้อน ดังนั้นฉันไม่ต้องอธิบายอัลกอริทึมมากเกินไป อย่างไรก็ตาม:

ฉันใช้แสดงโดยf(n, m)จำนวน partitionings ขององค์ประกอบที่ใช้ชิ้นส่วนที่ไม่เกินn mฉันเก็บมันไว้ในอาร์เรย์ 2 มิติ (ประกาศใน C เป็นf[65][64] ) ที่แรกคือดัชนีและที่สองn m-1ฉันตัดสินใจที่จะสนับสนุนn=65เป็นปัญหามากเกินไปละทิ้งมัน ...

นี่คือรหัส C ที่คำนวณตารางนี้:

#define MAX_M 64
int f[(MAX_M + 1) * MAX_M];
int* f2;
int c; // accumulates the numbers needed to calculate f(n, m)
int m;
int k; // f(k, m), for various values of k, are accumulated
int n1;

for (n1 = 0; n1 <= n; ++n1)
{
    f2 = f;
    f2[n1 * MAX_M] = 1;
    for (m = 2; m <= n; ++m)
    {
        c = 0;
        k = n1;
        while (k >= 0)
        {
            c += f2[k * MAX_M];
            k -= m;
        }
        ++f2;
        f2[n1 * MAX_M] = c;
    }
}

รหัสนี้มีลักษณะยุ่งเหยิงดังนั้นจึงสามารถแปลงเป็นภาษาแอสเซมบลีได้อย่างง่ายดาย มันคำนวณองค์ประกอบขึ้นไปf(n, n)ซึ่งเป็นจำนวนพาร์ทิชันของnองค์ประกอบ เมื่อรหัสนี้เสร็จแล้วตัวแปรชั่วคราวcมีหมายเลขที่ต้องการซึ่งสามารถใช้เพื่อเลือกการแบ่งพาร์ติชันแบบสุ่ม:

int index = rand() % c;

ต่อมาสิ่งนี้ indexจะถูกแปลงเป็นรูปแบบที่ต้องการ (รายการหมายเลข) โดยใช้ตารางที่สร้างขึ้น

do {
    if (index == 0)
        break;

    m = 0;
    f2 = &f[n * MAX_M];
    while (f2[m] <= index)
    {
        ++m;
    }

    index -= f2[m-1];
    ++m;
    *result++ = m;
    n -= m;
} while (n > 0);

do {
    *result++ = 1;
    --n;
} while (n > 0);

รหัสนี้เหมาะสำหรับการแปลงเป็นภาษาแอสเซมบลี มี "บั๊ก" ขนาดเล็ก: หากการแบ่งพาร์ติชันไม่มีองค์ประกอบใด ๆ1ตัวเลขในตอนท้ายการวนซ้ำครั้งล่าสุดn = 0และการส่งออกที่ไม่จำเป็น1องค์ประกอบที่อย่างไรก็ตามมันไม่ได้เจ็บเพราะรหัสการพิมพ์จะติดตามผลรวมของตัวเลขและไม่พิมพ์หมายเลขที่ไม่เกี่ยวข้องนี้

เมื่อแปลงเป็นชุดประกอบแบบอินไลน์โค้ดนี้จะมีลักษณะดังนี้:

__declspec(naked) void _fastcall random_partition_asm(int n, int result[])
{
    _asm {
        pushad;

        // ecx = n
        // edx = m
        // bh = k; ebx = k * MAX_M * sizeof(int)
        // ah = n1; eax = n1 * MAX_M * sizeof(int)
        // esp = f
        // ebp = c
        // esi = f2
        // edi = result

        mov edi, edx;
        sub esp, (MAX_M + 1) * MAX_M * 4; // allocate space for table
        xor eax, eax;
    row_loop:
        mov esi, esp;
        xor edx, edx;
        inc edx;
        mov dword ptr [esi + eax], edx;
        inc edx;

    col_loop:
        xor ebp, ebp;
        mov ebx, eax;

    sum_loop:
        add ebp, [esi + ebx];
        sub bh, dl;
        jae sum_loop;

        add esi, 4;
        mov [esi + eax], ebp;
        inc edx;
        cmp edx, ecx;
        jbe col_loop;

        inc ah;
        cmp ah, cl;
        jbe row_loop;

        // Done calculating the table

        // ch = n; ecx = n * MAX_M * sizeof(int)
        // eax = m
        // ebx = 
        // edx = index
        // esp = f
        // esi = f2
        // ebp = c
        // edi = result

        xor edx, edx;
        rdrand eax; // generate a random number
        div ebp; // generate a random index in the needed range
        xchg ch, cl; // multiply by 256

    n_loop:
        test edx, edx;
        jz out_trailing;
        xor eax, eax;
        lea esi, [esp + ecx];

    m_loop:
        cmp [esi + eax * 4], edx;
        ja m_loop_done;
        inc eax;
        jmp m_loop;
    m_loop_done:

        sub edx, [esi + eax * 4 - 4];
        inc eax;
        mov [edi], eax;
        add edi, 4;
        sub ch, al;
        ja n_loop;

    out_trailing:
        inc edx;
    out_trailing_loop:
        mov dword ptr [edi], edx;
        add edi, 4;
        dec ch;
        jg out_trailing_loop;

        dec edx;
        mov dh, (MAX_M + 1) * MAX_M * 4 / 256;
        add esp, edx;
        popad;
        ret;
    }
}

สิ่งที่สนุกที่ควรทราบ:

  • การสร้างหมายเลขสุ่มจะใช้รหัสเครื่องเพียง 3 ไบต์ (rdrandคำสั่ง)
  • โดยบังเอิญขนาดของตารางคือ 64 ดังนั้นขนาดของหนึ่งแถวคือ 256 ไบต์ ฉันใช้สิ่งนี้เพื่อเก็บดัชนีแถวในรีจิสเตอร์ "high-byte" ahซึ่งให้การคูณอัตโนมัติ 256 กับฉันเพื่อใช้ประโยชน์จากสิ่งนี้ฉันเสียสละการสนับสนุนสำหรับn = 65เพื่อใช้ประโยชน์จากนี้ผมเสียสละการสนับสนุนสำหรับ ฉันหวังว่าฉันจะได้รับโทษบาปนี้ ...
  • การจัดสรรพื้นที่บนสแต็กจะดำเนินการโดยการลบ 0x4100 ออกจากการลงทะเบียนตัวชี้สแต็espก นี่คือคำสั่ง 6 ไบต์! เมื่อเพิ่มหมายเลขนี้กลับฉันจัดการเพื่อทำใน 5 ไบต์:

        dec edx; // here edx = 1 from earlier calculations
        mov dh, (MAX_M + 1) * MAX_M * 4 / 256; // now edx = 0x4100
        add esp, edx; // this deallocates space on stack
    
  • เมื่อทำการดีบั๊กฟังก์ชั่นนี้ใน MS Visual Studio ฉันพบว่ามันล้มเหลวเมื่อมันเขียนข้อมูลไปยังพื้นที่ที่มันจัดสรรในกอง! หลังจากที่ขุดไปมาแล้วฉันค้นพบการป้องกันการโอเวอร์คล็อกแบบเรียงซ้อนบางส่วน: ระบบปฏิบัติการดูเหมือนว่าจะจัดสรรที่อยู่เสมือนจำนวน จำกัด สำหรับสแต็กเท่านั้น หากฟังก์ชั่นเข้าถึงที่อยู่ไกลเกินไป OS จะถือว่าเป็นการทำงานหนักเกินไปและฆ่าโปรแกรม อย่างไรก็ตามหากฟังก์ชั่นนั้นมีตัวแปรในตัวเครื่องหลายตัวระบบปฏิบัติการจะใช้ "เวทย์มนตร์" พิเศษเพื่อให้มันทำงานได้ ดังนั้นฉันต้องเรียกฟังก์ชันว่างที่มีอาร์เรย์ขนาดใหญ่ที่จัดสรรไว้ในสแต็ก หลังจากที่ฟังก์ชันนี้ส่งคืนหน้า VM สแต็กพิเศษจะถูกจัดสรรและสามารถใช้ได้

        void make_stack()
        {
            volatile int temp[65 * 64];
            temp[0] = 999; // have to "use" the array to prevent optimizing it out
        }
    
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.