Golf ngẫu nhiên trong ngày # 3: Phân chia số nguyên


19

Giới thiệu về bộ

Trước hết, bạn có thể coi điều này giống như bất kỳ thử thách chơi gôn mã nào khác, và trả lời nó mà không phải lo lắng về loạt bài này. Tuy nhiên, có một bảng xếp hạng trên tất cả các thách thức. Bạn có thể tìm thấy bảng xếp hạng cùng với một số thông tin khác về loạt bài trong bài đầu tiên .

Mặc dù tôi có một loạt các ý tưởng được xếp hàng cho loạt bài này, những thách thức trong tương lai vẫn chưa được đặt ra. Nếu bạn có bất kỳ đề xuất nào, xin vui lòng cho tôi biết trên bài đăng hộp cát có liên quan .

Lỗ 3: Phân vùng nguyên

Thời gian để tăng độ khó một chút.

Một phân vùng của một số nguyên dương nđược định nghĩa là một bội của các số nguyên dương mà tổng hợp với n. Như một ví dụ nếu n = 5, các phân vùng sau tồn tại:

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

Lưu ý rằng đây là nhiều trang, vì vậy không có thứ tự nào cho chúng {3,1,1}, {1,3,1}{1,1,3} tất cả đều được coi là giống hệt nhau.

Nhiệm vụ của bạn là, được đưa ra n, để tạo một phân vùng ngẫu nhiên n. Dưới đây là các quy tắc chi tiết:

  • Việc phân phối các phân vùng được sản xuất phải thống nhất . Đó là, trong ví dụ trên, mỗi phân vùng phải được trả về với xác suất 1/7.

    Tất nhiên, do những hạn chế kỹ thuật của PRNG, tính đồng nhất hoàn hảo sẽ là không thể. Với mục đích đánh giá tính đồng nhất của bài nộp của bạn, các hoạt động sau đây sẽ được coi là mang lại sự phân phối thống nhất hoàn hảo:

    • Lấy số từ PRNG (trên bất kỳ phạm vi nào), được ghi nhận là đồng nhất (xấp xỉ).
    • Ánh xạ phân bố đồng đều trên một tập hợp số lớn hơn vào một tập nhỏ hơn thông qua modulo hoặc phép nhân (hoặc một số thao tác khác phân phối các giá trị đồng đều). Tập lớn hơn phải chứa ít nhất 1024 lần giá trị có thể so với tập nhỏ hơn.
  • Vì các phân vùng là nhiều trang, bạn có thể trả lại chúng theo bất kỳ thứ tự nào và thứ tự này không nhất thiết phải nhất quán. Tuy nhiên, với mục đích phân phối ngẫu nhiên, thứ tự bị bỏ qua. Đó là, trong ví dụ trên {3,1,1}, {1,3,1}{1,1,3} cùng nhau phải có xác suất 1/7 được trả lại.

  • Thuật toán của bạn phải có thời gian chạy xác định. Cụ thể, bạn không thể tạo nhiều trang ngẫu nhiên và từ chối chúng nếu chúng không tổng hợpn .
  • Độ phức tạp thời gian của thuật toán của bạn phải là đa thức n. Cụ thể, bạn không thể đơn giản tạo tất cả các phân vùng và chọn một phân vùng ngẫu nhiên (vì số lượng phân vùng tăng theo cấp số nhân n). Bạn có thể cho rằng PRNG bạn đang sử dụng có thể trả về các giá trị được phân phối đồng đều theo O (1) cho mỗi giá trị.
  • Bạn không được sử dụng bất kỳ chức năng tích hợp nào để giải quyết công việc này.

Bạn có thể viết một chương trình đầy đủ hoặc một hàm và nhận đầu vào thông qua STDIN hoặc đối số dòng lệnh hoặc đối số hàm thay thế gần nhất và tạo đầu ra thông qua giá trị trả về hoặc bằng cách in ra STDOUT (hoặc thay thế gần nhất).

Bạn có thể giả định rằng n ≤ 65(sao cho số lượng phân vùng ít hơn 2 21 ). Đầu ra có thể ở bất kỳ định dạng chuỗi hoặc danh sách thuận tiện, rõ ràng.

Nếu bạn gửi một hàm, vui lòng xem xét việc cung cấp một chương trình thử nghiệm nhỏ gọi hàm đó một số lần và in kết quả. Không sao nếu các tham số phải được điều chỉnh trong mã. Đây chỉ là để mọi người có thể kiểm tra rằng giải pháp ít nhất là đồng nhất.

Đây là mã golf, vì vậy bài nộp ngắn nhất (tính bằng byte) sẽ thắng. Và tất nhiên, lần gửi ngắn nhất cho mỗi người dùng cũng sẽ tham gia vào bảng xếp hạng tổng thể của loạt bài.

Bảng xếp hạng

Bài đầu tiên của loạt bài tạo ra một bảng thành tích.

Để đảm bảo rằng câu trả lời của bạn hiển thị, vui lòng bắt đầu mọi câu trả lời bằng tiêu đề, sử dụng mẫu Markdown sau:

# Language Name, N bytes

nơi Nlà kích thước của trình của bạn. Nếu bạn cải thiện điểm số của mình, bạn có thể giữ điểm số cũ trong tiêu đề, bằng cách đánh bại chúng thông qua. Ví dụ:

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

(Ngôn ngữ hiện không được hiển thị, nhưng đoạn mã yêu cầu và phân tích nó và tôi có thể thêm bảng xếp hạng ngôn ngữ trong tương lai.)

Câu trả lời:


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

Tôi đã sử dụng công thức (39) từ trích xuất Knuth này , đưa ra số lượng phân vùng nmcác phần chính xác . Điều này xảy ra bằng với số lượng phân vùng nmphần tử tối đa, đó là cách hiểu tôi đang sử dụng. Các yếu tố của phân vùng được tạo ra từ lớn nhất đến nhỏ nhất. Ở mỗi giai đoạn, công thức được sử dụng lại với phần còn lại hiện tại nvà phần tử tối đa được phép.


5

APL Dyalog, 67 59 51 byte

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

plà một vectơ của vectơ trong đó p[n][k]là số lượng phân vùng nthành triệu k, hoặc tương đương: số lượng phân vùng có triệu hồi lớn nhất k. Chúng tôi xây dựng pbằng cách bắt đầu với vector trống , đọc n( đầu vào đọc) và liên tục áp dụng như sau:

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

Sau khi nứng dụng ( ⍣⎕), chúng tôi đã xây dựng p.

fchọn một phân vùng ngẫu nhiên. n f klà một phân vùng ngẫu nhiên của hầu hết các triệu hồi k . 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

Một số cải tiến:

  • nội tuyến pvới chi phí hơi kém (nhưng vẫn đủ tốt)

  • trong tính toán psắp xếp lại 1,để cứu một nhân vật

  • biến {1++/(?+/⍵)>+\⍵}thành một chuyến tàu với 1+phía trước:1+(+/(?+/)>+\)

  • tạo fmột hàm ẩn danh và cung cấp (đầu vào được đánh giá) làm đối số để có được một chương trình hoàn chỉnh

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

Kiểm tra với n = 5

Kiểm tra với n = 65

Và liên kết sau đây chạy n = 5 nghìn lần và thu thập số liệu thống kê về tần suất của mỗi phân vùng: ⎕rl←0 ⋄ {⍺,⍴⍵}⌸ {⍵=0:⍬⋄a,a∇⍵-a←1+(+/(?+/)>+\)⍺↑⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬}⍨ ¨10000⍴5


Nhiều cải tiến hơn, với sự giúp đỡ từ Roger Hui :

  • thay thế {⍵=0:A⋄B}bằng {×⍵:B⋄A}. Signum ( ×⍵) trả về true ⍵>0và false cho⍵=0 .

  • thay thế (+/(?+/)>+\)bằng+/b<?⊃⌽b←+\ , nó lưu một nhân vật

  • sử dụng ma trận thay vì vectơ của vectơ để tính p: thay thế ⍵⊃{⍵,⊂⌽1,+/¨⍵↑¨⍨⌽⍳⍴⍵}⍣⍵⊢⍬bằng ⊃↓(0,⍨⊢⍪⍨1 1⍉+\)⍣⍵⍪1.

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

kiểm tra n = 5 ; kiểm tra n = 65 ; số liệu thống kê freq


2
Làm thế nào để có được sự giúp đỡ từ Roger Hui?
FUZxxl

5
Viết một thông dịch viên APL đồ chơi để được thuê trong cùng một công ty với anh ta. Đặt biểu hiện trên như một thách thức, hứa hẹn một cốc bia cho mỗi nhân vật anh ta đưa ra. Sau đó, lợi nhuận: ít nhân vật hơn và nhiều rượu hơn khi anh ta không uống bia.
ngn

1
Tôi hiểu rồi. Đó là một chiến lược gọn gàng, hãy xem liệu tôi có thể tái tạo điều đó không. Bạn có thể hỏi anh ấy nếu Dyalog APL sẽ sớm nhận được một cái gì đó giống như J u/\. ykhông?
FUZxxl


Cảm ơn bạn đã hỏi anh ấy. Bây giờ tôi tự hỏi nếu nó cũng có thể trong thời gian tuyến tính.
FUZxxl

4

GolfScript, 90 byte

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

Bản demo trực tuyến

Đây là bản phóng tác của mã đếm phân vùng (đơn giản hơn) của tôi , thay vì chỉ theo dõi một số đếm theo dõi cả số lượng và một trong các yếu tố được chọn thống nhất.

So sánh cạnh nhau của hai:

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

Sự khác biệt:

  • Chữ cái đầu ~ là bởi vì đây là một chương trình chứ không phải là một đoạn.
  • Việc [1.]thay thế1 tương ứng với sự thay đổi trong những gì được theo dõi.
  • Phần bổ sung {(\{)}%+}%tăng từng phần tử trong phân vùng đó và {1+}%thêm 1vào phân vùng.
  • 0trở thành [0](chơi gôn1, ) như là một phần của sự thay đổi trong những gì được theo dõi, nhưng bởi vì nó cần duy trì một mảng khi được bổ sung vào một mảng khác, nó cần thêm [ ].
  • Tổng đơn giản {+}* trở thành một lựa chọn có trọng số từ các phân vùng, kết hợp với tổng của số đếm của chúng.
  • Các (;` xóa số đếm từ đầu ra và đặt phân vùng thành một định dạng đẹp.

Khung kiểm tra

;7000,{;
  '5'

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

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

Tinh chỉnh 7000 ban đầu nếu bạn muốn chạy một số thử nghiệm khác nhau. Lưu ý rằng điều này là quá chậm cho một bản demo trực tuyến.


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

Đây là phương pháp tương tự như câu trả lời của TheBestOne, nhưng nó sử dụng một mảng đơn giản thay vì bản đồ. Ngoài ra, thay vì trả về phân vùng ngẫu nhiên như mộtList , nó sẽ in chúng ra bàn điều khiển.

Dưới đây là một chương trình thử nghiệm chạy nó 100000 lần. Ví dụ n=5, tất cả các bộ nằm trong 0,64% của 1/7 hoàn hảo trong lần chạy cuối cùng của tôi.

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
Mặc dù bạn đã thực hiện Math.mincuộc gọi xuống (k<n?k:n), bạn có thể đi xa hơn bằng cách bỏ hoàn toàn cuộc gọi và chỉ cần thực hiện hai kiểm tra : b<k&b++<n. Bạn cũng có thể dễ dàng bỏ n>0phần điều kiện của vòng lặp (kể từ khi n>0&b<ngiảm xuống b<nkhi bđược đảm bảo không âm).
Peter Taylor

@PeterTaylor Cảm ơn. Nhìn lại cho phép tôi thoát khỏi tuyên bố trả lại thêm và tuyên bố riêng biệt intcũng.
Geobits 24/2/2015

3

CJam, 64 56 byte

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

Bạn có thể kiểm tra nó với kịch bản này:

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

Giải trình

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
Bạn nên loại bỏ phần "không chơi golf không chính xác" trong câu trả lời của bạn;)
anatolyg

@anatolyg Đã xóa. Nhưng tôi tin rằng vẫn có thể loại bỏ một số byte. Tôi chỉ quá lười biếng để làm điều đó.
jimmy23013

3

Bình thường, 64 byte

Sử dụng /programming//a/2163753/4230423 ngoại trừ việc a) Không có bộ đệm vì Pyth tự động ghi nhớ, b) In từng thay vì nối vào danh sách và c) được dịch sang Pyth.

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

Tôi sẽ đăng một lời giải thích về điều này khi tôi có thời gian, nhưng đây là mã python tương ứng:

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

Chỉnh sửa: Cuối cùng tôi đã có xung quanh để làm lời giải thích:

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

Tháng mười, 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

Ung dung:

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

Xây dựng một ma trận vuông trong đó mỗi ô (m, n) phản ánh số lượng phân vùng mcó số lượng lớn nhất n, theo trích dẫn Knuth @feersum rất vui lòng trích dẫn. Ví dụ: 5,2cung cấp cho chúng tôi 2 vì có hai phân vùng hợp lệ 2,2,12,1,1,1. 6,3cung cấp cho chúng tôi 3 cho 3,1,1,1, 3,2,13,3.

Bây giờ chúng ta có thể xác định tìm phân vùng p'th. Ở đây, chúng tôi đang tạo pnhư một số ngẫu nhiên nhưng bạn có thể thay đổi tập lệnh một chút để ptham số:

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

Bây giờ chúng ta có thể xác định rõ ràng rằng mỗi kết quả chỉ phụ thuộc vào 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

Do đó, quay trở lại bản gốc nơi p được tạo ngẫu nhiên, chúng ta có thể yên tâm rằng mỗi kết quả đều có khả năng như nhau.


Tôi không chắc về ví dụ 5,2 của bạn. Không nên hai phân vùng là (2,2,1)(2,1,1,1,1)(vì hai phân vùng bạn đã liệt kê có số lớn hơn 2).
Martin Ender

Bạn nói đúng, tôi có những thứ xoắn lên. Có hai phân vùng với hai thành phần và hai phân vùng bắt đầu bằng 2. Tôi có nghĩa là sau này.
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}

Ung dung:

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

Nó tuân theo cấu trúc tương tự như giải pháp tuyệt vời của @ dcsohl trong Octave , và do đó cũng dựa trên chiết xuất Knuth được đăng bởi @feersum.

Tôi sẽ chỉnh sửa điều này sau nếu tôi có thể đưa ra một giải pháp sáng tạo hơn trong R. Trong khi đó, bất kỳ đầu vào nào cũng được chào đón.


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

Gọi với a(n) . Trả về a Listcủa Integers

Thụt lề:

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

Chuyển thể từ /programming//a/2163753/4230423 và chơi gôn

Cách thức hoạt động: Chúng ta có thể tính toán có bao nhiêu phân vùng của một số nguyên n trong thời gian O ( n 2 ). Là một tác dụng phụ, điều này tạo ra một bảng có kích thước O ( n 2 ) mà sau đó chúng ta có thể sử dụng để tạo phân vùng thứ k của n , cho bất kỳ số nguyên k nào , trong O ( n thời gian ).

Vì vậy, hãy để tổng = số lượng phân vùng. Chọn một số ngẫu nhiên k từ 0 đến tổng - 1. Tạo k phân vùng thứ .

Như thường lệ , các đề xuất đều được chào đón :)


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]

Đệ quy tạo một từ điển d, với các khóa kđại diện cho một cặp (n,m)bằng cách k=67*n+m(sử dụng được bảo đảm n<=65). Mục nhập là bộ số phân vùng nthành mcác phần và phân vùng ngẫu nhiên như vậy. Tổng số được tính theo công thức đệ quy (cảm ơn vì đã chỉ ra nó)

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

và phân vùng ngẫu nhiên được cập nhật bằng cách chọn một trong hai nhánh của nó với xác suất tỷ lệ thuận với số lượng của nó. Việc cập nhật được thực hiện bằng cách thêm được thực hiện bằng cách nối thêm1 nhánh đầu tiên và tăng từng phần tử cho lần thứ hai.

Tôi đã gặp rất nhiều khó khăn khi nhận được các giá trị ngoài giới hạn mnđưa ra số không. Lúc đầu, tôi đã sử dụng một từ điển mặc định là 0 và một danh sách trống. Ở đây, tôi đang sử dụng một danh sách và đệm nó với mục mặc định này để thay thế. Các chỉ số tiêu cực làm cho danh sách được đọc từ cuối của nó, cung cấp một mục nhập mặc định là không có gì gần cuối như đã đạt được, và các bao quanh chỉ chạm vào một vùng trong đó m>n.


1

Mã máy 80386, 105 byte

Mã thập phân của mã:

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

Là một hàm C : void random_partition(int n, int result[]);. Nó trả về kết quả dưới dạng danh sách các số trong bộ đệm được cung cấp; nó không đánh dấu sự kết thúc của danh sách theo bất kỳ cách nào, nhưng người dùng có thể khám phá sự kết thúc bằng cách tích lũy các số - danh sách kết thúc khi tổng bằngn .

Cách sử dụng (trong 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');
}

Ví dụ đầu ra (với n = 64):

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

Điều này đòi hỏi nhiều lời giải thích ...

Tất nhiên tôi đã sử dụng thuật toán mà mọi người khác cũng sử dụng; không có lựa chọn nào với yêu cầu về độ phức tạp. Vì vậy, tôi không phải giải thích thuật toán quá nhiều. Dù sao:

Tôi biểu thị bằng f(n, m)số lượng phân vùng của ncác phần tử bằng cách sử dụng các phần không lớn hơn m. Tôi lưu trữ chúng trong một mảng 2 chiều (được khai báo bằng C là f[65][64]), trong đó chỉ mục đầu tiên nvà thứ hai m-1. Tôi quyết định hỗ trợn=65 là quá nhiều rắc rối, vì vậy đã từ bỏ nó ...

Đây là mã C tính toán bảng này:

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

Mã này có một số kiểu bị xáo trộn, vì vậy nó có thể được chuyển đổi sang ngôn ngữ lắp ráp dễ dàng. Nó tính toán các phần tử lên đến f(n, n), đó là số lượng phân vùng của ncác phần tử. Khi mã này kết thúc, biến tạm thời cchứa số cần thiết, có thể được sử dụng để chọn phân vùng ngẫu nhiên:

int index = rand() % c;

Sau đó, điều này indexđược chuyển đổi sang định dạng bắt buộc (danh sách các số) bằng cách sử dụng bảng được tạo.

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

Mã này cũng được tối ưu hóa để chuyển đổi sang ngôn ngữ lắp ráp. Có một "lỗi" nhỏ: nếu phân vùng không chứa bất kỳ 1số nào ở cuối, vòng lặp cuối sẽ gặp n = 0và xuất ra một 1phần tử không cần thiết . Tuy nhiên, điều đó không gây hại gì vì mã in theo dõi tổng của số và không in số không liên quan này.

Khi được chuyển đổi để lắp ráp nội tuyến, mã này trông như thế này:

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

Một số điều thú vị cần lưu ý:

  • Tạo một số ngẫu nhiên chỉ mất 3 byte mã máy ( rdrandhướng dẫn)
  • Do một sự trùng hợp ngẫu nhiên, kích thước của bảng là 64, vì vậy kích thước của một hàng là 256 byte. Tôi sử dụng điều này để giữ các chỉ số hàng trong các thanh ghi "byte cao" như thế ah, nó cho tôi nhân tự động với 256. Để tận dụng lợi thế này, tôi đã hy sinh sự hỗ trợ cho n = 65. Tôi hy vọng tôi có thể được tha cho tội lỗi này ...
  • Phân bổ không gian trên ngăn xếp được thực hiện bằng cách trừ 0x4100 khỏi thanh ghi con trỏ ngăn xếp esp. Đây là một hướng dẫn 6 byte! Khi thêm số này trở lại, tôi quản lý để làm điều đó trong 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
    
  • Khi gỡ lỗi chức năng này trong MS Visual Studio, tôi phát hiện ra rằng nó gặp sự cố khi ghi dữ liệu vào không gian được phân bổ trên ngăn xếp! Sau khi đào bới xung quanh, tôi phát hiện ra một số loại bảo vệ tràn ngăn xếp: Hệ điều hành dường như chỉ phân bổ một phạm vi địa chỉ ảo rất hạn chế cho ngăn xếp; nếu một chức năng truy cập một địa chỉ quá xa, HĐH sẽ thừa nhận rằng đó là một lỗi tràn ngập và giết chết chương trình. Tuy nhiên, nếu một hàm có nhiều biến cục bộ, HĐH sẽ thực hiện thêm một số "phép thuật" để làm cho nó hoạt động. Vì vậy, tôi phải gọi một hàm trống có một mảng lớn được phân bổ trên ngăn xếp. Sau khi hàm này trả về, các trang VM ngăn xếp bổ sung được phân bổ và có thể được sử dụng.

        void make_stack()
        {
            volatile int temp[65 * 64];
            temp[0] = 999; // have to "use" the array to prevent optimizing it out
        }
    
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.