Bozuk para kadar yarım kafa alma olasılığını hesaplayın.
Polis girişi (Conor O'Brien tarafından gönderildi): /codegolf//a/100521/8927
Orijinal soru: Bozuk para kadar yarım kafa alma olasılığını hesaplayın.
Kaydedilen çözelti uygulanan bir çift gizleme tekniği ve ardından aynı gizleme tekniğinin birden fazla katmanı olmuştur. İlk birkaç hileyi geçtikten sonra, gerçek işlevi çıkarmak basit (sıkıcı!) Bir görev haline geldi:
nCr(a,b) = a! / ((a-b)! * b!)
result = nCr(x, x/2) / 2^x
Neye baktığımı anlamak için bir süre aldı (bir süre entropi ile ilgili bir şeyden şüphelendim), ancak bir kez dallandıktan sonra, "madeni para fırlatma olasılığı" arayarak soruyu kolayca bulmayı başardım.
Conor O'Brien kodunun derinlemesine bir açıklamasına meydan okuduğundan, daha ilginç bitlerin bir özeti:
Bazı yerleşik işlev çağrılarını gizleyerek başlar. Bu, base-32 işlev adlarının kodlanması ve ardından tek bir karakterin yeni global ad alanı adlarına atanmasıyla elde edilir. Sadece 'atob' kullanılır; diğer 2 sadece kırmızı sürülerdir (eval atob ile aynı stenoyu alır, sadece geçersiz kılmak için ve btoa sadece kullanılmaz).
_=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)]
}
);
Sonra kodu gizlemek için birkaç önemsiz dize karışımı vardır. Bunlar kolayca tersine çevrilebilir:
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))')"
Gizlemenin büyük kısmı, g
sadece yeni işlevleri tanımlayan işlevin kullanılmasıdır. Bu, işlevler yeni işlevler döndüren veya parametreler olarak işlevler gerektiren yinelemeli olarak uygulanır, ancak sonunda hemen basitleştirilir. Bundan çıkmanın en ilginç işlevi:
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)
}
Ayrıca bu çizgide son bir hile var:
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"
Bir sonraki bit ".pow (T, a)" olduğundan, "Math" olması her zaman muhtemeldi!
Genişleyen işlevler rotası boyunca attığım adımlar:
// 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))
}
İşlev yuvalamasının yapısı faydaya dayalıdır; en dıştaki "D" / "j" işlevi bir oranı hesaplar, daha sonra iç "C" / "h" ve "E" (satır içi) işlevleri, gerekli bozuk para çevirme sayısını hesaplar. Üçüncü geçişte kaldırılan "F" işlevi, bunları kullanılabilir bir bütün olarak birleştirmekten sorumludur. Benzer şekilde "k" fonksiyonu, gözlemlenmesi gereken kafa sayısını seçmekle sorumludur; "L" parametre bağlama fonksiyonu ile "D" / "j" oran fonksiyonuna devrettiği bir görev; parametreyi düzeltmek için burada kullanılırb
için kullanılırT
(burada her zaman 2, madalyonun alabileceği durum sayısıdır).
Sonunda şunu elde ederiz:
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)
}