SUMTEC -- There's a thing in my bloglet.

But it's not only one. It's many. It's the same as other things but it exactly likes nothing else...

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
  220 随笔 :: 19 文章 :: 2014 评论 :: 22 引用

本故事根据现实经历改造,数据等比例缩放,并非真实数据。故事内容纯属虚构,如有雷同,纯属巧合。

今天的大整顿也太缺德了点,竟然在下午3点多收到信说要明天下午5点之前做好关键字过滤工作。天啊!自己假设一个小网站闹一下,虽然小打小闹,也有十几万篇文章。要是人工审核肯定要疯了——就我一个人不吃不喝不拉不撒不睡觉,连续审他个24小时,假设是15W篇,那就需要平均每秒审1.74篇。疯了……

之前也没有管这些,只好临时抱佛脚,赶紧写一个小程序对关键字做过滤。对方发了一个关键字的文本文件,根据这些文字作过滤就OK了。(顺便骂一句,制作这个文本的简直不是人,一大堆的重复数据不说,格式乱七八糟简直要人命,光整理这个就花了我1个小时。)

OK,说干就干,这个时候再到网上找什么算法是来不及了,只好自己来做。

Version1:
string[] xxxList = GetList();
foreach(Article article in GetAllArticles())
{
  foreach(string xxxWord in xxxList)
  {
    if (article.Text.Contains(xxxWord))
    {
      article.NeedManualAudit = true;
      article.Update();
      break;
    }
  }
}

不管了,先运行一下,能查多少先查多少吧。再说了,我还要整理一下那个该死的关键字列表。
十分钟后一看,乖乖龙地动!平均超过一分钟才搜索完一篇文章……比亲自上阵还要糟糕!
稍微分析了一下,发现如下几个问题:
1、关键词文件内容混乱,大量出现重复的关键词,造成无谓的重复运算;
2、关键词挑选方式太垃圾了,比如南方xxx、北方xxx、西方xxx、东方xxx,其实只要有xxx就应该要标记上,更可气的是,这个关键词列表里面甚至就有xxx这个词,那么那一大堆的南方xxx都不知道有什么存在的必要;
3、关键词数量巨大,完全不重复的关键词超过2k个条目,即使提炼关键字之后,还有将近700个关键字。
搞清楚了问题所在,基本上就可以开始动手了。

Version2:
string[] xxxList = GetList();
foreach(Article article in GetAllArticles())
{
  foreach(string xxxWord in xxxList)
  {
    if (article.Text.Contains(xxxWord))
    {
      article.NeedManualAudit = true;
      article.Update();
      break;
    }
  }
}

嗯,怎么还是一样的程序?没错,我想程序跑着再慢,也还是跑着。所以花了时间修完关键字列表之后,就用这个程序来跑。有一句话,叫做最简单的就是最美的。虽然很多时候这句话不准确,但是有的时候还是对的。比如说,你想了一个很复杂的方案,说不定你写错了就翘翘了——指不定需要花多少个Hour来拍查你的bug。这个程序虽然看着简陋,而且估计性能有问题。但怎么讲这个程序肯定是理论上正确的……

盯着程序跑了3分钟之后,发现还是不理想。总结了一下原因:
1、程序没有改变,不可能期待有质的飞跃,这个程序的时间复杂度估计应该是O(NL),即取决于关键字条目数量N和文本长度L的乘积;
2、关键字整理之后,虽然理论上关键字数量是少了,但是原来有一些关键字堆在一块,比如xxxAAAbbbCCCCddd,实际上每一个都是关键字。手动修了关键字列表之后,程序能识别出来的关键字反而增加了,扣掉重复的多余的关键字,实际上没有少多少关键字。

那怎么办好呢?已经半夜了,没时间去思考太多的东西了,最明显能够加快速度的,就是把时间复杂度从O(NL)降低到O(L),或者接近这个O(L)也可以。幸好回家路上已经有一些构思,于是

Version 3:
string[] xxxList = GetList();
List<string>[] xxxListIndexed = new List<string>[65536]; // 内存大就是好啊……386时代用QB写程序可不能这么写
// 下面这个是提速的关键,就是建立一个简单的首字符索引。
// 反正一个Unicode最多就是0~65535,所以前面直接建立了这么一个数组。
foreach(string item in xxxList)
{
   List<string> xlist = xxxListIndexed[(ushort) item[0]]
   if (xlist == null)
  {
     xlist = new List<string>();
     xxxListIndexed[(ushort) item[0]] = xlist;
  }
  xlist.Add(item);
}
// 对每一个文章做搜索
foreach(Article article in GetAllArticles())
{
  string text = article.Text;
  ScanArticle(article);
}

/*** ScanArticle函数 ***/
for (int i=0; i<text.Length; i++)
{
  // 看看当前字符是否存在首字符列表当中,如果有,在搜索首字符是这个字符的那堆关键字。
  List<string> foundKeys = xxxListIndexed[(ushort) item[0]];
  if (foundKeys != null)
  {
    foreach(string key in foundKeys)
    {
       if (text.IndexOf(key, i, 20) >=0)
      {
         article.NeedManualAudit = true;
         article.Update();
         return;
      }
    }
  }
}

这一改不得了,基本上接近于O(L)的速度了,于是立即变成每分钟扫描大概50多篇文章。其实按道理来讲,还有更加快速的算法,比如说前面那个给他作成二叉树,或者结合KMP的思想弄一下。但是实在是没有时间去做这个优化,要检索的文章数量也不大,估计优化完之前就已经检索完了。尽管如此,我还是挑了一个简单的办法去优化。虽然我说是这里面最简单的,也够复杂了,弄了一大堆的ManualResetEvent,主要是为了一些不可告人的目的(其实是说出来太复杂罗嗦了)。这个Version4就不贴出来了,主要的改进就是用了多线程,服务器有两块双核CPU,不利用上太可惜了,用上之后Version4的速度大概每分钟150篇。

到这里还没有完,这个时候感觉整个服务器的性能都已经被压榨光了——却不平衡。其实我这里用了两台服务器,前端Web一台,后端DB一台。发现DB端的CPU全满,服务端的CPU懒懒散散。赶紧分析了一下SQL语句,结果发现是索引的问题。按照分析器的建议给做了一下索引,结果DB服务器的CPU占用量就猛降一半。而且这个时候速度也更快了,达到每分钟600篇左右。

又三个小时过去了,天终于亮了,所有文章也终于搜索完毕了。辛辛苦苦熬了一晚上,对所有的工作作了一个总结:
1、鞋不在花,合脚就行
2、优化系统,综合才灵
3、斯是陋室,惟吾德馨……不是有句话么:人品问题……幸好人品好,总算按时搞定。

事后我又上网搜索了一下,没有发现什么简单的关键词/关键字过滤算法。一搜出来都是给Google百度之类用的重量级网页搜索算法,如果不是网页搜索的,就是要弄什么词干分析,中文分词,或者神经网络的。要我24小时能把这些东西堆上去,那估计我要么是这一行业的老大了,要么我就真是神经了。

大家为了对付那些所谓关键字屏蔽/过滤/审核之类的事情,有什么自己的算法或者心得体会呢?我这个简陋的算法,算是抛砖引玉吧。

posted on 2007-04-16 17:39 Sumtec 阅读(5168) 评论(19)  编辑 收藏 网摘 所属分类: 其他公司

评论

#1楼 2007-04-16 17:56 lazy[未注册用户]
没想明白:“指不定需要花多少个Hour”,这里为什么要用“Hour”而不用“小时”呢?“这个Version4就不贴出来了”,这里为什么要用“Version4”而不用“V4”或者“第4版”呢?还请不于白丁往来的鸿儒多多指教~多多指教~
  回复  引用    

#2楼 2007-04-16 18:34 deerchao      
@lazy
这个。。。
成天接触英文资料的人大多有这个习惯,不注意的话就是这样的。

  回复  引用  查看    

#3楼 2007-04-16 18:37 Wisdom-zh      
关键字列表贴出来瞧瞧,到底有多乱?
  回复  引用  查看    

#4楼 2007-04-16 18:48 奕锐[未注册用户]
挺玄乎的,
  回复  引用    

#5楼 2007-04-16 20:09 Wuya      
我也想看看到底有些什么是不允许出现的。
  回复  引用  查看    

#6楼 2007-04-16 22:15 Flier Lu      
http://www-igm.univ-mlv.fr/~lecroq/string/node1.html">http://www-igm.univ-mlv.fr/~lecroq/string/node1.html
  回复  引用  查看    

@Wisdom-zh
@Wuya
可以啊!你们的要求很容易满足,这个列表就是:
** ** ** ** *** * *** **** *** *********
******* *** *** ******* ***** ***
……忘了这里也有关键字过滤……

@Flier Lu:
Thx, 正在看。不过貌似是在一个Text里面找出一个Pattern的一个或多个匹配。这个跟关键字过滤还不是一回事:关键字过滤是在一个Text里面找出N个Pattern的一个匹配(或多个也可以)。
不过我没有仔细看啦,看完再说,不管怎么样,先谢谢了:)

  回复  引用    

http://www.***
  回复  引用    

#9楼 2007-04-17 09:32 仰天一笑      
学习一下
  回复  引用  查看    

#10楼 2007-04-17 09:35 Dongyi      
楼主的要过滤的那些文章每篇有多大
  回复  引用  查看    

@Dongyi:

大小不确定。
这是一个实际问题,实际问题跟理论问题最大的区别就是,实际问题几乎没有任何边界(你可能遇到各种情况),或者边界没有意义,理论问题通常有严格的边界限制。

事实上这个大小每篇有多大,存理论上的边界是0-4G,实际上估计集中在2k-500k。这个是一个随便估计的值,我没有做过任何的统计。因为个人认为大致知道不是超大规模的文本就可以了,在这个前提下,具体大小与算法的选择几乎没有关系。

  回复  引用    

强人啊,找一沙发慢慢看,我也在学习SEO,呵呵。这篇文章对我有帮助。
  回复  引用    

#13楼 2007-04-17 16:21 thirsty[未注册用户]
@Dongyi:

大小不确定。
这是一个实际问题,实际问题跟理论问题最大的区别就是,实际问题几乎没有任何边界(你可能遇到各种情况),或者边界没有意义,理论问题通常有严格的边界限制。

事实上这个大小每篇有多大,存理论上的边界是0-4G,实际上估计集中在2k-500k。这个是一个随便估计的值,我没有做过任何的统计。因为个人认为大致知道不是超大规模的文本就可以了,在这个前提下,具体大小与算法的选择几乎没有关系。
================
同上

  回复  引用    

#14楼 2007-04-25 19:59 Mick[未注册用户]
grep一下不就完了?
  回复  引用    

出售蓝奇高级验证码识别引擎,可准确识别新浪动网淘宝CSDN等多种复杂验证码。

输出为一个标准DLL,可供VB,VC,Delphi,C#.NET,VB.NET,模拟精灵,按键精灵等多平台调用,调用方法简单,几行代码即可完成。独具特色的边缘检测字符分离、旋转倾斜纠正和通用字符匹配算法(无论字体和大小), 使得该引擎对于像新浪、动网、淘宝、CSDN等多种验证码均有不错的识别率,是一款效果较为理想的验证码识别引擎。附详细的调用实例和代码注释等相关技术文档。

官方网站 - http://***/yzm_advocr
识别效果怎么样一试就知道 - DEMO下载 http://***/yzm_advocr/advocr.rar

  回复  引用    

#16楼 2008-06-30 15:45 U2U      
看来,不妨恶补《算法导论》。。。。。
  回复  引用  查看    

#17楼 2008-06-30 15:47 U2U      
补充:你搜索到的关键字算法是从文本中自动抓取关键字,你这个只能叫做字符匹配而已。
  回复  引用  查看    

#18楼[楼主] 2008-06-30 16:04 Sumtec      
@U2U:
这个确实叫作字符匹配,不过当时任务紧,一时间也没有转过弯来。你可以看看后面的算法,这个算法其实不咋样。

  回复  引用  查看    

可以用 Dictionary<Key, Value>
  回复  引用    




发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 715633




相关文章:

相关链接: