Regex 用正则表达式检查数字整除性

Regex 用正则表达式检查数字整除性,regex,math,Regex,Math,给定一个十进制数N作为一个数字字符串,如何仅使用正则表达式检查它是否可被M整除,而不转换为int M=2、4、5、10是显而易见的。对于M=3,这里有一些有趣的见解: 有人能为M=7、9、11、13等提供解决方案吗?普通的 测试代码(使用python,但可以随意使用任何语言): 对于那些好奇的人,这里有一个M=3的例子(假设引擎支持递归): Upd:有关更多讨论和示例,请参见此。在那里张贴的表达结果是错误的(在70*N上失败),但“如何到达那里”部分很有教育意义。尽管这个问题很有趣,但我认为除了

给定一个十进制数
N
作为一个数字字符串,如何仅使用正则表达式检查它是否可被
M
整除,而不转换为int

M=2、4、5、10是显而易见的。对于M=3,这里有一些有趣的见解:

有人能为M=7、9、11、13等提供解决方案吗?普通的

测试代码(使用python,但可以随意使用任何语言):

对于那些好奇的人,这里有一个
M=3
的例子(假设引擎支持递归):


Upd:有关更多讨论和示例,请参见此。在那里张贴的表达结果是错误的(在70*N上失败),但“如何到达那里”部分很有教育意义。

尽管这个问题很有趣,但我认为除了你列出的“明显的”问题之外,其他任何问题都不可能

大多数问题都需要数学处理


您可以使用a,这样您就可以将成对的“明显的”组合在一起(2x3、3x5等):

使用
\b\w{6}\b
很容易匹配一个6个字母的单词。匹配一个单词 包含“cat”同样容易:
\b\w*cat\w*\b

将这两者结合起来,我们得到:
(?=\b\w{6}\b)\b\w*cat\w*\b
分析这一点 带有RegexBuddy的正则表达式。容易的!这就是它的工作原理。在 字符串中尝试正则表达式的每个字符位置, 引擎将首先在正向前瞻中尝试正则表达式。 只有当 字符串中的当前字符位置位于6个字母的开头 字符串中的单词。否则,前瞻将失败,引擎将停止运行 将从下一个字符开始继续尝试正则表达式 在字符串中的位置


如果您的数字是基于一元数的,则可以使用以下正则表达式:
s/1{divisior}//g
,然后测试数字是否为空

下面是一种Perl方法

my @divs = (2,3,5,7,11,13);
for my $num(2..26) {
    my $unary = '1'x$num; # convert num to unary
    print "\n$num can be divided by : ";
    for(@divs) {
        my $test = $unary;
        $test =~ s/1{$_}//g;
        print "$_, " unless $test;
    }
}
输出:

2 can be divided by : 2, 
3 can be divided by : 3, 
4 can be divided by : 2, 
5 can be divided by : 5, 
6 can be divided by : 2, 3, 
7 can be divided by : 7, 
8 can be divided by : 2, 
9 can be divided by : 3, 
10 can be divided by : 2, 5, 
11 can be divided by : 11, 
12 can be divided by : 2, 3, 
13 can be divided by : 13, 
14 can be divided by : 2, 7, 
15 can be divided by : 3, 5, 
16 can be divided by : 2, 
17 can be divided by : 
18 can be divided by : 2, 3, 
19 can be divided by : 
20 can be divided by : 2, 5, 
21 can be divided by : 3, 7, 
22 can be divided by : 2, 11, 
23 can be divided by : 
24 can be divided by : 2, 3, 
25 can be divided by : 5, 
26 can be divided by : 2, 13, 

如果允许修改字符串并重复,则可以一次执行一步长除法。sed语法为7:重新应用,直到获得剩余部分。当您有单个0、1、2、3、4、5或6时停止

s/^0//
s/^7/0/
s/^8/1/
s/^9/2/
s/^([18]4|[29][18]|35|4[29]|56|63)/0/
s/^([18]5|[29][29]|36|43|5[07]|64)/1/
s/^([18]6|[29]3|3[07]|44|5[18]|65)/2/
s/^([18][07]|[29]4|3[18]|45|5[29]|66)/3/
s/^([18][18]|[29]5|3[29]|46|53|6[07])/4/
s/^([18][29]|[29]6|33|4[07]|54|6[18])/5/
s/^([18]3|[29][07]|34|4[18]|55|6[29])/6/

但这是很淡的酱汁。完全取消“regex”更容易,只需一次读入一个字符并切换到适当的状态。如果这不是一个选项,那么我怀疑你在7和13中运气不佳,尽管11可能仍然是可能的。

也许令人惊讶的结果是这样一个正则表达式总是存在的。更不令人惊讶的是,它通常没有用处

存在的结果来自于(DFA)和正则表达式之间的对应关系。让我们做一个DFA。用N表示模数(它不需要是素数),用B表示数字基数,对于普通的十进制数是10。具有N个标记为0到N-1的状态的DFA。初始状态为0。DFA的符号为数字0至B-1。状态表示输入字符串左前缀的剩余部分,当除以N时解释为整数。边表示在右侧添加数字时状态的变化。在算术上,这是状态映射S(状态,数字)=B*状态+数字(模N)。接受状态为0,因为零余数表示可整除性。所以我们有一个DFA。DFA识别的语言与正则表达式识别的语言相同,因此存在一种。因此,虽然这很有趣,但它没有帮助,因为它没有告诉您如何确定表达式

如果您想要一个通用算法,那么很容易在运行时构建这样的DFA,并通过直接计算填充其状态表。初始化只是一对运行时为O(M*N)的嵌套循环。机器识别每个输入字符的时间是恒定的。这是非常快的,但不使用regexp库,如果这是您真正需要的

在获得实际的正则表达式时,我们需要考虑。根据这个定理,我们知道B^(N-1)==1(模N)。例如,当N=7和B=10时,这意味着每一个6位数的块都相当于0范围内的某个单位数。。6为了可分割性的目的。指数可以小于N-1;一般来说,它是N的一个因子。调用块D的大小。有N个正则表达式用于D个数字的块,每个正则表达式表示模N的余数的一个特定等价类。这些表达式最多有长度O(B^D),这是大的。对于N=7,这是一组一百万个字符长的正则表达式;我想这会破坏大多数regexp库

这与示例代码中的表达式如何工作有关;表达式
(?1)
匹配的字符串等于0(mod 3)。这适用于N=3,因为10^1==1(mod 3),这意味着A0B==AB(mod 3)。当指数大于1时,情况更为复杂,但原理相同。(请注意,示例代码使用的识别器严格来说不仅仅是正则表达式。)表达式
[0369]
[147]
[258]
是模3表达式中数字0、1和2的正则表达式。一般来说,您将以类似的方式使用上面的regexp数字


我不提供代码是因为(1)编写代码的时间比这个答案要长,(2)我真的怀疑它在任何已知的实现中都不会执行。

这里是一个通用的直接递归蛮力长除法。它并没有经过优化,也绝对不是优雅的lol。它是用JavaScript编写的(下面是一个带有代码的测试html页面):


这是一个古老的问题,但现有的答案中没有一个包含代码。早在2010年,我就编写了计算这些正则表达式的代码,并且有一个到的链接,所以我认为在这里添加一个链接可能会很有用

该技术基本上与exc中所述的技术相同
2 can be divided by : 2, 
3 can be divided by : 3, 
4 can be divided by : 2, 
5 can be divided by : 5, 
6 can be divided by : 2, 3, 
7 can be divided by : 7, 
8 can be divided by : 2, 
9 can be divided by : 3, 
10 can be divided by : 2, 5, 
11 can be divided by : 11, 
12 can be divided by : 2, 3, 
13 can be divided by : 13, 
14 can be divided by : 2, 7, 
15 can be divided by : 3, 5, 
16 can be divided by : 2, 
17 can be divided by : 
18 can be divided by : 2, 3, 
19 can be divided by : 
20 can be divided by : 2, 5, 
21 can be divided by : 3, 7, 
22 can be divided by : 2, 11, 
23 can be divided by : 
24 can be divided by : 2, 3, 
25 can be divided by : 5, 
26 can be divided by : 2, 13, 
s/^0//
s/^7/0/
s/^8/1/
s/^9/2/
s/^([18]4|[29][18]|35|4[29]|56|63)/0/
s/^([18]5|[29][29]|36|43|5[07]|64)/1/
s/^([18]6|[29]3|3[07]|44|5[18]|65)/2/
s/^([18][07]|[29]4|3[18]|45|5[29]|66)/3/
s/^([18][18]|[29]5|3[29]|46|53|6[07])/4/
s/^([18][29]|[29]6|33|4[07]|54|6[18])/5/
s/^([18]3|[29][07]|34|4[18]|55|6[29])/6/
<!doctype html>
<html>
<head>
<script type="text/javascript">
function isNDivisibleByM(N, M) {
    var copyN = N;
    var MLength = (""+M).length;
    var multiples = [];
    for(var x = 0; x < M; x++)
        multiples[x] = [];
    for(var i = M, x=0; i < M*10; x=0) {
        for(var j = i; j < i+M; j++, x++)
            multiples[x].push(j);
        i+=M;
    }
    var REs = [];
    for(var x = 0; x < M; x++)
        REs[x] = new RegExp("^("+multiples[x].join("|")+")");
    while(N.length >= MLength) {
        var sameLen = (N.length == MLength);
        for(var x = 0; x < M; x++)
            N = N.replace(REs[x], (x==0)?"":(""+x));            
        N = N.replace(/^0/g, "");           
        if(sameLen) break;
    }
    N = N.replace(/^0/g, "");
    var numericN = parseInt(copyN);
    if(N.length == 0) {
        if(numericN%M!=0) {
            console.error("Wrong claim: " + copyN + " NOT divisible by " + M);
        }
        return true;
    }
    if(numericN%M==0 && N.length != 0) {
        console.error("Missed claim: " + copyN + " IS divisible by " + M + " - " + N + " is: " + copyN);
    }
    return false;
}
function run() {
    alert(isNDivisibleByM((""+document.getElementById("N").value), parseInt(document.getElementById("M").value)));
}
</script>
</head>
<body>
<label>N:</label><input type="text" id="N" />
<label>M:</label><input type="text" id="M" />
<button onclick="run()">Is N divisible by M?</button>
</body>
</html>
while(numStr.length > 1) {
    numStr = numStr.replace(/^(14|21|35|42|56|63)/, "");
    numStr = numStr.replace(/^(15|22|36|43|50|64)/, "1");
    numStr = numStr.replace(/^(16|23|30|44|51|65)/, "2");
    numStr = numStr.replace(/^(10|24|31|45|52|66)/, "3");
    numStr = numStr.replace(/^(11|25|32|46|53|60)/, "4");
    numStr = numStr.replace(/^(12|26|33|40|54|61)/, "5");
    numStr = numStr.replace(/^(13|20|34|41|55|62)/, "6");
}