计算获得抛硬币一半头的概率。
警察入场(由Conor O'Brien发布):https : //codegolf.stackexchange.com/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)”,所以总是很有可能必须是“ 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)
}