Tính xác suất để có được một nửa số đầu khi tung đồng xu.
Mục nhập cảnh sát (được đăng bởi Conor O'Brien): /codegolf//a/100521/8927
Câu hỏi ban đầu: Tính xác suất nhận được một nửa số đầu khi tung đồng xu.
Các giải pháp được đăng có một vài kỹ thuật obfuscation được áp dụng, tiếp theo là nhiều lớp của cùng một kỹ thuật obfuscation. Khi đã qua một vài thủ thuật đầu tiên, nó trở thành một nhiệm vụ đơn giản (nếu tẻ nhạt!) Để trích xuất hàm thực tế:
nCr(a,b) = a! / ((a-b)! * b!)
result = nCr(x, x/2) / 2^x
Mất một lúc để nhận ra những gì tôi đang nhìn (trong một thời gian tôi đã nghi ngờ phải làm gì đó với entropy), nhưng một khi nó xoay, tôi đã tìm thấy câu hỏi một cách dễ dàng bằng cách tìm kiếm "xác suất tung đồng xu".
Vì Conor O'Brien đã thách thức một lời giải thích sâu sắc về mã của mình, đây là một danh sách các bit thú vị hơn:
Nó bắt đầu bằng cách làm xáo trộn một số cuộc gọi chức năng tích hợp. Điều này đạt được bằng cách mã hóa cơ sở 32 tên hàm, sau đó gán chúng cho tên không gian tên toàn cục mới của một ký tự. Chỉ 'atob' thực sự được sử dụng; 2 cái còn lại chỉ là cá trích đỏ (eval có cùng tốc ký như atob, chỉ bị ghi đè và btoa đơn giản là không được sử dụng).
_=this;
[
490837, // eval -> U="undefined" -> u(x) = eval(x) (but overwritten below), y = eval
358155, // atob -> U="function (M,..." -> u(x) = atob(x)
390922 // btoa -> U="function (M,..." -> n(x) = btoa(x), U[10] = 'M'
].map(
y=function(M,i){
return _[(U=y+"")[i]] = _[M.toString(2<<2<<2)]
}
);
Tiếp theo là một vài kết hợp chuỗi tầm thường để ẩn mã. Đây là những dễ dàng đảo ngược:
u(["","GQ9ZygiYTwyPzE6YSpk","C0tYSki","SkoYSkvZChhLWIpL2QoYikg"].join("K"))
// becomes
'(d=g("a<2?1:a*d(--a)"))(a)/d(a-b)/d(b) '
u("KScpKWIsYShFLCliLGEoQyhEJyhnLGM9RSxiPUQsYT1D").split("").reverse().join("")
// becomes
"C=a,D=b,E=c,g('D(C(a,b),E(a,b))')"
Phần lớn của obfuscation là việc sử dụng g
hàm, chỉ đơn giản là định nghĩa các hàm mới. Điều này được áp dụng đệ quy, với các hàm trả về các hàm mới hoặc yêu cầu các hàm làm tham số, nhưng cuối cùng đơn giản hóa ngay. Chức năng thú vị nhất được đưa ra là:
function e(a,b){ // a! / ((a-b)! * b!) = nCr
d=function(a){return a<2?1:a*d(--a)} // Factorial
return d(a)/d(a-b)/d(b)
}
Ngoài ra còn có một mẹo cuối cùng với dòng này:
U[10]+[![]+[]][+[]][++[+[]][+[]]]+[!+[]+[]][+[]][+[]]+17..toString(2<<2<<2)
// U = "function (M,i"..., so U[10] = 'M'. The rest just evaluates to "ath", so this just reads "Math"
Mặc dù vì bit tiếp theo là ".pow (T, a)", nên luôn có khả năng nó phải là "Toán học"!
Các bước tôi đã thực hiện dọc theo lộ trình mở rộng chức năng là:
// Minimal substitutions:
function g(s){return Function("a","b","c","return "+s)};
function e(a,b,c){return (d=g("a<2?1:a*d(--a)"))(a)/d(a-b)/d(b)}
function h(a,b,c){return A=a,B=b,g('A(a,B(a))')}
function j(a,b,c){return a/b}
function L(a,b,c){return Z=a,Y=b,g('Z(a,Y)')}
k=L(j,T=2);
function F(a,b,c){return C=a,D=b,E=c,g('D(C(a,b),E(a,b))')}
RESULT=F(
h(e,k),
j,
function(a,b,c){return _['Math'].pow(T,a)}
);
// First pass
function e(a,b){
d=function(a){return a<2?1:a*d(--a)}
return d(a)/d(a-b)/d(b)
}
function h(a,b){
A=a
B=b
return function(a){
return A(a,B(a))
}
}
function j(a,b){ // ratio function
return a/b
}
function L(a,b){ // binding function (binds param b)
Z=a
Y=b
return function(a){
return Z(a,Y)
}
}
T=2; // Number of states the coin can take
k=L(j,T); // function to calculate number of heads required for fairness
function F(a,b,c){
C=a
D=b
E=c
return function(a,b,c){return D(C(a,b),E(a,b))}
}
RESULT=F(
h(e,k),
j,
function(a){return Math.pow(T,a)}
);
// Second pass
function e(a,b){...}
function k(a){
return a/2
}
function F(a,b,c){
C=a
D=b
E=c
return function(a,b,c){return D(C(a,b),E(a,b))}
}
RESULT=F(
function(a){
return e(a,k(a))
},
function(a,b){
return a/b
},
function(a){return Math.pow(2,a)}
);
// Third pass
function e(a,b) {...}
C=function(a){ // nCr(x,x/2) function
return e(a,a/2)
}
D=function(a,b){ // ratio function
return a/b
}
E=function(a){return Math.pow(2,a)} // 2^x function
RESULT=function(a,b,c){
return D(C(a,b),E(a,b))
}
Cấu trúc của chức năng lồng nhau dựa trên tiện ích; hàm "D" / "j" ngoài cùng tính toán tỷ lệ, sau đó các hàm "C" / "h" và "E" (nội tuyến) bên trong tính toán số lần lật đồng xu cần thiết. Hàm "F", được loại bỏ trong đường chuyền thứ ba, chịu trách nhiệm kết nối những thứ này lại với nhau thành một tổng thể có thể sử dụng được. Tương tự, chức năng "k" chịu trách nhiệm chọn số lượng đầu cần quan sát; một tác vụ mà nó ủy nhiệm cho hàm tỷ lệ "D" / "j" thông qua chức năng liên kết tham số "L"; sử dụng ở đây để tham số cố định b
để T
(ở đây luôn 2, là số của các quốc gia đồng xu có thể mất).
Cuối cùng, chúng tôi nhận được:
function e(a,b){ // a! / ((a-b)! * b!)
d=function(a){return a<2?1:a*d(--a)} // Factorial
return d(a)/d(a-b)/d(b)
}
RESULT=function(a){
return e(a, a/2) / Math.pow(2,a)
}