Golf ngẫu nhiên trong ngày # 6: Cuộn một d20


17

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 golf 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ỗ 6: Cuộn một d20

Một cái chết rất phổ biến trong các game nhập vai đỉnh cao là cái chết hai mươi mặt (một icosahedron , thường được gọi là d20 ). Nhiệm vụ của bạn là lăn một cái chết như vậy. Tuy nhiên, nếu bạn chỉ trả về một số ngẫu nhiên trong khoảng từ 1 đến 20, thì đó sẽ là một chút tầm thường. Vì vậy, nhiệm vụ của bạn là tạo ra một mạng ngẫu nhiên cho một súc sắc nhất định.

Chúng tôi sẽ sử dụng mạng sau:

nhập mô tả hình ảnh ở đây

Đó là một dải tam giác, vì vậy nó có thể dễ dàng được biểu diễn dưới dạng một danh sách các số nguyên. Ví dụ: nếu bạn được cung cấp đầu vào:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

Điều đó sẽ tương ứng với cái chết sau đây (sự thật thú vị: đây là mạng được sử dụng bởi Magic: bộ đếm cuộc sống thu thập / súc sắc quay xuống).

nhập mô tả hình ảnh ở đây

Tuy nhiên, đây không phải là mạng duy nhất đại diện cho cái chết này. Tùy thuộc vào cách chúng ta bỏ các khuôn mặt, có 60 lưới khác nhau. Đây là hai:

[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]

Hoặc đồ họa (Tôi không xoay nhãn mặt cho đơn giản):

nhập mô tả hình ảnh ở đây nhập mô tả hình ảnh ở đây

Các thách thức

Đưa ra một danh sách các số nguyên đại diện cho một khuôn (như được mô tả ở trên) và một số nguyên N, đầu ra Nđộc lập, các lưới d20 ngẫu nhiên đồng nhất tương ứng với khuôn đã cho. (Nghĩa là, mỗi trong số 60 lưới có thể sẽ có cùng xác suất được tạo.)

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ới những giả định này, thuật toán của bạn phải mang lại sự phân phối đồng đều hoàn hảo.

Chương trình của bạn sẽ có thể tạo ra 100 lưới trong chưa đầy một giây (vì vậy đừng thử tạo lưới ngẫu nhiên cho đến khi một mạng tương ứng với điểm chết được đưa ra ở trên).

Bạn có thể viết chương trình hoặc hàm, lấy đầu vào qua STDIN (hoặc thay thế gần nhất), đối số dòng lệnh hoặc đối số hàm và xuất kết quả qua tham số STDOUT (hoặc thay thế gần nhất), tham số trả về hàm hoặc tham số hàm (out).

Đầu vào và đầu ra có thể ở bất kỳ định dạng danh sách phẳng thuận tiện, rõ ràng, thuận tiện. Bạn có thể giả sử rằng các giá trị khuôn mặt của d20 là các số nguyên dương, riêng biệt, phù hợp với loại số nguyên tự nhiên của ngôn ngữ của bạn.

Đâ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.

Đầu ra mẫu

Đối với đầu vào

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]

60 lưới có thể (với điều kiện tôi không phạm sai lầm), không theo thứ tự cụ thể, là:

[11, 10, 9, 18, 19, 20, 13, 12, 3, 2, 1, 8, 7, 17, 16, 15, 14, 4, 5, 6]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20]
[8, 7, 17, 18, 9, 10, 2, 1, 5, 6, 15, 16, 20, 19, 11, 12, 3, 4, 14, 13]
[3, 12, 13, 14, 4, 5, 1, 2, 10, 11, 19, 20, 16, 15, 6, 7, 8, 9, 18, 17]
[3, 4, 5, 1, 2, 10, 11, 12, 13, 14, 15, 6, 7, 8, 9, 18, 19, 20, 16, 17]
[11, 19, 20, 13, 12, 3, 2, 10, 9, 18, 17, 16, 15, 14, 4, 5, 1, 8, 7, 6]
[4, 14, 15, 6, 5, 1, 2, 3, 12, 13, 20, 16, 17, 7, 8, 9, 10, 11, 19, 18]
[2, 10, 11, 12, 3, 4, 5, 1, 8, 9, 18, 19, 20, 13, 14, 15, 6, 7, 17, 16]
[4, 5, 1, 2, 3, 12, 13, 14, 15, 6, 7, 8, 9, 10, 11, 19, 20, 16, 17, 18]
[10, 2, 1, 8, 9, 18, 19, 11, 12, 3, 4, 5, 6, 7, 17, 16, 20, 13, 14, 15]
[3, 2, 10, 11, 12, 13, 14, 4, 5, 1, 8, 9, 18, 19, 20, 16, 15, 6, 7, 17]
[7, 8, 1, 5, 6, 15, 16, 17, 18, 9, 10, 2, 3, 4, 14, 13, 20, 19, 11, 12]
[13, 12, 11, 19, 20, 16, 15, 14, 4, 3, 2, 10, 9, 18, 17, 7, 6, 5, 1, 8]
[16, 15, 14, 13, 20, 19, 18, 17, 7, 6, 5, 4, 3, 12, 11, 10, 9, 8, 1, 2]
[15, 16, 17, 7, 6, 5, 4, 14, 13, 20, 19, 18, 9, 8, 1, 2, 3, 12, 11, 10]
[20, 13, 12, 11, 19, 18, 17, 16, 15, 14, 4, 3, 2, 10, 9, 8, 7, 6, 5, 1]
[5, 4, 14, 15, 6, 7, 8, 1, 2, 3, 12, 13, 20, 16, 17, 18, 9, 10, 11, 19]
[10, 11, 12, 3, 2, 1, 8, 9, 18, 19, 20, 13, 14, 4, 5, 6, 7, 17, 16, 15]
[4, 3, 12, 13, 14, 15, 6, 5, 1, 2, 10, 11, 19, 20, 16, 17, 7, 8, 9, 18]
[19, 20, 13, 12, 11, 10, 9, 18, 17, 16, 15, 14, 4, 3, 2, 1, 8, 7, 6, 5]
[1, 8, 9, 10, 2, 3, 4, 5, 6, 7, 17, 18, 19, 11, 12, 13, 14, 15, 16, 20]
[8, 1, 5, 6, 7, 17, 18, 9, 10, 2, 3, 4, 14, 15, 16, 20, 19, 11, 12, 13]
[18, 9, 8, 7, 17, 16, 20, 19, 11, 10, 2, 1, 5, 6, 15, 14, 13, 12, 3, 4]
[12, 3, 2, 10, 11, 19, 20, 13, 14, 4, 5, 1, 8, 9, 18, 17, 16, 15, 6, 7]
[2, 3, 4, 5, 1, 8, 9, 10, 11, 12, 13, 14, 15, 6, 7, 17, 18, 19, 20, 16]
[10, 9, 18, 19, 11, 12, 3, 2, 1, 8, 7, 17, 16, 20, 13, 14, 4, 5, 6, 15]
[9, 8, 7, 17, 18, 19, 11, 10, 2, 1, 5, 6, 15, 16, 20, 13, 12, 3, 4, 14]
[16, 17, 7, 6, 15, 14, 13, 20, 19, 18, 9, 8, 1, 5, 4, 3, 12, 11, 10, 2]
[17, 7, 6, 15, 16, 20, 19, 18, 9, 8, 1, 5, 4, 14, 13, 12, 11, 10, 2, 3]
[1, 5, 6, 7, 8, 9, 10, 2, 3, 4, 14, 15, 16, 17, 18, 19, 11, 12, 13, 20]
[9, 18, 19, 11, 10, 2, 1, 8, 7, 17, 16, 20, 13, 12, 3, 4, 5, 6, 15, 14]
[16, 20, 19, 18, 17, 7, 6, 15, 14, 13, 12, 11, 10, 9, 8, 1, 5, 4, 3, 2]
[5, 1, 2, 3, 4, 14, 15, 6, 7, 8, 9, 10, 11, 12, 13, 20, 16, 17, 18, 19]
[8, 9, 10, 2, 1, 5, 6, 7, 17, 18, 19, 11, 12, 3, 4, 14, 15, 16, 20, 13]
[13, 20, 16, 15, 14, 4, 3, 12, 11, 19, 18, 17, 7, 6, 5, 1, 2, 10, 9, 8]
[6, 15, 16, 17, 7, 8, 1, 5, 4, 14, 13, 20, 19, 18, 9, 10, 2, 3, 12, 11]
[6, 5, 4, 14, 15, 16, 17, 7, 8, 1, 2, 3, 12, 13, 20, 19, 18, 9, 10, 11]
[7, 6, 15, 16, 17, 18, 9, 8, 1, 5, 4, 14, 13, 20, 19, 11, 10, 2, 3, 12]
[19, 18, 17, 16, 20, 13, 12, 11, 10, 9, 8, 7, 6, 15, 14, 4, 3, 2, 1, 5]
[14, 15, 6, 5, 4, 3, 12, 13, 20, 16, 17, 7, 8, 1, 2, 10, 11, 19, 18, 9]
[17, 18, 9, 8, 7, 6, 15, 16, 20, 19, 11, 10, 2, 1, 5, 4, 14, 13, 12, 3]
[6, 7, 8, 1, 5, 4, 14, 15, 16, 17, 18, 9, 10, 2, 3, 12, 13, 20, 19, 11]
[14, 13, 20, 16, 15, 6, 5, 4, 3, 12, 11, 19, 18, 17, 7, 8, 1, 2, 10, 9]
[20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
[7, 17, 18, 9, 8, 1, 5, 6, 15, 16, 20, 19, 11, 10, 2, 3, 4, 14, 13, 12]
[15, 6, 5, 4, 14, 13, 20, 16, 17, 7, 8, 1, 2, 3, 12, 11, 19, 18, 9, 10]
[9, 10, 2, 1, 8, 7, 17, 18, 19, 11, 12, 3, 4, 5, 6, 15, 16, 20, 13, 14]
[2, 1, 8, 9, 10, 11, 12, 3, 4, 5, 6, 7, 17, 18, 19, 20, 13, 14, 15, 16]
[12, 13, 14, 4, 3, 2, 10, 11, 19, 20, 16, 15, 6, 5, 1, 8, 9, 18, 17, 7]
[17, 16, 20, 19, 18, 9, 8, 7, 6, 15, 14, 13, 12, 11, 10, 2, 1, 5, 4, 3]
[18, 17, 16, 20, 19, 11, 10, 9, 8, 7, 6, 15, 14, 13, 12, 3, 2, 1, 5, 4]
[18, 19, 11, 10, 9, 8, 7, 17, 16, 20, 13, 12, 3, 2, 1, 5, 6, 15, 14, 4]
[11, 12, 3, 2, 10, 9, 18, 19, 20, 13, 14, 4, 5, 1, 8, 7, 17, 16, 15, 6]
[15, 14, 13, 20, 16, 17, 7, 6, 5, 4, 3, 12, 11, 19, 18, 9, 8, 1, 2, 10]
[19, 11, 10, 9, 18, 17, 16, 20, 13, 12, 3, 2, 1, 8, 7, 6, 15, 14, 4, 5]
[12, 11, 19, 20, 13, 14, 4, 3, 2, 10, 9, 18, 17, 16, 15, 6, 5, 1, 8, 7]
[20, 16, 15, 14, 13, 12, 11, 19, 18, 17, 7, 6, 5, 4, 3, 2, 10, 9, 8, 1]
[13, 14, 4, 3, 12, 11, 19, 20, 16, 15, 6, 5, 1, 2, 10, 9, 18, 17, 7, 8]
[5, 6, 7, 8, 1, 2, 3, 4, 14, 15, 16, 17, 18, 9, 10, 11, 12, 13, 20, 19]
[14, 4, 3, 12, 13, 20, 16, 15, 6, 5, 1, 2, 10, 11, 19, 18, 17, 7, 8, 9]

Đối với bất kỳ mạng nào khác, chỉ cần thay thế mọi lần xuất hiện ibằng isố thứ trong đầu vào (trong đó idựa trên 1).

Những thách thức liên quan

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


Về cuộc thảo luận của chúng tôi, tôi đã thêm từ "phải" để làm cho nó rõ ràng hơn. Tôi nghĩ rằng nó đã đóng lỗ hổng mà tôi đã lợi dụng. Tuy nhiên, tôi nghĩ rằng cách tiếp cận tôi đã sử dụng (mặc dù không hợp lệ) là thú vị.
Cấp sông St

Điều này gần như chính xác như bài sandbox của tôi!
Rɪᴋᴇʀ

@RikerW Đó là những gì tôi nghĩ khi bạn sandbox nó. ;) (Vào thời điểm đó, của tôi ở ngay bên dưới của bạn. Tôi nghĩ rằng cái này đã truyền cảm hứng cho bạn.)
Martin Ender

Không bao giờ nhìn thấy bạn. Điều đó thật kỳ lạ, tôi nghĩ rằng tôi đã đọc tất cả những cái trên trang đầu tiên. Nhưng không, tôi đã làm cho tôi độc lập.
Rɪᴋᴇʀ

Nên có thể có tiêu đề "mở ra một d20"?
Tít

Câu trả lời:


7

Ruby, 160 byte (rev B)

17 byte được lưu nhờ các đề xuất từ ​​Martin Büttner.

->a,n{n.times{k=rand 60
%w{ABCD@GHIJKLMNEFPQRSO PFENOSRQHG@DCMLKJIAB GFPQHIA@DENOSRJKBCML}.map{|b|k.times{a=b.chars.map{|i|a[i.ord-64]}}}
k>29&&a.reverse!
p a}}

Ruby, 177 byte (rev A)

->n,a{n.times{k=rand(60)
h=->b{k.times{|j|a=(0..19).map{|i|a[b[i].ord-64]}}}
h['ABCD@GHIJKLMNEFPQRSO']
h['PFENOSRQHG@DCMLKJIAB']
h['GFPQHIA@DENOSRJKBCML']
k>29&&a.reverse!
p a}}

Giải trình

Có thể tạo ra tất cả các định hướng có thể bằng cách xoay quanh một mặt (3 lần), một đỉnh (5 lần) và hai cạnh (2 lần). Nhưng không chỉ bất kỳ khuôn mặt và các cạnh. Các trục phải có mối quan hệ chính xác và các phép quay phải được thực hiện theo đúng thứ tự, hoặc những điều kỳ lạ có thể xảy ra.

Đây là cách tôi đã làm (mặc dù tôi hiểu Martin đã làm khác đi.)

Tất cả các định hướng của một tứ diện có thể được tạo ra bởi sự kết hợp của ba hoạt động đối xứng sau đây:

a) Xoay khoảng hai trục 2 lần ở bên phải qua các điểm giữa của các cạnh (chúng nằm vuông góc với nhau. Nếu chúng ta nhân chúng với nhau, chúng ta phát hiện ra trục 2 lần thứ ba thông qua cặp cạnh còn lại.)

b) Xoay một đường chéo 3 trục với các trục 2 lần, đi qua một đỉnh và một mặt.

Đối xứng của icosahedron là một siêu đối xứng của tứ diện. Trong hình ảnh bên dưới từ https://en.wikipedia.org/wiki/Icosahedron , các mặt màu vàng và mặt đỏ xác định hai khối tứ diện khác nhau (hoặc thay vào đó là một khối tám mặt), trong khi các cạnh chung cho hai mặt màu xanh lam nằm trong ba cặp tại góc phải (và nằm trên các mặt của khối lập phương.)

Tất cả các định hướng của icosahedron có thể được tạo ra bởi các hoạt động đối xứng được đề cập ở trên cộng với một hoạt động 5 lần bổ sung.

nhập mô tả hình ảnh ở đây

Các phép quay khoảng ba trong số bốn trục được đề cập ở trên được thể hiện bằng các chuỗi ma thuật giữa các ''nhãn hiệu. Xoay quanh trục 2 lần thứ hai là khác nhau, và có thể được thực hiện chỉ bằng cách đảo ngược mảng a[].

Ungolfed trong chương trình thử nghiệm:

f=->a,n{
  n.times{                     #Iterate n times, using the result from the previous iteration to generate the next
    k=rand(60)                 #pick a random number

    h=->b{                     #helper function taking a string representing a transformation
      k.times{|j|              #which is performed on a using the number of times according to k
        a=(0..19).map{|i|a[b[i].ord-64]}
      }
    }

    #Rotate about axes k times (one 5-fold, one 3-fold, two 2-fold)
    #The first three axes have coprime rotation orders
    #And the rotations themselves take care of the modulus operation so no need to add it.
    #The second 2-fold rotation is equivalent to reversing the order
    #And is applied to the last 30 numbers as it is not coprime with the first 2-fold rotation.

    h['ABCD@GHIJKLMNEFPQRSO']  #rotate k times about 5-fold axis
    h['PFENOSRQHG@DCMLKJIAB']  #rotate k times about 3-fold axis
    h['GFPQHIA@DENOSRJKBCML']  #rotate k times about 2-fold axis
    k>29&&a.reverse!
    p a
  }
}

z=(1..20).map{|i|i} 
f[z,50]

Giải pháp thay thế 131 byte (Không hợp lệ do cách tiếp cận ngẫu nhiên nhị phân, chỉ cung cấp phân phối chính xác.)

->a,n{(n*99).times{|i|s=['@DEFGHIABCMNOPQRJKLS','ABCD@GHIJKLMNEFPQRSO'][rand(2)] 
a=(0..19).map{|i|a[s[i].ord-64]}
i%99==98&&p(a)}}

Đây là một cuộc tranh giành (giống như các chương trình được sử dụng để tranh giành khối rubik.)

Các phép quay cụ thể mà tôi sử dụng là hai trong số các phép quay rõ ràng nhất:

-Một góc quay 120 độ (khoảng 1 và 20 trên sơ đồ đầu tiên)

-Một vòng xoay 72 độ (về các đỉnh chung cho 1,2,3,4,5 và 16,17,18,19,20 trên sơ đồ đầu tiên.)

chúng ta lật một đồng xu 99 lần và mỗi lần chúng ta thực hiện một trong hai lần quay này tùy thuộc vào việc đó là đầu hay đuôi.

Lưu ý rằng xen kẽ các xác định này dẫn đến các chuỗi khá ngắn. Ví dụ, với các giác quan xoay chính xác, xoay 180 độ có thể được tạo ra với khoảng thời gian chỉ là 2.


Có vẻ như lật một đồng xu để chọn một hoạt động sẽ mang lại một cái gì đó gần với phân phối nhị thức hơn là phân phối đồng đều.
Sparr

@Sparr đó sẽ là trường hợp nếu không gian trạng thái lớn hơn bước đi ngẫu nhiên. Nhưng trong trường hợp này, việc đi bộ ngẫu nhiên chỉ 6 bước có thể mở ra tới 2 ^ 6 = 64 khả năng (tôi chưa tính chúng) và không gian trạng thái của chúng tôi chỉ là 60. Sau 99 bước (2 ^ 99 đường khác nhau) mọi thứ ít nhất phải được phân phối đồng đều như một mẫu PRNG được sử dụng để tạo ra các số.
Cấp sông St

@ MartinBüttner Cảm ơn những lời khuyên, tôi đã áp dụng những cách hiệu quả. b.mapkhông hoạt động trực tiếp, tôi cần b.chars.maplàm cho nó hoạt động (BTW không hoạt động trong máy của tôi vì tôi có Ruby 1.9.3 nhưng nó hoạt động trên Ideone.) Đó là một cách tiết kiệm hợp lý. Tôi không nghĩ việc thay đổi chuỗi ma thuật cho các ký tự không thể in được để lưu ý -64chí sẽ hoạt động: %w{}phiên dịch \n(cũng như khoảng trắng) làm dấu phân cách giữa các chuỗi trong mảng được tạo. Tôi không biết nó sẽ làm gì với các ký tự ASCII không thể in khác.
Cấp sông St

@Martin điều này khó hơn tôi mong đợi - lúc đầu tôi gặp khó khăn khi mã của tôi không hoạt động chính xác, sau đó tôi nghỉ và đột nhiên nhận ra rằng các đối xứng 2 lần và 3 lần phải có cùng mối quan hệ như trên một khối tứ diện (tôi phải thay đổi khuôn mặt hình tam giác mà tôi đang xoay cho một khuôn mặt hình tam giác khác.)
Level River St

1
Chúc mừng bạn là người dùng đầu tiên có huy hiệu hình học mới được mở khóa . :)
Martin Ender

2

Mã máy IA-32, 118 byte

Hexdump:

60 33 c0 51 8b 74 24 28 8b fa 6a 05 59 f3 a5 e8
21 00 00 00 20 c4 61 cd 6a 33 00 84 80 ad a8 33
32 00 46 20 44 8e 48 61 2d 2c 33 32 4a 00 21 20
a7 a2 90 8c 00 5b b1 04 51 0f c7 f1 83 e1 1f 49
7e f7 51 8b f2 56 8d 7c 24 e0 b1 14 f3 a4 5f 8b
f3 ac 8b ee d4 20 86 cc e3 0a 56 8d 74 04 e0 f3
a4 5e eb ed 59 e2 db 8b dd 59 e2 cc 59 83 c2 14
e2 91 61 c2 04 00

Nó hơi dài, vì vậy một số giải thích đi trước. Tôi đã sử dụng hầu hết các thuật toán tương tự như hiện câu trả lời bằng Cấp sông St . Để làm cho câu trả lời của tôi khác đi, tôi đã sử dụng các hoán vị cơ bản khác, không nhất thiết phải có ý nghĩa hình học trực quan, nhưng bằng cách nào đó thuận tiện hơn.

Mã về cơ bản phải tạo ra một hoán vị, là một ứng dụng tuần tự như sau:

  1. Hoán vị của lệnh 3, mà tôi gọi p3, áp dụng 0 ... 2 lần
  2. Hoán vị của lệnh 2, mà tôi gọi q2, áp dụng 0 hoặc 1 lần
  3. Một hoán vị của thứ tự 5, mà tôi gọi p5, áp dụng 0 ... 4 lần
  4. Một hoán vị khác của lệnh 2, mà tôi gọi p2, áp dụng 0 hoặc 1 lần

Có nhiều sự lựa chọn có thể cho những hoán vị này. Một trong số đó là như sau:

p3 = [0   4   5   6   7   8   9   1   2   3  13  14  15  16  17  18  10  11  12  19]
q2 = [4   5   6   7   0   1   2   3  13  14  15  16  17   8   9  10  11  12  19  18]
p5 = [6   7   0   4   5  14  15  16  17   8   9   1   2   3  13  12  19  18  10  11]
p2 = [1   0   7   8   9  10  11   2   3   4   5   6  16  17  18  19  12  13  14  15]

Lựa chọn này tốt hơn các lựa chọn khác vì các hoán vị ở đây có các chỉ số tuần tự dài, có thể được nén bằng mã hóa độ dài chạy - chỉ 29 byte cho 4 hoán vị.

Để đơn giản hóa việc tạo ra các số ngẫu nhiên, tôi đã chọn các quyền hạn (số lần mỗi lần hoán vị được áp dụng) trong phạm vi 1 ... 30 cho tất cả chúng. Điều này dẫn đến nhiều công việc bổ sung trong mã, bởi vì, ví dụ như p3trở thành hoán vị danh tính mỗi lần nó nhân lên gấp 3 lần. Tuy nhiên, mã nhỏ hơn theo cách đó và miễn là phạm vi chia hết cho 30, đầu ra sẽ có phân phối đồng đều.

Ngoài ra, quyền hạn phải tích cực để hoạt động giải mã độ dài chạy được thực hiện ít nhất một lần.

Mã có 4 vòng lặp lồng nhau; các phác thảo là như thế này:

void doit(int n, uint8_t* output, const uint8_t input[20])
{    
    uint8_t temp[20];

    n_loop: for i_n = 0 ... n
    {
        memcpy(output, input, 20);
        expr_loop: for i_expr = 0 ... 3
        {
            power = rand(1 ... 30);
            power_loop: for i_power = 0 ... power
            {
                memcpy(temp, output, 20);
                output_index = 0;
                perm_loop: do while length > 0
                {
                    index = ...; // decode it
                    length = ...; // decode it
                    memcpy(output + output_index, temp + index, length);
                    output_index += length;
                }
            }
        }
        output += 20;
    }
}

Tôi hy vọng mã giả này rõ ràng hơn mã lắp ráp nội tuyến bên dưới.

_declspec(naked) void __fastcall doit(int n, uint8_t* output, const uint8_t* input)
{
    _asm {
        pushad
        xor eax, eax

        n_loop:
            push ecx

            ; copy from input to output
            mov esi, [esp + 0x28]
            mov edi, edx
            push 5
            pop ecx
            rep movsd

            call end_of_data
#define rl(index, length) _emit(length * 32 + index)
            rl(0, 1)
            rl(4, 6)
            rl(1, 3)
            rl(13, 6)
            rl(10, 3)
            rl(19, 1)
            _emit(0)

            rl(4, 4)
            rl(0, 4)
            rl(13, 5)
            rl(8, 5)
            rl(19, 1)
            rl(18, 1)
            _emit(0)

            rl(6, 2)
            rl(0, 1)
            rl(4, 2)
            rl(14, 4)
            rl(8, 2)
            rl(1, 3)
            rl(13, 1)
            rl(12, 1)
            rl(19, 1)
            rl(18, 1)
            rl(10, 2)
            _emit(0)

            rl(1, 1)
            rl(0, 1)
            rl(7, 5)
            rl(2, 5)
            rl(16, 4)
            rl(12, 4)
            _emit(0)

            end_of_data:
            pop ebx ; load the address of the encoded data
            mov cl, 4

            expr_loop:
                push ecx

                make_rand:
                rdrand ecx
                and ecx, 31
                dec ecx
                jle make_rand

                ; input: ebx => encoding of permutation
                ; output: ebp => encoding of next permutation
                power_loop:
                    push ecx

                    ; copy from output to temp
                    mov esi, edx
                    push esi
                    lea edi, [esp - 0x20]
                    mov cl, 20
                    rep movsb
                    pop edi

                    ; ebx => encoding of permutation
                    ; edi => output
                    mov esi, ebx
                    perm_loop:
                        ; read a run-length
                        lodsb
                        mov ebp, esi

                        _emit(0xd4)             ; divide by 32, that is, split into
                        _emit(32)               ; index (al) and length (ah)
                        xchg cl, ah             ; set ecx = length; also makes eax = al
                        jecxz perm_loop_done    ; zero length => done decoding
                        push esi
                        lea esi, [esp + eax - 0x20]
                        rep movsb
                        pop esi
                        jmp perm_loop

                    perm_loop_done:
                    pop ecx
                    loop power_loop

                mov ebx, ebp
                pop ecx
                loop expr_loop

            pop ecx
            add edx, 20
            loop n_loop

        popad
        ret 4
    }
}

Một số chi tiết thực hiện thú vị:

  • Tôi đã sử dụng lắp ráp thụt lề, như trong các ngôn ngữ cấp cao; nếu không thì mã sẽ là một mớ hỗn độn khó hiểu
  • Tôi sử dụng callvà tiếp theo popđể truy cập dữ liệu (hoán vị được mã hóa) được lưu trữ trong mã
  • Các jecxzhướng dẫn thuận tiện cho phép tôi sử dụng một byte số không như chấm dứt cho quá trình giải mã chạy dài
  • May mắn thay, số 30 (2 * 3 * 5) gần như là sức mạnh của 2. Điều này cho phép tôi sử dụng mã ngắn để tạo một số trong phạm vi 1 ... 30:

            and ecx, 31
            dec ecx
            jle make_rand
    
  • Tôi sử dụng một lệnh "phân chia mục đích chung" ( aam) để tách một byte thành các trường bit (độ dài 3 bit và chỉ số 5 bit); may mắn, ở vị trí đó trong mã cl = 0, vì vậy tôi được hưởng lợi từ cả hai "mặt" củaxchg

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.