Mục đích của thách thức này là tìm ra một triển khai ngắn có thể thực hiện được của chức năng sau đây p
, trong ngôn ngữ bạn chọn. Đây là mã C triển khai nó (xem
liên kết TIO này cũng in các kết quả đầu ra của nó) và một trang wikipedia có chứa nó.
unsigned char pi[] = {
252,238,221,17,207,110,49,22,251,196,250,218,35,197,4,77,
233,119,240,219,147,46,153,186,23,54,241,187,20,205,95,193,
249,24,101,90,226,92,239,33,129,28,60,66,139,1,142,79,
5,132,2,174,227,106,143,160,6,11,237,152,127,212,211,31,
235,52,44,81,234,200,72,171,242,42,104,162,253,58,206,204,
181,112,14,86,8,12,118,18,191,114,19,71,156,183,93,135,
21,161,150,41,16,123,154,199,243,145,120,111,157,158,178,177,
50,117,25,61,255,53,138,126,109,84,198,128,195,189,13,87,
223,245,36,169,62,168,67,201,215,121,214,246,124,34,185,3,
224,15,236,222,122,148,176,188,220,232,40,80,78,51,10,74,
167,151,96,115,30,0,98,68,26,184,56,130,100,159,38,65,
173,69,70,146,39,94,85,47,140,163,165,125,105,213,149,59,
7,88,179,64,134,172,29,247,48,55,107,228,136,217,231,137,
225,27,131,73,76,63,248,254,141,83,170,144,202,216,133,97,
32,113,103,164,45,43,9,91,203,155,37,208,190,229,108,82,
89,166,116,210,230,244,180,192,209,102,175,194,57,75,99,182,
};
unsigned char p(unsigned char x) {
return pi[x];
}
Những gì là p
p
là một thành phần của hai tiêu chuẩn mật mã của Nga, cụ thể là hàm băm Streebog và mật mã khối Kuznyechik . Trong bài viết này (và trong các cuộc họp ISO), các nhà thiết kế của các thuật toán này tuyên bố rằng họ đã tạo ra mảng pi
bằng cách chọn các hoán vị 8 bit ngẫu nhiên.
Triển khai "không thể"
Có hoán vị trên 8 bit. Do đó, đối với một hoán vị ngẫu nhiên nhất định, một chương trình thực hiện nó sẽ không cần ít hơn 1683 bit.
Tuy nhiên, chúng tôi đã tìm thấy nhiều triển khai nhỏ bất thường (mà chúng tôi liệt kê ở đây ), ví dụ như chương trình C sau đây:
p(x){unsigned char*k="@`rFTDVbpPBvdtfR@\xacp?\xe2>4\xa6\xe9{z\xe3q5\xa7\xe8",l=0,b=17;while(--l&&x^1)x=2*x^x/128*285;return l%b?k[l%b]^k[b+l/b]^b:k[l/b]^188;}
chỉ chứa 158 ký tự và do đó phù hợp với 1264 bit. Nhấn vào đây để thấy rằng nó hoạt động.
Chúng tôi nói về một triển khai ngắn "không thể tưởng tượng được" bởi vì, nếu hoán vị là đầu ra của một quá trình ngẫu nhiên (theo yêu cầu của các nhà thiết kế của nó), thì một chương trình ngắn này sẽ không tồn tại (xem trang này để biết thêm chi tiết).
Thực hiện tham khảo
Một phiên bản dễ đọc hơn của mã C trước đó là:
unsigned char p(unsigned char x){
unsigned char
s[]={1,221,146,79,147,153,11,68,214,215,78,220,152,10,69},
k[]={0,32,50,6,20,4,22,34,48,16,2,54,36,52,38,18,0};
if(x != 0) {
unsigned char l=1, a=2;
while(a!=x) {
a=(a<<1)^(a>>7)*29;
l++;
}
unsigned char i = l % 17, j = l / 17;
if (i != 0) return 252^k[i]^s[j];
else return 252^k[j];
}
else return 252;
}
Bảng k
là như vậy k[x] = L(16-x)
, nơi L
là tuyến tính theo nghĩa L(x^y)==L(x)^L(y)
, và ở đâu, như trong C, ^
biểu thị XOR. Tuy nhiên, chúng tôi đã không quản lý để tận dụng tài sản này để rút ngắn việc thực hiện của chúng tôi. Chúng tôi không biết bất kỳ cấu trúc nào trong s
đó có thể cho phép thực hiện đơn giản hơn --- mặc dù đầu ra của nó luôn ở trong trường con, tức là trong đó phép lũy thừa được thực hiện trong trường hữu hạn. Tất nhiên, bạn hoàn toàn tự do sử dụng một biểu thức đơn giản hơn nếu bạn tìm thấy!s
Vòng lặp while tương ứng với việc đánh giá một logarit rời rạc trong trường hữu hạn với 256 phần tử. Nó hoạt động thông qua một tìm kiếm brute-force đơn giản: biến giả a
được đặt thành một bộ tạo của trường hữu hạn và nó được nhân với bộ tạo này cho đến khi kết quả bằng x
. Khi đó là trường hợp, chúng ta có đó l
là nhật ký rời rạc của x
. Hàm này không được định nghĩa bằng 0, do đó trường hợp đặc biệt tương ứng với if
câu lệnh.
Phép nhân của trình tạo có thể được xem là phép nhân với trong sau đó được giảm modulo đa thức . Vai trò của là để đảm bảo rằng biến vẫn ở trên 8 bit. Ngoài ra, chúng ta có thể sử dụng , trong trường hợp đó có thể là một (hoặc bất kỳ loại số nguyên nào khác). Mặt khác, cần phải bắt đầu như chúng ta cần phải có khi bằng 1.unsigned char
a
a=(a<<1)^(a>>7)*(256^29)
a
int
l=1,a=2
l=255
x
Thông tin chi tiết về các thuộc tính p
được trình bày trong bài báo của chúng tôi , với phần viết về hầu hết các tối ưu hóa của chúng tôi để có được việc thực hiện ngắn trước đó.
Quy tắc
Đề xuất một chương trình thực hiện chức năng p
trong ít hơn 1683 bit. Chương trình càng ngắn thì càng bất thường, đối với một ngôn ngữ nhất định, ngắn hơn là tốt hơn. Nếu ngôn ngữ của bạn có Kuznyechik, Streebog hoặc p
dưới dạng dựng sẵn, bạn không thể sử dụng chúng.
Số liệu chúng tôi sử dụng để xác định triển khai tốt nhất là độ dài chương trình tính bằng byte. Chúng tôi sử dụng độ dài bit trong bài báo học thuật của mình nhưng chúng tôi sử dụng byte ở đây vì mục đích đơn giản.
Nếu ngôn ngữ của bạn không có một khái niệm rõ ràng về chức năng, lập luận hoặc đầu ra, mã hóa là tùy thuộc vào bạn để xác định, nhưng thủ đoạn như mã hóa các giá trị pi[x]
như x
rõ ràng là bị cấm.
Chúng tôi đã gửi một bài nghiên cứu với những phát hiện của chúng tôi về chủ đề này. Nó có sẵn ở đây . Tuy nhiên, nếu nó được xuất bản ở một địa điểm khoa học, chúng tôi sẽ sẵn sàng thừa nhận các tác giả của các triển khai tốt nhất.
Nhân tiện, cảm ơn xnor vì sự giúp đỡ của anh ấy khi soạn thảo câu hỏi này!
1683 bits at most
một hạn chế nghiêm ngặt [sic?] Hay mục tiêu?