C++博客 :: 首页 ::  :: 联系 ::  :: 管理

用Regular Expression判定素数

Posted on 2006-08-08 12:03 chenger 阅读(530) 评论(0)  编辑 收藏 引用 所属分类: Programming Stuff
代码如下

sub is_prime {
    my ($number) = @_;
    return (1 x $number) !~ m/\A (?: 1? | (11+?)(?> \1+)) \Z/xms;
}

是不是有点匪夷所思?堪称Perl中的混乱代码(Obfuscated Code)。所以正则表达式这个玩意,用得好了不说,巨强大无比,可是晦涩起来也不输于机器码。我现在对Perl了解不多,上面这行正则表达式就让我郁闷了很久,刚看到就蒙了:?:和?>是什么东西?赶紧翻出Programming Perl看,原来是Perl的Extented Regular Expression……恶补了一阵之后回头来琢磨,终于大致明白其中的道理。

原理其实很简单,就是最原始的方法:如果要决定正整数N是不是素数,就拿小于N的正整数(从2开始)挨个去除,如果发现除尽则表明是合数。当然,0和1要特殊处理,他们两个都不是素数,前面正则表达式中的1?就是为了这个目的。而(11+?)(?> \1+)就开始了循环检验的过程。这其中的过程很有意思,最好是拿Perl的re模块来debug一下,只要在文件开头加上use re"debug"就成了,执行时会详细的输出整个匹配的过程。我们现在就来跟踪一下,在匹配时到底发生了什么好玩的事情。

(1 x $number)创建了一个长度为$number,全部由'1'组成的串。首先,(11+?)可以匹配长度不小于2的全由'1'组成的字符串。假如我们拿"1111"去匹配,那么首先(11+?)会匹配整个串,并将这部分匹配到的内容保存在\1中。然后往下走,这时第二部分的要求不能满足了——(?> \1+)。暂且不管?>是什么意思,这个表达式的要求就是前面匹配到的部分重复出现一次以上(看到这里可能已经有人明白了,这不就是整除么?变态整除!),但是目前的匹配不满足要求:整个串都匹配光了,只剩下一个\Z了。所以我们唯物主义的正则表达式引擎会backtracing(回溯),往回退一步,让(11+?)只匹配"111",但这样还是不行,只好再往回退,这时你发现,\1中的内容是"11",剩下的部分也是"11",匹配成功了。其实这只是4=2*2的另一种说法。注意到函数中的匹配运算符是!~,整个函数返回0。如果一直回退到最少("11")还是匹配不了,说明$number是一个素数。

说到这里,作者的巧思确实值得佩服,虽然看穿了会觉得不过如此。然后还有两个问题没有解决,就是?>和?:到底是干什么吃的??>的意思是No backtracing,?:则是Cluster but no grouping。?>可以不要,不影响结果(可能对效率有些影响,但相信没有人会拿这种办法去判定素性的),但?:就不可缺少了。


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理