Berechnen Sie die Wahrscheinlichkeit, halb so viele Köpfe wie Münzwürfe zu erhalten.
Cops-Eintrag (gepostet von Conor O'Brien): /codegolf//a/100521/8927
Ursprüngliche Frage: Berechnen Sie die Wahrscheinlichkeit, halb so viele Köpfe wie Münzwürfe zu erhalten.
Bei der veröffentlichten Lösung wurden mehrere Verschleierungstechniken angewendet, gefolgt von mehreren Schichten derselben Verschleierungstechnik. Nach den ersten Tricks war es eine einfache (wenn auch mühsame!) Aufgabe, die eigentliche Funktion zu extrahieren:
nCr(a,b) = a! / ((a-b)! * b!)
result = nCr(x, x/2) / 2^x
Es dauerte eine Weile, bis mir klar wurde, wonach ich suchte (ich vermutete für eine Weile, dass etwas mit Entropie zu tun hatte), aber als es sich verzog, gelang es mir, die Frage leicht zu finden, indem ich nach "Wahrscheinlichkeit des Münzwurfs" suchte.
Da Conor O'Brien eine ausführliche Erklärung seines Codes in Frage stellte, folgt hier eine Übersicht über die interessanteren Aspekte:
Zunächst werden einige integrierte Funktionsaufrufe verschleiert. Dies wird erreicht, indem die Funktionsnamen mit der Basis 32 codiert und dann neuen globalen Namespace-Namen eines einzelnen Zeichens zugewiesen werden. Es wird nur "atob" verwendet. die anderen 2 sind nur rote Heringe (eval verwendet die gleiche Abkürzung wie atob, nur um überschrieben zu werden, und btoa wird einfach nicht verwendet).
_=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)]
}
);
Als nächstes gibt es ein paar triviale Verwechslungen, um den Code zu verbergen. Diese sind leicht umzukehren:
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))')"
Der Hauptteil der Verschleierung ist die Verwendung der g
Funktion, die einfach neue Funktionen definiert. Dies wird rekursiv angewendet, wobei Funktionen neue Funktionen zurückgeben oder Funktionen als Parameter benötigen. Die interessanteste Funktion, die dabei herauskommt, ist:
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)
}
Es gibt auch einen letzten Trick mit dieser Zeile:
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"
Obwohl das nächste Bit ".pow (T, a)" ist, war es immer ziemlich wahrscheinlich, dass es "Math" sein musste!
Die Schritte, die ich unternahm, um Funktionen zu erweitern, waren:
// 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))
}
Die Struktur der Funktionsverschachtelung basiert auf dem Nutzen. Die äußerste "D" / "j" -Funktion berechnet ein Verhältnis, dann berechnen die inneren "C" / "h" - und "E" -Funktionen (Inline) die erforderlichen Münzwurfzahlen. Die im dritten Durchgang entfernte "F" -Funktion ist dafür verantwortlich, diese zu einem brauchbaren Ganzen zusammenzufügen. In ähnlicher Weise ist die "k" -Funktion für die Auswahl der Anzahl von Köpfen verantwortlich, die beobachtet werden müssen; eine Aufgabe, die sie über die Parameterbindungsfunktion "L" an die Verhältnisfunktion "D" / "j" delegiert; Hier wird der Parameter b
auf festgelegt T
(hier immer 2, dh die Anzahl der Zustände, die die Münze annehmen kann).
Am Ende bekommen wir:
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)
}