Günün Rastgele Golf # 3: Tamsayı Bölümleri


19

Seri Hakkında

Öncelikle, bunu diğer kod golf zorlukları gibi ele alabilir ve seri hakkında endişelenmeden cevaplayabilirsiniz. Ancak, tüm zorluklarda bir lider tablosu vardır. Liderlik tablosunu, diziyle ilgili daha fazla bilgiyi ilk gönderide bulabilirsiniz .

Dizi için sıralanmış bir sürü fikrim olmasına rağmen, gelecekteki zorluklar henüz taş değil. Eğer herhangi bir öneriniz varsa, lütfen ilgili sandbox yazı hakkında bana bildirin .

Delik 3: Tamsayı Bölümleri

Zorluğu biraz artırma zamanı.

Pozitif bir tamsayının bir bölümü , ntoplamı oluşturan pozitif tamsayıların çoklu kümesi olarak tanımlanır n. Örnek olarak n = 5, aşağıdaki bölümler bulunur:

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

Bu MULTISETS, bu nedenle hiçbir emir onlara, orada o Not {3,1,1}, {1,3,1}ve {1,1,3}tüm özdeş kabul edilir.

Göreviniz, nrastgele bir bölüm oluşturmak için verilir n. Ayrıntılı kurallar şunlardır:

  • Üretilen bölümlerin dağılımı eşit olmalıdır . Yani, yukarıdaki örnekte, her bölüm 1/7 olasılıkla döndürülmelidir.

    Tabii ki, PRNG'lerin teknik sınırlamaları nedeniyle, mükemmel tekdüzelik imkansız olacaktır. Gönderinizin tekdüzeliğini değerlendirmek amacıyla, aşağıdaki işlemler mükemmel şekilde eşit dağılımlar sağladığı düşünülecektir:

    • Bir PRNG'den (herhangi bir aralıkta) bir sayı elde etmek (yaklaşık olarak) tek tip olarak belgelenmiştir.
    • Modulo veya çarpma (veya değerleri eşit olarak dağıtan başka bir işlem) aracılığıyla daha büyük bir sayı kümesi üzerinde daha küçük bir kümeye tekdüze bir dağılım eşleme. Büyük küme, küçük kümenin en az 1024 katı değer içermelidir.
  • Bölümler çoklu kümeler olduğu için bunları herhangi bir sırayla döndürebilirsiniz ve bu siparişin tutarlı olması gerekmez. Ancak, rastgele dağılım amacıyla, sıra ihmal edilir. Kendisine, yukarıdaki örnekte, {3,1,1}, {1,3,1}ve {1,1,3} birlikte döndürülen 1/7 bir olasılığına sahip olmalıdır.

  • Algoritmanızın deterministik bir çalışma zamanı olmalıdır . Özellikle, rastgele çoklu ayarlar oluşturamaz ve toplamıyorlarsa reddedemezsiniz n.
  • Algoritmanızın zaman karmaşıklığı polinom olmalıdır n . Özellikle, tüm bölümleri oluşturamaz ve rastgele bir bölüm seçemezsiniz (bölüm sayısı katlanarak arttığından n). Kullandığınız PRNG'nin değer başına O (1) olarak eşit dağıtılmış değerler döndüğünü varsayabilirsiniz.
  • Bu görevi çözen yerleşik bir işlev kullanmamalısınız.

Tam bir program veya işlev yazabilir ve STDIN veya en yakın alternatif, komut satırı bağımsız değişkeni veya işlev bağımsız değişkeni ile girdi alabilir ve dönüş değeri üzerinden veya STDOUT'a (veya en yakın alternatife) yazdırarak çıktı üretebilirsiniz.

Sen varsayabiliriz n ≤ 65bölümleri sayısının 2'den az olacak şekilde ( 21 ). Çıktı herhangi bir uygun, anlaşılır liste veya dize biçiminde olabilir.

Bir işlev gönderirseniz, lütfen işlevi birkaç kez çağıran ve sonuçları yazdıran küçük bir test programı da sağlayın. Parametrelerin kodda değiştirilmesi gerekiyorsa sorun değil. Bu, insanların çözümün en azından yaklaşık olarak tek tip olduğunu kontrol edebilmeleri içindir.

Bu kod golf, yani en kısa gönderme (bayt cinsinden) kazanır. Ve elbette, kullanıcı başına en kısa gönderim de serinin genel skor tablosuna girecektir.

Liderler Sıralaması

Serinin ilk gönderisi bir skor tablosu oluşturur.

Yanıtlarınızın göründüğünden emin olmak için, lütfen aşağıdaki Markdown şablonunu kullanarak her cevaba bir başlık ile başlayın:

# Language Name, N bytes

Ngönderiminizin büyüklüğü nerede . Puanınızı artırmak varsa, olabilir onları içinden vurarak, başlığa eski hesapları tutmak. Örneğin:

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

(Dil şu anda gösterilmiyor, ancak snippet gerektiriyor ve ayrıştırıyor ve gelecekte bir dil lider tablosu ekleyebilirim.)

Yanıtlar:


8

Python 2, 179 bayt

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

Tam olarak parçalara sahip olan bölümlerin sayısını veren bu Knuth ekstresinden formül (39) kullandım . Bu, bölümleri sayısına eşit olur var ben kullanıyorum yorumudur maksimum elemanı niteliğindedir. Bölümün elemanları büyükten küçüğe oluşturulur. Her aşamada formül, mevcut kalan ve izin verilen maksimum eleman ile yeniden kullanılır .nmnmn


5

Dyalog APL, 67 59 51 bayt

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

pki burada vektörler bir vektördür p[n][k]ve bölüm sayısı niçine kbüyük toplam kısmı ile bölmelerin sayısı: summands veya eşdeğer k. pBoş vektörle başlayarak , okuyarak n( okuma girişi) ve tekrar tekrar aşağıdakileri uygulayarak inşa ederiz :

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

nUygulamalardan sonra ( ⍣⎕) yaptık p.

frastgele bir bölüm seçer. en fazla özetin n f krastgele bir bölümüdür. olduğunu . kf 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

Bazı iyileştirmeler:

  • pbiraz daha kötü (ama yine de yeterince iyi) performans pahasına satır içi

  • hesaplamasında pYeniden düzenle ve 1,bir karakterin tasarrufu için

  • çevirmek {1++/(?+/⍵)>+\⍵}olan bir trene 1+önünde:1+(+/(?+/)>+\)

  • tam bir program elde etmek fiçin anonim bir işlev yapmak ve argüman olarak tedarik (değerlendirilmiş girdi)

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

N = 5 ile test

N = 65 ile test

Ve aşağıdaki bağlantı n = 5 binlerce kez çalışır ve her bir bölümün sıklığı ile ilgili istatistikleri toplar: ⎕rl←0 ⋄ {⍺,⍴⍵}⌸ {⍵=0:⍬⋄a,a∇⍵-a←1+(+/(?+/)>+\)⍺↑⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬}⍨ ¨10000⍴5


Roger Hui'nin yardımıyla daha fazla iyileştirme :

  • yerine {⍵=0:A⋄B}sahip {×⍵:B⋄A}. Signum ( ×⍵), için true değerini ⍵>0ve false değerini döndürür ⍵=0.

  • yerini (+/(?+/)>+\)ile +/b<?⊃⌽b←+\bir karakteri kazandırır,

  • hesaplamak için vektörlerin bir matris yerine vektör kullanımı p: yerine ⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬ile ⊃↓(0,⍨⊢⍪⍨1 1⍉+\)⍣⍵⍪1.

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

test n = 5 ; test n = 65 ; frekans istatistikleri


2
Roger Hui'den nasıl yardım alınır?
FUZxxl

5
Kendini onunla aynı şirkette işe almak için bir oyuncak APL tercümanı yazın. Yukarıdaki ifadeyi bir meydan okuma olarak ortaya çıkarın, aldığı her karakter için bir bardak bira vaat edin. Sonra kar: bira içmediği için daha az karakter ve daha fazla içki.
ngn

1
Anlıyorum. Bu temiz bir strateji, bakalım bunu yeniden üretebilir miyim… Dyalog APL'nin yakın u/\. yzamanda J gibi bir şey alıp almayacağını sorabilir misiniz ?
FUZxxl


Ona sorduğun için teşekkürler. Şimdi, doğrusal zamanda da mümkün olup olmadığını merak ediyorum.
FUZxxl

4

GolfScript, 90 bayt

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

Çevrimiçi demo

Bu benim (daha basit) bölüm sayma kodumun bir uyarlaması yalnızca bir sayımı izlemek yerine oluşturan öğelerin hem sayısını hem de eşit olarak seçilen birini izleyen .

İkisinin yan yana karşılaştırması:

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

farklılıklar:

  • İlk ~ , bunun bir pasaj yerine bir program olmasıdır.
  • [1.]Yerine 1ne de değişime karşılık gelir izlenenden.
  • Ek {(\{)}%+}%, bu bölümdeki her öğeyi artırır ve {1+}%ekler1 ve bölüme .
  • 0olur [0](üzere golfed1, izlenenden değişimin parçası olarak), ancak bir dizi kalması gerekiyor çünkü ekstra ihtiyacı başka birine de başa zaman [ ].
  • Basit toplam {+}* , bölümlerin bir haline gelir ve bunların sayılarının toplanmasıyla birlikte.
  • (;`Güzel bir biçime çıktı ve koyar bölüm gelen sayımın kaldırır.

Test çerçevesi

;7000,{;
  '5'

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

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

Farklı sayıda deneme yapmak istiyorsanız ilk 7000'i değiştirin. Bunun çevrimiçi bir demo için çok yavaş olduğunu unutmayın.


3

Java, 285 267 bayt

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

Bu, TheBestOne'un cevabı ile aynı yöntemdir, ancak harita yerine basit bir dizi kullanır. Ayrıca, rastgele bölümü a olarak döndürmek yerine List, konsola yazdırır.

Aşağıda 100000 kez çalıştıran bir test programı bulunmaktadır. Örneğin n=5, tüm setler son koşumda mükemmel bir 1/7 değerinin% 0.64'ündeydi.

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
Eğer golfed etsem de Math.minaşağı çağrısı (k<n?k:n), bunu tamamen terkediyor ve sadece iki çek yaparak daha ileri gidebilir: b<k&b++<n. Ayrıca n>0, döngünün bir kısmını koşullu olarak kolayca atabilirsiniz (çünkü negatif olmayan ne zaman garanti edildiği zaman n>0&b<nazalır ). b<nb
Peter Taylor

@PeterTaylor Teşekkürler. Başka bir bakış attığımda ekstra iade beyanından ve ayrı intbeyandan kurtulmama izin verin .
Geobits

3

CJam, 64 56 bayt

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

Bu komut dosyasıyla test edebilirsiniz:

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

açıklama

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
Cevabınızın yanlış "çok iyi golf oynamadı" kısmını kaldırmalısınız;)
anatolyg

@anatolyg Kaldırıldı. Ancak bazı baytları kaldırmanın hala mümkün olduğuna inanıyorum. Bunu yapmak için çok tembelim.
jimmy23013

3

Pyth, 64 bayt

Https://stackoverflow.com/a/2163753/4230423 kullanır, ancak a) Pyth otomatik olarak not aldığı için önbellek yok, b) Listeye eklemek yerine her birini yazdırır ve c) Pyth'e çevrilir.

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

Zamanım olduğunda bunun bir açıklaması yayınlayacağım, ama burada karşılık gelen python kodu:

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

Edit: Sonunda açıklama yapmak için var:

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

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

Her bir hücrenin (m, n) en çok sayılan bölüm sayısını yansıtan bir kare matris oluşturun m, nbu nedenle atıfta bulunulan Knuth özü @feersum'a göre. Örneğin, 5,2bize 2 verir çünkü iki geçerli bölüm vardır 2,2,1ve 2,1,1,1. 6,3Bize verir için 3 3,1,1,1, 3,2,1ve3,3 .

Şimdi pinth bölümünü deterministik olarak bulabiliriz. Burada, prastgele bir sayı olarak üretiyoruz , ancak komut dosyasını biraz değiştirebilirsiniz, bu yüzden pbir parametredir:

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

Artık her sonucun yalnızca p'ye bağlı olduğunu belirleyici olarak gösterebiliriz:

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

Böylece, p'nin rastgele oluşturulduğu orijinale geri dönersek, her sonucun eşit derecede muhtemel olduğundan emin olabiliriz.


5,2 örneğinizden emin değilim. İki bölüm olmamalı (2,2,1)ve (2,1,1,1,1)(listelediğiniz ikiden daha büyük sayılara sahip olduğundan 2).
Martin Ender

Haklısın, bükülmüş şeyler var. İki bileşenli iki bölüm ve ile başlayan iki bölüm vardır 2. İkincisi demek istedim.
dcsohl

2

R, 198 bayt

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'un Octave'deki mükemmel çözümü ile aynı yapıyı takip eder ve bu nedenle Knuth özütüne dayanır. @feersum tarafından yayınlanan dayanır.

Daha sonra R'de daha yaratıcı bir çözüm bulabilirsem bunu düzenleyeceğim. Bu arada, herhangi bir girdi elbette hoş geldiniz.


1

Java, 392 bayt

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

İle ara a(n) . S Listdeğerini a döndürürInteger

Girintili'ye:

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

Dan uyarlandı Https://stackoverflow.com/a/2163753/4230423 adresinden ve golf yapıldı

: Bu nasıl çalışır Biz bir tamsayı kaç bölümleri hesaplayabilir n (O'da vardır n 2 ) süresi. Bir yan etki olarak, bu boyut O (bir tablo oluşturur , n 2 o zaman üretmek için kullanılır) k inci bölüm n bir tam sayı için, k O'da, ( n- ) zamanda.

Toplam = bölüm sayısı olsun . 0'dan toplama rasgele bir sayı k seçin - 1. k. Bölümünü oluşturun .

Her zamanki gibi , önerilerinizi bekliyoruz :)


1

Python 2,173 bayt

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]

Ardışık olarak bir sözlük yapar danahtarları ile, kbir çift temsil (n,m)göre k=67*n+m(garantili kullanılarak n<=65). Giriş bölünmesi sayısı başlık olur niçine mparçalar ve rastgele bu bölüm. Sayımlar, özyinelemeli formülle hesaplanır (işaret ettiği feersum sayesinde)

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

ve rastgele bölüm, sayısıyla orantılı olasılıkla dallarından ikisinden birini seçerek güncellenir. Güncelleme, 1ilk dal için a eklenerek ve her öğe için ikinci dal artırılarak ekleyerek yapılır .

Sınırların dışına çıkma değerlerini almakta mve nsıfır sayımı vermekte çok zorlandım . İlk başta, varsayılan olarak 0 sayıyı ve boş bir listeyi kullanan bir sözlük kullandım. Burada bir liste kullanıyorum ve bunun yerine bu varsayılan girişle dolduruyorum. Negatif endeksler listenin sonundan okunmasına neden olur, bu da varsayılan bir girişin sonuna kadar ulaştığı hiçbir şey değildir ve sarma ortamları yalnızca burada bir bölgeye dokunur m>n.


1

80386 makine kodu, 105 bayt

Onaltılık kod:

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

Bir C fonksiyonu olarak: void random_partition(int n, int result[]); . Sonucu verilen arabellekteki sayılar listesi olarak döndürür; listenin sonunu hiçbir şekilde işaretlemez, ancak kullanıcı sayıları toplayarak sonu bulabilir - toplam eşit olduğunda liste sona erern .

Nasıl kullanılır (Visual Studio'da):

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

Örnek çıktı (n = 64 ile):

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

Bu çok fazla açıklama gerektirir ...

Tabii ki herkesin kullandığı algoritmayı kullandım; karmaşıklık şartıyla herhangi bir seçenek yoktu. Bu yüzden algoritmayı çok fazla açıklamak zorunda değilim. Neyse:

Daha büyük olmayan parçalar kullanan elemanların f(n, m)bölümleme sayısı ile belirtiyorum . Bunları ilk dizinin olduğu ve ikinci dizinin (C olarak bildirildi) 2-B dizisinde saklıyorumnmf[65][64]nm-1 . Karar verdimn=65 çok fazla sorun , bu yüzden terk ettim ...

İşte bu tabloyu hesaplayan C kodu:

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

Bu kodun gizlenmiş bir stili vardır, bu yüzden montaj diline kolayca dönüştürülebilir. Öğelerin f(n, n)bölümleme sayısı olan öğelerini hesaplar n. Bu kod tamamlandığında, geçici değişken c, rasgele bir bölümleme seçmek için kullanılabilecek gerekli sayıyı içerir:

int index = rand() % c;

Daha sonra, bu indexoluşturulan tablo kullanılarak gerekli formata (sayı listesi) dönüştürülür.

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

Bu kod aynı zamanda montaj diline dönüştürmek için de optimize edilmiştir. Küçük bir "hata" vardır: bölümleme 1sonunda herhangi bir sayı içermiyorsa , son döngü karşılaşır n = 0ve gereksiz bir çıktı oluşturur1 öğe . Ancak, baskı kodu sayının toplamını izlediğinden ve bu gereksiz sayıyı yazdırmadığından zarar vermez.

Satır içi derlemeye dönüştürüldüğünde, bu kod şöyle görünür:

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

Dikkat edilmesi gereken bazı eğlenceli şeyler:

  • Rasgele bir sayı oluşturmak yalnızca 3 bayt makine kodu alır ( rdrandtalimat)
  • Tesadüf olarak, tablonun boyutu 64'tür, bu nedenle bir satırın boyutu 256 bayttır. Bunu "yüksek bayt" kayıtlarında satır endekslerini tutmak için kullanıyorum ah, bu da bana 256 ile otomatik çarpma sağlıyor. Bundan yararlanmak için, desteğini feda ettimn = 65 . Umarım bu günah için mazur görülebilirim ...
  • Yığın üzerinde alan ayırma, 0x4100'ün yığın işaretçi kaydından çıkarılmasıyla gerçekleştirilir esp. Bu 6 baytlık bir talimattır! Bu numarayı geri eklerken, 5 baytta yapmayı başardım:

        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'da bu işlevi hata ayıklarken, veri yığın üzerinde ayrılan alana yazdığında çöktüğünü öğrendim! Bazı kazmalardan sonra, bir tür yığın taşması koruması keşfettim: OS, yığın için sadece çok sınırlı bir sanal adres aralığı ayırıyor gibi görünüyor; bir işlev bir adrese çok uzak erişirse, işletim sistemi bunun bir aşırı çalışma olduğunu varsayar ve programı öldürür. Ancak, bir işlevin birçok yerel değişkeni varsa, işletim sisteminin çalışması için fazladan "sihir" yapar. Bu yüzden yığın üzerinde ayrılmış büyük bir dizi olan boş bir işlevi çağırmak zorunda. Bu işlev döndükten sonra, ek yığın VM sayfaları ayrılır ve kullanılabilir.

        void make_stack()
        {
            volatile int temp[65 * 64];
            temp[0] = 999; // have to "use" the array to prevent optimizing it out
        }
    
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.