当前位置: 首页 > news >正文

中国供应商网app下载/网络优化排名培训

中国供应商网app下载,网络优化排名培训,企业一般用什么邮箱?,任何判断网站SEO做的好坏一、废话字节数组搜索,即在一个字节数组中搜索一个更短的子数组。C#中并没有直接提供字节数组的搜索算法,而如果用Linq扩展方法的话效率又很低,且不支持通配符,本文简述了如何利用 表达式树 实现支持通配符的字节数组搜索。字节数…

一、废话

字节数组搜索,即在一个字节数组中搜索一个更短的子数组。C#中并没有直接提供字节数组的搜索算法,而如果用Linq扩展方法的话效率又很低,且不支持通配符,本文简述了如何利用 表达式树 实现支持通配符的字节数组搜索。

字节数组的搜索算法可以参考字符串的搜索算法,而且实现起来比后者更加简单高效。因为元素的范围就那么大,预处理快速且不占空间。

任何字符串搜索算法都可以使用,这里我选择了一个Sunday算法的改进版本,具体可以参考 《Sunday字符串匹配算法的效率改进》 一文,当然懒得看也没关系,各位读者老爷一定明白这类算法的核心就是(先做预处理然后)一旦发现不匹配就跳过尽可能多的元素。我在后文也会给出简单的描述。

完整的项目可以在 这里 看到。

二、算法简述

Sunday算法的思路和实现都非常简单,这个改进版本也是如此。具体来说分为两大块骤:

  1. 根据模式串Pattern的长度进行预处理,生成一个跳过表。
  2. 遍历目标数组并进行比照时,如果不匹配,则根据跳过表跳过相应数量的元素。

具体细节恕我表达能力差,直接看代码吧:

//改进的Sunday算法,被称作RoSunday 
private static int RoSunday(byte[] source, byte[] pattern)
{var count = source.Length;var patternLength = pattern.Length;var pattMaxIdx = patternLength - 1;var maxLen = count - patternLength + 1;var moveTable = new int[256];//下面两个for循环用于初始化跳过表for (int i = 0; i < 256; i++){//默认的跳过长度是Pattern的长度moveTable[i] = patternLength;}for (int i = 0; i < patternLength; i++){//针对Pattern的每个元素,计算它对应的跳过长度。//其实就是元素索引的翻转moveTable[pattern[i]] = pattMaxIdx - i;}var index = 0;while (index < maxLen){var mov = moveTable[source[index + pattMaxIdx]];if (mov < patternLength){index += mov;for (var i = 0; i < patternLength; i++){if (source[index + i] != pattern[i]){++index;continue;}return index;}}else{index += patternLength;}}return -1;
}

如果不需要支持通配符,那么上面的代码稍微优化一下就能够直接使用了。

三、支持通配符的字符串Pattern的格式设计

  • 字节数组中的每个元素必须用两位十六进制数表示,如1表示为01,16表示为10
  • 任何空格(0x20)都会被忽略。例如 010203,0102 03,01 02 03 都是等价的。
  • ? 表示通配符。它必须成对出现,或者搭配一位十六进制数。如 ?? 表示255,1? 表示0x10~0x1F , ?1 表示 0x11, 0x21, 0x31......0xF1。

这种设计的目标就是为了偷懒,便于解析。

如果你不嫌麻烦,可以给字符串Pattern设计更宽松的规则,如果你有时间而且有耐心去搞量词处理,甚至可以试着弄一个正则表达式版本,无非就是先转语法树。之前我想着搞个大新闻,花了一个星期写了个极烂的正则表达式的实现,速度慢不说,关键是大部分语法都用不到……

其实这种通配符的设计可以理解成一个只有 连(Concat) 的正则表达式,对于我来说足够了,因为就是写修改器的时候用……

四、增加通配符搜索的支持

支持通配符,无非就是把byte[]形式的Pattern换成了string形式。 在第二节的代码中,对于Pattern的内容有要求的地方只有两处:

//针对Pattern的每个元素,计算它对应的跳过长度。
//其实就是元素索引的翻转
moveTable[pattern[i]] = pattMaxIdx - i;

以及

if (source[index + i] != pattern[i]){}

为了方便说明我们先定义一个示例Patern:A1 B? ?C ??

然后先看第二处,它的作用是依次比照主串sourcepattern的元素,根据规则,描述示例Pattern的方法就不言而喻了:

if (source[index + i++] != 0xA1 || (source[index + i++] & 0xF0) != 0xB0 || (source[index + i++] & 0x0F) != 0x0C){}

原始的代码位于一个循环之内,对于想要支持通配符,这种设计肯定不行,只有把循环转化为以上的形式。至于最后的?? ,直接无视即可。

再来看第一处,初始化跳转表。对于通配符而言,它需要处理的不再是某一个元素,而是某个范围内的元素。

?? 一切都匹配:

for (int j = 0; j < 256; i++)
{moveTable[j] = badMove;
}

?C则是:

 for (int j = 0x0C; j < 256; j+=0x10)
{moveTable[j] = badMove;
}

B?则是:

 for (int j = 0xB0; j< 256; i++)
{moveTable[j] = badMove;
}

而上面三段代码中badMove的值也很好确定,还是以示例Pattern为例,这个串去掉所有空格后是 A1B??C?? ,长度为8,除以2后即是这个Pattern的“实际意义上的长度”……

没错,定义规则时我的第一原则就是方便解析……

五、Pattern的解析与生成表达式树

无论如何,Pattern必须要进行解析才能够使用。而解析完毕之后,通配符和主串中元素的比对也需要更多的操作,效率会变低。所以一个更好的办法是,在解析Pattern的同时,直接生成表达式树,然后编译成委托的形式直接调用。

微软提供的文档中 表达式树 的介绍和 API文档 相当完善, 这里就不详细介绍了,这里直接上代码。

这里的代码仅作示例,不考虑Pattern错误的情况。

我们的Pattern是支持空格分隔的,所以要先去掉空格:

pattern = pattern.Replace(" ", string.Empty);

然后将跳过表初始化为默认值:

for (int i = 0; i < 256; i++)
{moveTable[i] = pattern.Length / 2;
}

然后定义最初的表达式:

//注意这里的判断:要先确定余下的source元素数量是否大于patern的实际长度,以防止溢出。
Expression exp = Expression.LessThanOrEqual(Expression.Constant(pattern.Length / 2, typeof(int)),Expression.Subtract(Expression.ArrayLength(ExpParamSource), ExpParamSourceIndex));

设置一些常用的变量:

var patternMaxIndex = pattern.Length / 2 - 1; 
var ExpParamSource = Expression.Parameter(typeof(byte[]), "source");
var ExpParamSourceIndex = Expression.Parameter(typeof(int), "sourceIndex");
var ExpArrayItemIterator = Expression.ArrayIndex(ExpParamSource, Expression.PostIncrementAssign(ExpParamSourceIndex));

然后就开始解析Pattern,同时设置跳过表,并生成表达式树了:

for (int idx = 0; idx < pattern.Length; idx += 2) //两个一组
{badMove = patternMaxIndex - (idx / 2);//.....
}

在上面的循环中,如果pattern[idx]pattern[idx+1]表示一个数字hexNum

moveTable[hexNum] = badMove;
exp = Expression.AndAlso(exp,Expression.Equal(ExpArrayItemIterator,Expression.Constant(hexNum, typeof(byte))));

pattern[idx]pattern[idx+1]表示 ??

for (int j = 0; j < 256; i++)
{moveTable[j] = badMove;
}
exp = Expression.AndAlso(exp,Expression.Block(Expression.PreIncrementAssign(ExpParamSourceIndex),Expression.Constant(true, typeof(bool))));

pattern[idx]表示问号,而pattern[idx+1]表示一位数字lowDigit

for (int j = lowDigit; j < 256; j += 0x10)
{moveTable[j] = badMove;
}
exp = Expression.AndAlso(exp,Expression.Equal(Expression.And(ExpArrayItemIterator,Expression.Constant((byte) 0x0F, typeof(byte))),Expression.Constant((byte) lowDigit, typeof(byte))));

pattern[idx]表示一位数字highDigit,而pattern[idx+1]表示问号:

for (int j = highDigit; j< 256; i++)
{moveTable[j] = badMove;
}
exp = Expression.AndAlso(exp,Expression.Equal(Expression.And(ExpArrayItemIterator,Expression.Constant((byte) 0xF0, typeof(byte))),Expression.Constant((byte) highDigit, typeof(byte))));

关于表达式树的创建,看着代码有些多,其实很简单,就是利用 Expression.AndAlso 把每次的判断串起来……严格的说这离树还差得远呢,就是一坨连起来的二元运算。

全部工作做完之后,编译表达式:

var comparedFunc = Expression.Lambda<Func<byte[], int, bool>>(exp, ExpParamSource, ExpParamSourceIndex).Compile();

最后,把第二节的代码略作修改,作为算法框架:

private static int RoSunday(byte[] source, int[] moveTable, Func<byte[], int, bool> compareFunc, int patternLength)
{var count = source.Length;var pattMaxIdx = patternLength - 1;var maxLen = count - patternLength + 1;var index = 0;while (index < maxLen){var mov = moveTable[source[index + pattMaxIdx]];if (mov < patternLength){index += mov;if (compareFunc(source, index)) return index;++index;}else{index += patternLength;}}return -1;
}

通过这种方式实现的支持通配符的搜索,除了初始化以及第一次调用时花费的时间略长外,之后的执行都是相当快速的。

六、其他

虽然一提到字符串搜索算法很多人首先想到的就是BM,其实Sunday算法的效果意外的好。

我用 我的实现 和另一个纯C#写的标准BM实现做了速度测试,100字节~16M的千次随机数据,结果如下:

Total: bytes pattern cost is 1309.7017 , string pattern cost is 948.357200000001 , wildcard string pattern cost is 32906.4641, Boyer Moore algorithm implementation is 1440.9692
Total: bytes pattern cost is 676.126699999998 , string pattern cost is 608.047799999999 , wildcard string pattern cost is 14577.6418, Boyer Moore algorithm implementation is 929.0357

前一条是Debug下的,后一条是Release下的。显而易见的是,(这个改进的)Sunday算法优势很大,尽管通配符功能的效率又量级的差距,但也能控制在20毫秒以内,是可以接受的范围。

(全文完)

http://www.lbrq.cn/news/730783.html

相关文章:

  • 电商类网站建设需要多少钱/丁香人才网官方网站
  • 潍坊公司做网站/长沙seo霜天
  • 老河口网站设计/在百度怎么免费发布广告
  • 网站标题切换/百度指数购买
  • 微信怎么自己创建公众号/重庆seo海洋qq
  • 微信网站建设和维护/广州网站优化公司排名
  • 晋中建设集团有限公司网站/windows优化大师破解版
  • 漯河哪里做网站/2022近期重大新闻事件10条
  • 毕业设计做网站应该学什么/2023疫情第三波爆发时间
  • 怎么做qq代挂网站/白度指数
  • 怎样自己制作网站做情感顾问/网络营销的模式有哪些?
  • 青岛集团网站建设/seo优质友链购买
  • 河南建设工程一体化/百度代做seo排名
  • 网站不符合个人备案性质/百度推广关键词排名在哪看
  • 核酸检测公司上市/seo技术培训海南
  • 网站备案关闭/外贸怎么建立自己的网站
  • 建设政府网站申请/营销软文是什么
  • 黑龙江网站建设巨耀网络/行业关键词分类
  • 淘宝网的网络营销方式/网站seo教材
  • 陕西做网站电话/百度线上推广
  • 我想克隆个网站 怎么做/烟台seo
  • 自动化设计网站建设/网络推广公司收费标准
  • 做株洲网站需要多少钱/江苏搜索引擎优化
  • 宁波电商平台网站建设/外国搜索引擎登录入口
  • 西安专业做网站的公司/临沂seo排名外包
  • java网站开发平台/数据网站
  • 上海装饰公司30强排名/成都seo网络优化公司
  • 在日本做色情网站/求职seo推荐
  • 网站搭建平台流程/怎么在百度上发布信息广告
  • 男人和女人做污的视频网站/网站正能量免费推广软件
  • 计算机内存中的整型存储奥秘、大小端字节序及其判断方法
  • 乐迪信息:AI摄像机+刮板机人员入侵检测:杜绝井下安全事故
  • Mokker AI:一键更换照片背景的AI神器
  • 【GPT入门】第52课 openwebui安装与使用
  • FCN网络结构讲解与Pytorch逐行讲解实现
  • 【问题思考】为什么需要文件后缀?(gemini完成)