Calculez la probabilité d'obtenir deux fois moins de têtes que de lancers de pièces.
Entrée des flics (publiée par Conor O'Brien): /codegolf//a/100521/8927
Question initiale: Calculez la probabilité d'obtenir la moitié du nombre de têtes comme des lancers de pièces.
La solution publiée avait deux techniques d'obscurcissement appliquées, suivies de plusieurs couches de la même technique d'obscurcissement. Une fois les premières astuces passées, il est devenu simple (si fastidieux!) D'extraire la fonction réelle:
nCr(a,b) = a! / ((a-b)! * b!)
result = nCr(x, x/2) / 2^x
Il a fallu un certain temps pour réaliser ce que je regardais (pendant un moment, je soupçonnais quelque chose à voir avec l'entropie), mais une fois qu'il a brillé, j'ai réussi à trouver la question facilement en recherchant la «probabilité de lancer des pièces».
Depuis que Conor O'Brien a contesté une explication approfondie de son code, voici un aperçu des éléments les plus intéressants:
Il commence par masquer certains appels de fonction intégrés. Ceci est réalisé en codant en base 32 les noms de fonction, puis en les affectant à de nouveaux noms d'espace de noms globaux d'un seul caractère. Seul «atob» est réellement utilisé; les 2 autres sont juste des red-herrings (eval prend le même raccourci que atob, seulement pour être remplacé, et btoa n'est tout simplement pas utilisé).
_=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)]
}
);
Ensuite, il y a quelques mélanges de chaînes triviaux pour masquer le code. Ceux-ci sont facilement inversés:
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))')"
La majeure partie de l'obscurcissement est l'utilisation de la g
fonction, qui définit simplement de nouvelles fonctions. Ceci est appliqué de manière récursive, avec des fonctions renvoyant de nouvelles fonctions, ou nécessitant des fonctions en tant que paramètres, mais finalement simplifie tout de suite. La fonction la plus intéressante qui en ressort est:
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)
}
Il y a aussi une dernière astuce avec cette ligne:
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"
Bien que le bit suivant soit ".pow (T, a)", il était toujours assez probable que ce soit "Math"!
Les étapes que j'ai suivies tout au long du processus d'extension des fonctions étaient les suivantes:
// 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))
}
La structure de l'imbrication des fonctions est basée sur l'utilité; la fonction "D" / "j" la plus externe calcule un rapport, puis les fonctions "C" / "h" et "E" (en ligne) internes calculent le nombre de tours de pièces nécessaires. La fonction "F", supprimée lors de la troisième passe, est chargée de les relier entre eux en un tout utilisable. De même, la fonction "k" est chargée de choisir le nombre de têtes à observer; une tâche qu'il délègue à la fonction de rapport "D" / "j" via la fonction de liaison de paramètres "L"; utilisé ici pour le paramètre fixe b
à T
(ici toujours 2, étant le nombre d'états de la pièce peut prendre).
Au final, on obtient:
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)
}