동전 던지기의 절반만큼 머리를 얻을 확률을 계산하십시오.
경찰 입장 (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 개는 단지 빨간 청어입니다 (평가는 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)"이기는하지만 항상 "Math"일 가능성이 높습니다!
기능 확장 경로를 따라 취한 단계는 다음과 같습니다.
// 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"기능은 관찰해야 할 헤드 수를 선택합니다. 파라미터 바인딩 함수 "L"을 통해 비율 함수 "D"/ "j"에 위임하는 작업; 여기에 매개 변수를 고정하는 데 사용 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)
}