Golf Acak Hari Ini # 3: Partisi Integer


19

Tentang Seri

Pertama, Anda dapat memperlakukan ini seperti tantangan golf kode lainnya, dan menjawabnya tanpa khawatir tentang seri sama sekali. Namun, ada papan peringkat di semua tantangan. Anda dapat menemukan papan peringkat bersama dengan beberapa informasi lebih lanjut tentang seri di posting pertama .

Meskipun saya memiliki banyak ide untuk seri ini, tantangan di masa depan belum ditetapkan. Jika Anda memiliki saran, beri tahu saya di pos kotak pasir yang relevan .

Lubang 3: Partisi Integer

Saatnya menambah kesulitan sedikit.

Sebuah partisi dari bilangan bulat positif ndidefinisikan sebagai multiset bilangan bulat positif yang sum untuk n. Sebagai contoh jika n = 5, partisi berikut ada:

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

Perhatikan bahwa ini adalah multiset, jadi tidak ada urutannya {3,1,1},, {1,3,1}dan {1,1,3}semuanya dianggap identik.

Tugas Anda, diberikan n, untuk menghasilkan partisi acak n. Berikut aturan terperinci:

  • Distribusi partisi yang dihasilkan harus seragam . Artinya, dalam contoh di atas, setiap partisi harus dikembalikan dengan probabilitas 1/7.

    Tentu saja, karena keterbatasan teknis PRNG, keseragaman yang sempurna tidak mungkin terjadi. Untuk tujuan menilai keseragaman kiriman Anda, operasi berikut akan dianggap menghasilkan distribusi seragam yang sempurna:

    • Memperoleh nomor dari PRNG (pada rentang berapa pun), yang didokumentasikan sebagai (kurang-lebih) seragam.
    • Memetakan distribusi seragam pada set angka yang lebih besar ke set yang lebih kecil melalui modulo atau multiplikasi (atau operasi lain yang mendistribusikan nilai secara merata). Set yang lebih besar harus mengandung setidaknya 1024 kali lebih banyak nilai yang mungkin dari set yang lebih kecil.
  • Karena partisi adalah multiset, Anda dapat mengembalikannya dalam urutan apa pun, dan urutan ini tidak harus konsisten. Namun, untuk tujuan distribusi acak, urutannya diabaikan. Yaitu, dalam contoh di atas {3,1,1},, {1,3,1}dan {1,1,3} bersama - sama harus memiliki probabilitas 1/7 untuk dikembalikan.

  • Algoritme Anda harus memiliki runtime deterministik. Secara khusus, Anda tidak dapat membuat multiset acak dan menolaknya jika tidak dijumlahkan n.
  • Kompleksitas waktu algoritma Anda harus polinomial dalam n. Secara khusus, Anda tidak dapat hanya menghasilkan semua partisi dan memilih yang acak (karena jumlah partisi tumbuh secara eksponensial dengan n). Anda dapat berasumsi bahwa PRNG yang Anda gunakan dapat mengembalikan nilai yang didistribusikan secara seragam dalam O (1) per nilai.
  • Anda tidak boleh menggunakan fungsi bawaan yang menyelesaikan tugas ini.

Anda dapat menulis program atau fungsi lengkap dan mengambil input melalui STDIN atau alternatif terdekat, argumen baris perintah atau argumen fungsi dan menghasilkan output melalui nilai balik atau dengan mencetak ke STDOUT (atau alternatif terdekat).

Anda dapat mengasumsikan bahwa n ≤ 65(sehingga jumlah partisi kurang dari 21 ). Output mungkin dalam format string atau daftar yang mudah, tidak ambigu.

Jika Anda mengirimkan fungsi, harap pertimbangkan juga untuk menyediakan program uji kecil yang memanggil fungsi beberapa kali dan mencetak hasilnya. Tidak apa-apa jika parameter harus di-tweak dalam kode. Ini agar orang dapat memeriksa bahwa solusinya setidaknya sekitar seragam.

Ini adalah kode golf, jadi pengiriman terpendek (dalam byte) menang. Dan tentu saja, pengiriman terpendek per pengguna juga akan masuk ke papan peringkat keseluruhan seri.

Papan peringkat

Pos pertama dari seri ini menghasilkan papan peringkat.

Untuk memastikan jawaban Anda muncul, mulailah setiap jawaban dengan tajuk utama, menggunakan templat Penurunan harga berikut:

# Language Name, N bytes

di mana Nukuran kiriman Anda. Jika Anda meningkatkan skor Anda, Anda dapat menyimpan skor lama di headline, dengan mencoretnya. Contohnya:

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

(Bahasa saat ini tidak ditampilkan, tetapi cuplikan memang membutuhkan dan menguraikannya, dan saya dapat menambahkan leaderboard berdasarkan bahasa di masa mendatang.)

Jawaban:


8

Python 2, 179 byte

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

Saya menggunakan formula (39) dari ekstrak Knuth ini , yang memberikan jumlah partisi nyang memiliki mbagian yang persis sama . Ini terjadi sama dengan jumlah partisi nyang ada msebagai elemen maksimum, yang merupakan interpretasi yang saya gunakan. Elemen-elemen partisi dihasilkan dari yang terbesar ke yang paling kecil. Pada setiap tahap, rumus tersebut digunakan kembali dengan sisa nelemen saat ini dan maksimum yang diizinkan.


5

Dyalog APL, 67 59 51 byte

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

padalah vektor vektor di mana p[n][k]adalah jumlah partisi nke dalam kpuncak, atau setara: jumlah partisi dengan puncak terbesar k. Kami membangun pdengan memulai dengan vektor kosong , membaca n( input bacaan) dan berulang kali menerapkan hal berikut:

{⍵,⊂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

Setelah naplikasi ( ⍣⎕), kami telah membangun p.

fmengambil partisi acak. n f kadalah partisi acak paling banyak k . f nadalah n 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

Beberapa peningkatan:

  • sebaris pdengan biaya kinerja yang sedikit lebih buruk (tapi masih cukup baik)

  • dalam perhitungan pmengatur ulang dan 1,untuk menyimpan karakter

  • berubah {1++/(?+/⍵)>+\⍵}menjadi kereta dengan 1+di depan:1+(+/(?+/)>+\)

  • buatlah ffungsi dan persediaan anonim (input yang dievaluasi) sebagai argumen untuk mendapatkan program yang lengkap

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

Uji dengan n = 5

Tes dengan n = 65

Dan tautan berikut berjalan n = 5 ribuan kali dan mengumpulkan statistik tentang frekuensi setiap partisi: ⎕rl←0 ⋄ {⍺,⍴⍵}⌸ {⍵=0:⍬⋄a,a∇⍵-a←1+(+/(?+/)>+\)⍺↑⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬}⍨ ¨10000⍴5


Lebih banyak perbaikan, dengan bantuan dari Roger Hui :

  • ganti {⍵=0:A⋄B}dengan {×⍵:B⋄A}. Signum ( ×⍵) mengembalikan true for ⍵>0dan false for ⍵=0.

  • ganti (+/(?+/)>+\)dengan +/b<?⊃⌽b←+\, itu menyimpan karakter

  • gunakan matriks alih-alih vektor vektor untuk menghitung p: ganti ⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬dengan ⊃↓(0,⍨⊢⍪⍨1 1⍉+\)⍣⍵⍪1.

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

tes n = 5 ; tes n = 65 ; statistik freq


2
Bagaimana cara mendapat bantuan dari Roger Hui?
FUZxxl

5
Menulis mainan penerjemah APL untuk mendapatkan diri Anda dipekerjakan di perusahaan yang sama dengannya. Anggap ungkapan di atas sebagai tantangan, janjikan segelas bir untuk setiap karakter yang ia keluarkan. Kemudian untung: lebih sedikit karakter dan lebih banyak minuman keras karena dia tidak minum bir.
ngn

1
Saya melihat. Itu strategi yang rapi, mari kita lihat apakah saya bisa mereproduksi itu ... Bisakah Anda bertanya padanya apakah Dyalog APL akan mendapatkan sesuatu seperti J u/\. ydalam waktu dekat?
FUZxxl


Terima kasih telah bertanya padanya. Sekarang saya bertanya-tanya apakah itu mungkin dalam waktu linier juga.
FUZxxl

4

GolfScript, 90 byte

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

Demo online

Ini adalah adaptasi dari kode penghitungan partisi saya (yang lebih sederhana) yang alih-alih hanya melacak trek hitungan baik hitungan maupun yang dipilih secara seragam dari salah satu elemen yang dihitung.

Perbandingan keduanya secara berdampingan:

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

Perbedaan:

  • Yang pertama ~adalah karena ini adalah program daripada potongan.
  • The [1.]mengganti 1berkorespondensi dengan perubahan apa yang dilacak.
  • Tambahan {(\{)}%+}%setiap elemen di partisi itu, dan {1+}%menambah 1partisi.
  • 0menjadi [0](digerakkan ke 1,) sebagai bagian dari perubahan pada apa yang dilacak, tetapi karena itu perlu tetap menjadi array ketika ditambahkan ke yang lain itu membutuhkan ekstra [ ].
  • Jumlah sederhana {+}*menjadi pilihan tertimbang dari partisi, dikombinasikan dengan penjumlahan jumlah mereka.
  • The (;`menghilangkan count dari output dan menempatkan partisi ke dalam format yang bagus.

Kerangka uji

;7000,{;
  '5'

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

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

Tweak awal 7000 jika Anda ingin menjalankan jumlah percobaan yang berbeda. Perhatikan bahwa ini terlalu lambat untuk demo online.


3

Java, 285 267 byte

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;}

Ini adalah metode yang sama dengan jawaban TheBestOne, tetapi menggunakan array sederhana alih-alih peta. Selain itu, alih-alih mengembalikan partisi acak sebagai a List, partisi tersebut mencetaknya ke konsol.

Di bawah ini adalah program pengujian yang menjalankannya 100.000 kali. Sebagai contoh n=5, semua set berada dalam 0,64% dari 1/7 sempurna pada putaran terakhir saya.

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
Meskipun Anda sudah golfed Math.minpanggilan ke (k<n?k:n), Anda dapat pergi lebih jauh dengan membolos itu sepenuhnya dan hanya melakukan dua cek: b<k&b++<n. Anda juga dapat dengan mudah membuang n>0bagian dari pengulangan bersyarat (karena n>0&b<nmengurangi hingga b<nsaat bdijamin non-negatif).
Peter Taylor

@PeterTaylor Terima kasih. Melihat lagi, izinkan saya menyingkirkan pernyataan pengembalian ekstra dan intdeklarasi terpisah juga.
Geobits

3

CJam, 64 56 byte

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

Anda dapat mengujinya dengan skrip ini:

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

Penjelasan

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
Anda harus menghapus bagian "tidak golf dengan baik" yang salah dari jawaban Anda;)
anatolyg

@anatolyg Dihapus. Tapi saya percaya masih mungkin untuk menghapus beberapa byte. Aku terlalu malas untuk melakukan itu.
jimmy23013

3

Pyth, 64 byte

Menggunakan /programming//a/2163753/4230423 kecuali bahwa a) Tidak ada cache karena Pyth secara otomatis memoize, b) Mencetak masing-masing alih-alih menambahkan daftar, dan c) diterjemahkan ke Pyth.

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

Saya akan memposting penjelasan tentang ini ketika saya punya waktu, tetapi di sini adalah kode python yang sesuai:

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

Sunting: Saya akhirnya sempat melakukan penjelasan:

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

Oktaf, 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

Tidak Disatukan:

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

Bangun sebuah matriks persegi di mana setiap sel (m, n) mencerminkan jumlah partisi myang memiliki jumlah terbesar n, menurut ekstrak Knuth @feersum yang dikutip dengan baik. Sebagai contoh, 5,2beri kami 2 karena ada dua partisi yang valid 2,2,1dan 2,1,1,1. 6,3memberi kita 3 untuk 3,1,1,1, 3,2,1dan 3,3.

Kita sekarang dapat secara deterministik menemukan partisi p'th. Di sini, kami menghasilkan psebagai angka acak tetapi Anda dapat sedikit mengubah skrip sehingga pmerupakan parameter:

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

Kami sekarang dapat secara deterministik menunjukkan bahwa setiap hasil semata-mata bergantung pada 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

Dengan demikian, kembali ke aslinya di mana p dihasilkan secara acak, kita dapat yakin bahwa setiap hasil memiliki kemungkinan yang sama besar.


Saya tidak yakin tentang contoh 5,2 Anda. Bukankah seharusnya kedua partisi itu (2,2,1)dan (2,1,1,1,1)(karena dua yang Anda daftarkan memiliki angka lebih besar dari 2).
Martin Ender

Anda benar, saya punya banyak hal. Ada dua partisi dengan dua komponen, dan dua partisi dimulai dengan 2. Maksud saya yang terakhir.
dcsohl

2

R, 198 byte

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}

Tidak Disatukan:

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

Ini mengikuti struktur yang sama dengan solusi hebat @ dcsohl di Octave , dan karenanya juga berdasarkan pada ekstrak Knuth yang diposting oleh @feersum.

Saya akan mengedit ini nanti jika saya dapat menemukan solusi yang lebih kreatif di R. Sementara itu, setiap masukan tentu saja diterima.


1

Java, 392 byte

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;}

Panggil dengan a(n). Mengembalikan Listdari Integers

Bertakuk:

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;
}

Diadaptasi dari /programming//a/2163753/4230423 dan golf

Bagaimana ini bekerja: Kita dapat menghitung berapa banyak partisi bilangan bulat n ada dalam waktu O ( n 2 ). Sebagai efek samping, ini menghasilkan tabel ukuran O ( n 2 ) yang kemudian dapat kita gunakan untuk menghasilkan partisi k dari n , untuk setiap bilangan bulat k , dalam waktu O ( n ).

Jadi mari total = jumlah partisi. Pilih angka acak k dari 0 hingga total - 1. Hasilkan partisi k .

Seperti biasa , saran dipersilahkan :)


1

Python 2, 173 byte

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]

Secara rekursif membuat kamus d, dengan kunci yang kmewakili pasangan (n,m)dengan k=67*n+m(menggunakan dijamin n<=65). Entri adalah tuple dari jumlah partisi nmenjadi mbeberapa bagian, dan partisi acak seperti itu. Hitungan dihitung dengan rumus rekursif (terima kasih kepada feersum karena menunjukkannya)

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

dan partisi acak diperbarui dengan memilih salah satu dari dua cabangnya dengan probabilitas yang sebanding dengan jumlahnya. Pembaruan dilakukan dengan menambahkan dilakukan dengan menambahkan 1untuk cabang pertama dan menambah setiap elemen untuk yang kedua.

Saya mengalami banyak kesulitan untuk mendapatkan nilai di luar batas mdan nuntuk memberikan jumlah nol. Pada awalnya, saya menggunakan kamus yang default ke hitungan 0 dan daftar kosong. Di sini, saya menggunakan daftar dan menambahkannya dengan entri default ini sebagai gantinya. Indeks negatif menyebabkan daftar dibaca dari akhir, yang memberikan entri default tidak mendekati akhir seperti yang pernah dicapai, dan sampul hanya menyentuh wilayah di mana m>n.


1

80386 kode mesin, 105 byte

Hexdump kode:

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

Sebagai fungsi C: void random_partition(int n, int result[]);. Ini mengembalikan hasilnya sebagai daftar angka dalam buffer yang disediakan; itu tidak menandai akhir daftar dengan cara apa pun, tetapi pengguna dapat menemukan akhirnya dengan mengumpulkan angka - daftar berakhir ketika jumlahnya sama dengan n.

Cara menggunakan (dalam 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');
}

Contoh output (dengan n = 64):

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

Ini membutuhkan banyak penjelasan ...

Tentu saja saya menggunakan algoritma yang digunakan orang lain juga; tidak ada pilihan dengan persyaratan tentang kompleksitas. Jadi saya tidak perlu menjelaskan algoritme terlalu banyak. Bagaimanapun:

Saya tunjukkan dengan f(n, m)jumlah partisi nelemen menggunakan bagian tidak lebih besar dari m. Saya menyimpannya dalam array 2-D (dinyatakan dalam C as f[65][64]), di mana indeks pertama n, dan yang kedua m-1. Saya memutuskan bahwa mendukung n=65terlalu banyak masalah, jadi mengabaikannya ...

Berikut adalah kode C yang menghitung tabel ini:

#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;
    }
}

Kode ini memiliki beberapa gaya yang dikaburkan, sehingga dapat dikonversi ke bahasa assembly dengan mudah. Ini menghitung elemen hingga f(n, n), yang merupakan jumlah partisi nelemen. Ketika kode ini selesai, variabel sementara cberisi nomor yang diperlukan, yang dapat digunakan untuk memilih partisi acak:

int index = rand() % c;

Kemudian, ini indexdikonversi ke format yang diperlukan (daftar angka) menggunakan tabel yang dihasilkan.

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

Kode ini juga dioptimalkan untuk konversi ke bahasa assembly. Ada "bug" kecil: jika partisi tidak berisi 1angka apa pun di akhir, loop terakhir bertemu n = 0, dan menampilkan 1elemen yang tidak dibutuhkan . Tidak ada salahnya, karena kode pencetakan melacak jumlah angka, dan tidak mencetak nomor asing ini.

Ketika dikonversi ke perakitan inline, kode ini terlihat seperti ini:

__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;
    }
}

Beberapa hal menyenangkan yang perlu diperhatikan:

  • Menghasilkan nomor acak hanya membutuhkan 3 byte kode mesin ( rdrandinstruksi)
  • Secara kebetulan, ukuran tabel adalah 64, jadi ukuran satu baris adalah 256 byte. Saya menggunakan ini untuk menyimpan indeks baris di register "byte tinggi" seperti ah, yang memberi saya perkalian otomatis dengan 256. Untuk mengambil keuntungan dari ini, saya mengorbankan dukungan untuk n = 65. Saya harap saya dapat dimaafkan untuk dosa ini ...
  • Mengalokasikan ruang pada tumpukan dilakukan dengan mengurangi 0x4100 dari register penunjuk tumpukan esp. Ini adalah instruksi 6-byte! Saat menambahkan nomor ini kembali, saya berhasil melakukannya dalam 5 byte:

        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
    
  • Ketika men-debug fungsi ini di MS Visual Studio, saya menemukan bahwa itu crash ketika menulis data ke ruang yang dialokasikan pada tumpukan! Setelah beberapa penggalian di sekitar, saya menemukan semacam perlindungan overrun stack: OS tampaknya hanya mengalokasikan kisaran yang sangat terbatas dari alamat virtual untuk stack; jika suatu fungsi mengakses suatu alamat yang terlalu jauh, OS menganggap itu suatu overrun dan membunuh program. Namun, jika suatu fungsi memiliki banyak variabel lokal, OS melakukan beberapa "sihir" tambahan untuk membuatnya berfungsi. Jadi saya harus memanggil fungsi kosong yang memiliki array besar yang dialokasikan pada stack. Setelah fungsi ini kembali, tumpukan halaman VM dialokasikan dan dapat digunakan.

        void make_stack()
        {
            volatile int temp[65 * 64];
            temp[0] = 999; // have to "use" the array to prevent optimizing it out
        }
    
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.