缘起:
某个网游里有个任务让玩家玩猜数字,猜对送物品,不怎么值钱
但是如果用脱机程序自动猜的话......挥钱如土,日耗万金,岂不快哉?!
找了个算法插进脱机程序里,无奈效率太低,而且耗费系统资源,一台机器挂5个号就很卡
于是自己写了一个,效果和效率要略好一些
思路:
没学过什么算法,所以换了个角度考虑,即如何找出更好的算法(而不在意它的具体实现)
显然猜数算法可分两类,确定的和非确定的.不包括随机函数或随机函数种子固定的算确定的算法,包括随机函数的算不确定算法
对于确定算法,对于同一个数字,每次猜测总是确定的,比如第一次总猜1234
可以证明,就概率来说,确定的算法一定不会比不确定的差,或者说最好算法可以在确定的算法中找到
因为对每个数字猜测总是确定的,很容易想到构件一张表来查表猜数,这样效率要提高若干倍
例如:让一个确定算法去猜所有可能的数字(从0开始就是5040个,从1开始是3024个),第一次猜1234,可能的结果有哪些?
0a0b 0a1b 0a2b...0a4b
1a0b 1a1b...1a3b
....
4a0b
一共14种可能(为什么不是5+4+3+2+1=15种?因为3a1b是不可能的...)
如此,可把5040个数字归入guess=1234的14种情况中作为第一个节点,对每种情况建立字节点,每个子节点再分14类,以次类推
这样就建立一个树,总结了所有可能,查表就能猜数
上面是说从确定算法->树.显然,每个确定算法都可以构件这么个树,于是算法之好坏可化乎树之好坏,要找好的算法就是找好的树
既然如此,完全可以抛开算法不考虑,直接去构件树
什么样的树最好?可要求他的每个节点的14个子树所包括的数字中的最大的一支也最小,意思就是此节点尽可能的平均划分了数字
(调整条件会影响结果,下面代码中用幂函数来评估,取1.25次方时效果比较好)
以此思路,不用想什么算法,直接穷举找树就完了
代码:
#define START 0
int SPECISE[14]={00,01,02,03,04,10,11,12,13,20,21,22,30,40};
struct NUM
{
char a,b,c,d;
int isvalid()
{
if(a<START || a>9)return 0;
if(b<START || b>9)return 0;
if(c<START || c>9)return 0;
if(d<START || d>9)return 0;
if(a==b||a==c||a==d)return 0;
if(b==c||b==d)return 0;
if(c==d)return 0;
return 1;
}
int in(int num)
{
a=(num/1000)%10;
b=(num/100)%10;
c=(num/10)%10;
d=(num)%10;
return isvalid();
}
NUM(){;}
NUM(int num){in(num);}
};
int check(NUM n,NUM m)
{
int A=0,B=0;
if(m.a==n.a)A++;
if(m.b==n.b)A++;
if(m.c==n.c)A++;
if(m.d==n.d)A++;
if(m.a==n.b||m.a==n.c||m.a==n.d)B++;
if(m.b==n.a||m.b==n.c||m.b==n.d)B++;
if(m.c==n.a||m.c==n.b||m.c==n.d)B++;
if(m.d==n.a||m.d==n.b||m.d==n.c)B++;
return A*10+B;
}
int specise(int check)
{
for(int i=0;i<14;i++)if(check==SPECISE[i])break;
return i;
}
int specise(NUM n,NUM m)
{
return specise(check(n,m));
}
#define POWER 1.25
struct NODE
{
int guess;
vector<int> nums;
int total;
NODE*child[14];
NODE()
{
total=0;
for(int i=0;i<14;i++)child[i]=NULL;
}
~NODE()
{
for(int i=0;i<14;i++)if(child[i])delete child[i];
}
int findguess()
{
if(nums.size()==1)
{
guess=*nums.begin();
return 1;
}
NUM n;
double max=pow(5040,5);
vector<int>::iterator vi_cur;
for(vi_cur=nums.begin();vi_cur!=nums.end();vi_cur++)
//for(int i=0;i<10000;i++)
{
int i=*vi_cur;
if(!n.in(i))continue;
int s[14]={0};
vector<int>::iterator vi;
for(vi=nums.begin();vi!=nums.end();vi++)s[specise(*vi,n)]++;
double s_max=0;
for(int j=0;j<14;j++)
{
//if(s_max<s[j])s_max=s[j];
s_max+=pow(s[j],POWER);
//s_max+=sqrt(s[j]);
}
if(s_max<max)
{
max=s_max;
guess=i;
}
}
return 1;
}
int generate()
{
if(nums.size()<=1)return 1;
int i;
vector<int>tmp_nums[14];
vector<int>::iterator vi;
for(vi=nums.begin();vi!=nums.end();vi++)
{
tmp_nums[specise(*vi,guess)].push_back(*vi);
}
for(i=0;i<14;i++)
{
if(tmp_nums[i].size()==1 && *tmp_nums[i].begin()!=guess)
{
child[i]=new NODE;
child[i]->nums=tmp_nums[i];
}
if(tmp_nums[i].size()>1)
{
child[i]=new NODE;
child[i]->nums=tmp_nums[i];
}
}
for(i=0;i<14;i++)
{
if(child[i])child[i]->findguess();
if(child[i])child[i]->generate();
}
for(i=0;i<14;i++)if(child[i])total+=child[i]->total;
total++;
return 1;
}
};
int main(int argc, char* argv[])
{
NODE*tree=new NODE;
tree->guess=1234;
NUM n;
for(int i=0;i<10000;i++)
{
if(!n.in(i))continue;
tree->nums.push_back(i);
}
tree->generate();
printf("tree has been generated!\r\n");
int times[15]={0};
for(i=0;i<10000;i++)
{
if(!n.in(i))continue;
int time=0;
NODE*node=tree;
while(1)
{
time++;
if(check(node->guess,i)==40)break;
node=node->child[specise(node->guess,i)];
}
times[time]++;
}
float amount=0;
float total=0;
for(i=1;i<15;i++)
{
amount+=times[i];
total+=times[i]*i;
printf("%d=%d\r\n",i,times[i]);
}
printf("amount=%d\r\n",(int)amount);
printf("a=%f\r\n",total/amount);
delete tree;
return 0;
}
结果:
从1开始
1=1
2=13
3=104
4=570
5=1520
6=784
7=32
平均5.008929次
从0开始
1=1
2=13
3=109
4=647
5=2072
6=1904
7=289
8=5
平均5.315278次
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)