什么是自然区间?
每一个单位都可以顺序访问的区间就称之为自然区间。
什么是自然区间匹配?
很多时候需要验证一个值,这个值的粒度很小或者说是异构的(从另外的模块获取的)。配置这个值是否正确,我们通常会设定一个误差允许范围,然后计算每一个范围内的点的值,来验证是否正确。
应用场景:
Client给Server发送一个验证码,验证码是通过一个用户字符串和时间计算出来的,如下:
//伪代码
voidSendCode()
{
intuserString=GetUserString();//获取用户字符串
intnTimes=GetTime();//获取当前时间ms
stringcodeString=calcCode(userString,nTimes);//calcCode客户端服务器逻辑一样
send(codeString);//发送
}
Server收到这个codeString后,计算这个值是否正确,考虑到网络延时等问题,Server设定Times的允许误差在-100ms到100ms范围内,服务器有如下逻辑:
//codeString作为输入
boolverify(stringcodeString)
{
intuserString=GetUserString();//获取用户字符串
intnTimes=GetTime();//获取当前时间ms
intdeviations=100;//允许误差ms
for(inti=nTimes-deviation;i<=nTimes+deviation;i++)//可遍历的自然区间,共200次
{
if(codeString==calcCode(userString,i))//calcCode客户端服务器逻辑一样
{
returntrue;
}
}
returnfalse;
}
我们可以看到每次验证都需要进行200次的calcCode函数调用,效率很低!问题很明显,我们要进行改进!
很自然的我们会想到,使用取余的办法提前将服务器和客户端的时间归一,看图:
步骤:
1,修改客户端当前时间 A = A - (A % 100),这样A挪到了A1;
2,服务器实际上有两种情况,要嘛比客户端慢B1、B2,要嘛比客户端快B3、B4;
3,将B1,B2,B3,B4的时间归一,修改服务器当前时间 B = B - (B % 100)或者 B = B - (B % 100) + 100;
这样B1=1号点或者2号点,B2等于2号点或者3号点,B3等于3号点或者4号点,B4等于4号点或者5号点。
通过上图我们可以看到要使得误差在-100ms到100ms之间,也就是A1在坐标2,4之间的所有B值都是满足条件的。
B2和B3是误差范围内允许的值。
改进代码如下:
客户端A:
//伪代码
voidSendCode()
{
intuserString=GetUserString();//获取用户字符串
intnTimes=GetTime();//获取当前时间
intdeviations=100;//允许误差
nTimes-=nTimes%deviations;//将A点挪到A1点(将时间归一到定点)
stringcodeString=calcCode(userString,nTimes);//calcCode客户端服务器逻辑一样
send(codeString);
}
服务器B:
boolverify(stringcodeString)
{
intuserString=GetUserString();//获取用户字符串
intnTimes=GetTime();//获取当前时间
intdeviations=100;//允许误差
nTimes-=nTimes%deviations;//B(将时间归一到定点)
/*如果服务器时间慢了,B1检测1号点和2号点,B2检测2号点和3号点,B2在第二个if命中3号点
*如果服务器时间快了,B3检测3号点和4号点,B4检测4号点和5号点,B3在第一个if命中3号点
*B2和B3是对应单位坐标内的任意值,所以说误差范围内都能匹配
*执行次数从200次降低到2次
*/
if(codeString==calcCode(userString,nTimes))//calcCode客户端服务器逻辑一样
{
returntrue;
}
if(codeString==calcCode(userString,nTimes+deviations))//calcCode客户端服务器逻辑一样
{
returntrue;
}
returnfalse;
}
虽然算法很简单,但是很多时候都是自然思维敲代码,如果学会了,以后再也不用写for循环了!:) 如有问题,欢迎指出~~~