คำนวณความน่าจะเป็นในการได้ครึ่งหนึ่งของจำนวนหัวเท่ากับการโยนเหรียญ
รายการตำรวจ (โพสต์โดย Conor O'Brien): /codegolf//a/100521/8927
คำถามเดิม: คำนวณความน่าจะเป็นในการได้ครึ่งหนึ่งของจำนวนหัวเท่า ๆ กับการโยนเหรียญ
วิธีการแก้ปัญหาที่โพสต์นั้นมีการใช้เทคนิคการทำให้งงงวยอยู่สองสามวิธีแล้วตามด้วยเทคนิคการทำให้งงที่ซ้ำกันหลายชั้น เมื่อผ่านไปสองสามเทคนิคแรกมันกลายเป็นงานง่าย ๆ (ถ้าน่าเบื่อ!) เพื่อแยกฟังก์ชั่นจริง:
nCr(a,b) = a! / ((a-b)! * b!)
result = nCr(x, x/2) / 2^x
ใช้เวลาสักครู่เพื่อตระหนักถึงสิ่งที่ฉันกำลังดู (ในขณะที่ฉันสงสัยว่าจะทำอะไรกับเอนโทรปี) แต่เมื่อมันทวีคูณฉันก็สามารถหาคำถามได้อย่างง่ายดายโดยค้นหา "ความน่าจะเป็นของการโยนเหรียญ"
เนื่องจาก Conor O'Brien ท้าทายคำอธิบายเชิงลึกของรหัสของเขานี่เป็นบทสรุปของบิตที่น่าสนใจยิ่งขึ้น:
มันเริ่มต้นจากการทำให้ฟังก์ชั่นการโทรภายในเครื่องมีปัญหา นี่คือความสำเร็จโดย base-32 การเข้ารหัสชื่อฟังก์ชั่นจากนั้นกำหนดให้กับชื่อโกลบอลเนมสเปซใหม่ของอักขระตัวเดียว ใช้เพียง 'atob' เท่านั้น อีก 2 คนเป็นเพียงปลาเฮอริ่งแดง (eval ใช้ชวเลขเดียวกันกับ atob เท่านั้นที่จะถูกแทนที่และไม่ใช้ btoa)
_=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)]
}
);
ถัดไปมีการรวมตัวกันของสตริงเล็กน้อยเพื่อซ่อนรหัส สิ่งเหล่านี้สามารถย้อนกลับได้ง่าย:
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))')"
ส่วนใหญ่ของการทำให้งงคือการใช้g
ฟังก์ชั่นซึ่งเพียงกำหนดฟังก์ชั่นใหม่ สิ่งนี้ถูกนำไปใช้ซ้ำด้วยฟังก์ชั่นที่ส่งคืนฟังก์ชั่นใหม่หรือต้องการฟังก์ชั่นเป็นพารามิเตอร์ ฟังก์ชั่นที่น่าสนใจที่สุดที่จะออกมาคือ:
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)
}
นอกจากนี้ยังมีเคล็ดลับขั้นสุดท้ายสำหรับบรรทัดนี้:
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"
แม้ว่าตั้งแต่บิตถัดไปคือ ".pow (T, a)" มันเป็นไปได้เสมอที่จะต้องเป็น "คณิตศาสตร์"!
ขั้นตอนที่ฉันใช้ไปตามเส้นทางของฟังก์ชั่นการขยายคือ:
// 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))
}
โครงสร้างของฟังก์ชั่นการซ้อนนั้นมีพื้นฐานมาจากยูทิลิตี้ ฟังก์ชั่น "D" / "j" ส่วนใหญ่จะคำนวณอัตราส่วนจากนั้นฟังก์ชั่น "C" / "h" และ "E" (อินไลน์) ภายในจะคำนวณจำนวนการพลิกเหรียญที่จำเป็น ฟังก์ชั่น "F" ซึ่งถูกลบออกในรอบที่สามมีหน้าที่เชื่อมต่อสิ่งเหล่านี้เข้าด้วยกันเป็นทั้งการใช้งาน ในทำนองเดียวกันฟังก์ชั่น "k" มีหน้าที่ในการเลือกจำนวนหัวที่จะต้องปฏิบัติตาม; งานที่มอบหมายให้ฟังก์ชั่นอัตราส่วน "D" / "j" ผ่านฟังก์ชั่นการเชื่อมพารามิเตอร์ "L"; ใช้ที่นี่เพื่อแก้ไขพารามิเตอร์b
เป็นT
(ที่นี่เสมอ 2 คือจำนวนของเหรียญที่สามารถใช้ได้)
ในท้ายที่สุดเราจะได้รับ:
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)
}