这个CM好复杂啊,差不多用了6个小时才做出来……
这个程序就相当于一个计算器,先举个栗子讲:
随便写一个运算式 1+2
,然后我们把它改写成先运算符号,再两个操作数的形式
比如 1+2
改写成{+;1;2}
,+,1,2分别叫做运算类型,操作数1,操作数2,
表达式也可以成为操作数,比如说((2+3)*(x^4))/e^x
先展开成{/;(2+3)*(x^4);e^x}
然而数学算式是有运算顺序的,不能先算外部再算内部,因此需要把整个算式展开成数字的运算
先算value1={+;2;3}
,再算value2={^;x;4}
,然后计算value1={*;value1;value2}
,value2={^;e;x}
,最后计算result={/;value1;value2}
我们看一下GClass1的结构
public class GClass1
{
public byte byte_0;/* 运算类型,具体运算见smethod_6 */
public object object_0;/* 操作数1,可以是数字,也可以是另一个GClass1(表达式) */
public object object_1;/* 操作数2,可以是数字,也可以是另一个GClass1(表达式) */
}
这个CM的主要内容,便是用GClass的嵌套实现进行运算的目的
smethod_6是计算器的具体实现,我化简了一下标了一些备注贴在最后,看一下大概理解就行
接下来就可以开始分析Main主程序了
首先是让我们输入一串字符,这个字符串会被smethod_1转换成为一个GClass1,也就是一个数学算式
我们把输入的数学算式记作f(x)
往下看到调用了smethod_6,具体如下
object obj5 = (gclass2 != null) ? GClass0.smethod_6(gclass2, new object[]
{
num /* 此时num=0.0 */
}) : obj4
传入的num是自变量x,这就相当于obj5=f(0)
接下来是一串类型转换,直接跳过不看,可以把那段类型转换记作Object2Double
后面再次出现了熟悉的smethod_6
和Object2Double
,不过这次参与运算的表达式不是f(x)
,而是null(因为尝试把int 1转换为GClass1,转换失败得到了null)
这次smethod_6
运算的结果是1
然后有一个判断语句
/* num8=1, num7=f(0) */
if (Math.Abs((num8 - num7) / num7) >= 1E-05)
{
gclass4 = null;
}
else
{
······
}
如果if成立,那么就会执行gclass4=null,会导致后面出现NullPointerException,所以if不可以成立
这个语句看起来是在要求|(1-f(0))/f(0)|<0.00001
,但是后来想想1E-05貌似也没什么意义,应该是判断等于0的意思
所以利用数学知识可以解得一个条件f(0)=1
构造一个符合f(0)=1
的表达式之后,就可以进入else分支
看到程序内构造了一个GClass1
GClass0.GClass1 gclass5 = new GClass0.GClass1
{
byte_0 = 22,
object_0 = gclass,/* gclass就是输入的f(x) */
object_1 = 0/* 必须为0 */
};
这个byte_0=22是求导的意思,具体展开需要研究smethod_6(贴在最后了),我标了一些注释,应该能看懂
所以得到了此处的表达式d/dx(f(x))
接下来又出现了一串字符串,这个字符串会被处理成GClass(就和我们输入的内容一样)
注意,这里把gclass1中的object_1(操作数1)替换成了f(x)
根据 运算类型,操作数1,操作数2 解读GClass便可以得到第二个表达式 e^x*(2x+3)+f(x)
之后就进入了一个for循环,x的取值遍历了0-100之间每个0.1,共1000个值
d/dx(f(x))
和e^x*(2x+3)+f(x)
会被赋予自变量x的值,并且求出这两个表达式的具体结果
对这两个表达式的具体结果进行判断
/* num11 = `e^x*(2x+3)+f(x)` */
/* num7 = `d/dx(f(x))` */
if (Math.Abs((num11 - num7) / num7) >= 1E-05)
{
gclass4 = null;
goto IL_3CC;
}
这里的if同样不可以成立,并且由于if处于for循环内部,也就是说对于任意满足0<x<100的x都要有 ((e^x*(2x+3)+f(x))-(d/dx(f(x))))/(d/dx(f(x))) < 0.00001
成立
这个1E-05也是等于0的意思,因此条件可以化简为 e^x*(2x+3)+f(x) = d/dx(f(x))
然后…这个东西我就不会做了(高中并没有教导数233333)
小猿搜题拍一下,可以知道f(x)=(x^2+3x+1)*e^x
所以我们只要构造出这个表达式的GClass,然后调用CM里的smethod_3转化为字符串就行了
演示一下:
new GClass1() {
byte_0 = 18,/* 乘法 {*;x^2+3x+1;e^x} */
object_0 = new GClass1() {
byte_0=16,/* 加法 {+;x^2;3x+1} */
object_0= new GClass1()
{
byte_0 = 21,/* 乘方 {^;x;2} */
object_0 = new GClass1()
{
byte_0 = 7,/* 取值 {获取x的值} */
object_0 = 0,
object_1 = null
},
object_1 = 2
},
object_1=new GClass1() {
byte_0=16,/* 加法 {+;1;3x} */
object_0=1,
object_1=new GClass1() {
byte_0=18,/* 乘法 {*;3;x} */
object_0=3,
object_1=new GClass1()
{
byte_0 = 7,/* 取值 {获取x的值} */
object_0 = 0
}
}
}
},
object_1= new GClass1()
{
byte_0 = 20,/* e乘方 {e^;x} */
object_0 = new GClass1()
{
byte_0 = 7,/* 取值 {获取x的值} */
object_0 = 0,
object_1 = null
}
}
}
这个GClass转换成字符串的结果就是
1204100415040700000000000002000000041000010000000412000300000004070000000000041404070000000000
输入到程序里面验证一下,正确!收工了!
最后贴上简化注释过的smethod_6:
private static object smethod_6(GClass0.GClass1 gclass1_0, params object[] object_0)
{
Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>> stack = new Stack<Tuple<GClass0.GClass1, object[], object, object, bool, int>>();
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, null, null, false, 0));
object obj = null;
for (;;)
{
Tuple<GClass0.GClass1, object[], object, object, bool, int> tuple = stack.Pop();
gclass1_0 = tuple.Item1;
object_0 = tuple.Item2;
object obj2 = tuple.Item3;
object obj3 = tuple.Item4;
bool flag = tuple.Item5;
int item = tuple.Item6;
switch (item)
{
case 0:
/* item=0时,GClass1中的操作数分别放到obj2,obj3,前一步骤的运算结果obj不处理 */
obj2 = gclass1_0.object_0;
obj3 = gclass1_0.object_1;
flag = (obj2 is GClass0.GClass1);
/* 判断第一个操作数obj2是数字,还是算式 */
if (flag)
{
/* 如果obj2是算式,先Push自身item=1(具体含义见case1),并把obj2的表达式Push到stack上 */
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 1));
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj2, object_0, obj2, obj3, flag, 0));
goto IL_A66;/* 直接开始循环 */
}
flag = (obj3 is GClass0.GClass1);
/* 第一个操作数不是算式,接下来判断第二个操作数obj3是数字,还是算式 */
if (flag)
{
/* 如果obj3是算式,先Push自身item=2(具体含义见case2),并把obj3的表达式Push到stack上 */
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
goto IL_A66;
}
goto IL_1E;/* 直接开始循环 */
case 1:
if (flag)
{
obj2 = obj;/* 如果之前运算的东西是个算式,把结果从obj放入obj2(因为item=1) */
}
flag = (obj3 is GClass0.GClass1);
if (flag)
{
/* 同上,把obj3表达式Push到stack上 */
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 2));
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)obj3, object_0, obj2, obj3, flag, 0));
goto IL_A66;
}
goto IL_1E;
case 2:
if (flag)
{
obj3 = obj;/* 如果之前运算的东西是个算式,把结果从obj放入obj3(因为item=2) */
}
goto IL_1E;
case 3:
goto IL_1E;/* 不改变obj2,obj3,还保留了上一步的结果obj */
}
goto Block_101;
IL_A66:
if (stack.Count == 0)/* 如果stack上的东西都算完了 */
{
return obj;/* 返回运算结果 */
}
continue;
IL_1E:
switch (gclass1_0.byte_0)/* switch 运算类型 */
{
/* 类型判断被我精简过了,含义应该自己就能看懂 */
/* 一元运算 */
case 0:
{
obj = Object2Int(obj2);/* 把操作数1(Tuple.Item3)转为Int放到结果(obj)内 */
goto IL_A66;
}
case 1:
{
obj = Object2Long(obj2);
goto IL_A66;
}
case 2:
{
obj = Object2Float(obj2);
goto IL_A66;
}
case 3:
{
obj = Object2Double(obj2);
goto IL_A66;
}
/* 有关传入自变量(x)的运算(主要就是取出自变量的值) */
case 4:
{
obj = (int)object_0[Object2Int(obj2)];/* 理论上,obj2可以使任何整数,但是由于传入的object_0都是只含有一个元素,所以操作数1(obj2)必须为0 */
goto IL_A66;
}
case 5:
{
obj = (long)object_0[Object2Int(obj2)];
goto IL_A66;
}
case 6:
{
obj = (float)object_0[Object2Int(obj2)];
goto IL_A66;
}
case 7:
{
obj = (double)object_0[Object2Int(obj2)];
goto IL_A66;
}
/* 接下来是二元的加减乘除 */
case 16:
{
obj = Object2Double(obj2) + Object2Double(obj3);
goto IL_A66;
}
case 17:
{
obj = Object2Double(obj2) - Object2Double(obj3);
goto IL_A66;
}
case 18:
{
obj = Object2Double(obj2) * Object2Double(obj3);
goto IL_A66;
}
case 19:
{
obj = Object2Double(obj2) / Object2Double(obj3);
goto IL_A66;
}
/* 乘方类运算 */
case 20:
{
/* 计算e^obj2,e为自然底数 */
obj = Math.Exp(Object2Double(obj2));
goto IL_A66;
}
case 21:
{
/* 计算乘方 obj2^obj3 */
obj = Math.Pow(Object2Double(obj2), Object2Double(obj3));
goto IL_A66;
}
/* case22相当于计算了(f(x+10^-8)-f(x))/(10^-8),也就是计算f(x)的导数 */
case 22:/* 对于case22来说,操作数1必须是算式,Main里传递过来的是算式f(x) */
{
object obj7;
if (item != 3)/* 判断是否展开过求导运算 */
{
/* item!=3表明没有展开过,开始展开运算 */
object[] object_1 = Copy(object_0); /* Copy是伪代码,复制一下传入参数数组object_0,复制到object_1 */
obj7 = object_0[Object2Int(obj3)]; /* 把传入参数中的第(obj3)个取出(obj3只能为0) */
object_1[Object2Int(obj3)] = Object2Double(obj7) + 1E-08;/* 把取出的值加上1E-08, 即0.00000001,一个足够小的值*/
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>(gclass1_0, object_0, obj2, obj3, flag, 3));/* 此时压入f(x)表达式,item=3指示已经展开过求导运算 */
stack.Push(new Tuple<GClass0.GClass1, object[], object, object, bool, int>((GClass0.GClass1)gclass1_0.object_0, object_1, obj2, obj3, flag, 0));
/* 把f(x)展开后压入,由于object_1中的自变量被加上了1E-08,因此,实际运算的是f(x+1E-08) */
goto IL_A66;
}
obj = (Object2Double(obj) - Object2Double(obj2)) / 1E-08;/* obj是后Push的f(x+1E-08)的值;obj2是先Push的f(x)展开式的值 */
/* 最终求得导数f'(x) */
goto IL_A66;
}
}
break;
}
}