【问题标题】:regex to match irreducible fractions正则表达式匹配不可约分数
【发布时间】:2011-08-06 09:52:40
【问题描述】:

如何将irreducible fractions 与正则表达式匹配?

例如,23/25、3/4、5/2、100/101 等

首先,我对正则表达式中的gcd算法实现一无所知。

更新给所有回答如“您使用错误工具”的人:

是的,伙计们,我意识到正则表达式通常用于什么。没关系。但是这个问题很奇怪是它的重点。

更新 2: 我们的想法是找到一个在以下情况下可能有用的正则表达式:

$> echo "1/2" | grep -P regex
1/2
$> echo "2/4" | grep -P regex

所以,正则表达式应该只是一个字符串,不使用任何脚本和变量。只有正则表达式。

实际上,我已经知道一些匹配用一元数系统编写的可约分数的正则表达式。

$> echo "11/1111" | grep -P '^1/1+$|(11+)+\1+/\1+$'
11/1111

所以事情是在正则表达式中从十进制转换为一元系统,但我不知道如何。

【问题讨论】:

  • 您使用了错误工具来完成这项工作。
  • 你不能一次性完成(使用正则表达式根据算法识别数据),最好的方法是匹配任何分数提取那些然后以编程方式验证匹配的分数是否不可约基于gcd算法。
  • 哇,你的意思是正则表达式并没有像 HTML 那样演变成一门完整的编程语言?
  • 提问者可能想到了一些使用正则表达式匹配素数的味道:montreal.pm.org/tech/neil_kandalgaonkar.shtml, stackoverflow.com/questions/20448/…
  • 这对于现实世界的编程来说可能是个坏主意,但我喜欢它作为一个编程难题。

标签: regex math grep fractions


【解决方案1】:

更新

由于发帖人要求一个与“36/270”之类的字符串匹配的正则表达式,但表示它的易读性并不重要,因此该正则表达式是:

my $reducible_rx = qr{^(\d+)/(\d+)$(?(?{(1x$1."/".1x$2)=~m{^(?|1+/(1)|(11+)\1*/\1+)$}})|^)};

但是,如果像我一样,你认为一个难以辨认的正则表达式是绝对不可接受的,你会写得更清晰:

my $reducible_rx = qr{
  # first match a fraction:
    ^ ( \d+ ) / ( \d+ ) $
  # now for the hard part:
    (?(?{ ( 1 x $1 . "/" . 1 x $2 ) =~ m{
                ^
                (?|    1+      / (1)  # trivial case: GCD=1
                  |  (11+) \1* / \1+  # find the GCD
                )
                 $
            }x
        })
          # more portable version of (*PASS)
     | ^  # more portable version of (*FAIL)
     )
}x;

您可以通过将匹配一元版本的版本与匹配十进制版本的版本分开来提高可维护性,如下所示:

# this one assumes unary notation
my $unary_rx = qr{
    ^ 
    (?|   1+       / (1)
      | (11+)  \1* / \1+ 
    ) 
    $
}x;

# this one assumes decimal notation and converts internally
my $decimal_rx = qr{
  # first match a fraction:
    ^ ( \d+ ) / ( \d+ ) $ 
  # now for the hard part:
    (?(?{( 1 x $1 . "/" . 1 x $2 ) =~ $unary_rx})
          # more portable version of (*PASS)
     | ^  # more portable version of (*FAIL) 
     )
}x;

把它分成两个命名的正则表达式不是更容易吗?现在这将使$reducible_rx$decimal_rx 相同,但一元版本是它自己的东西。我就是这样做的,但最初的发布者想要一个正则表达式,所以你必须插入嵌套的一个,就像我在上面第一次介绍的那样。

无论哪种方式,您都可以使用以下方法插入测试工具:

    if ($frac =~ $reducible_rx) {
        cmp_ok($frac, "ne", reduce($i, $j), "$i/$j is $test");
    } else {
        cmp_ok($frac, "eq", reduce($i, $j), "$i/$j is $test");
    }

你会看到它是一个正确的正则表达式,它通过了所有测试,而且使用单个正则表达式,因此现在已经通过了原始问题的所有要求,我声明 Qᴜᴏᴅ ᴇʀᴀᴛ ᴅᴇᴍᴏɴsᴛʀᴀɴᴅᴜᴍ:“退出,做的够多了。 ” ?

不客气。


答案是将正则表达式^(?|1+/(1)|(11+)\1*/\1+)$ 与分数从十进制转换为一元符号后进行匹配,此时最大公因数将在匹配时在$1 中找到;否则它们是互质的。如果您使用的是 Perl 5.14 或更高版本,您甚至可以一步完成:

use 5.014;
my $reg  = qr{^(?|1+/(1)|(11+)\1*/\1+)$};
my $frac = "36/270";  # for example
if ($frac =~ s/(\d+)/1 x $1/reg =~ /$reg/) { 
    say "$frac can be reduced by ", length $1;
} else {
    say "$frac is irreducible";
}

哪个会正确报告:

36/270 can be reduced by 18

(当然,减 1 意味着不再有分母。)

如果你想和你的读者有一点双关语的乐趣,你甚至可以这样做:

use 5.014;
my $regex = qr{^(?|1+/(1)|(11+)\1*/\1+)$};
my $frac  = "36/270";  # for example
if ($frac =~ s/(\d+)/"1 x $1"/regex =~ /$regex/) {
    say "$frac can be reduced by ", length $1;
} else {
    say "$frac is irreducible";
}

这是演示如何执行此操作的代码。此外,它构建了一个测试套件,该套件使用所有(正)分子和分母(默认为 30)来测试其算法。要在测试工具下运行它,请将其放入名为 coprimes 的文件中并执行以下操作:

$ perl -MTest::Harness -e 'runtests("coprimes")'
coprimes .. ok       
All tests successful.
Files=1, Tests=900,  1 wallclock secs ( 0.13 usr  0.02 sys +  0.33 cusr  0.02 csys =  0.50 CPU)
Result: PASS

以下是在没有测试工具的情况下运行时的输出示例:

$ perl coprimes 10
1..100
ok 1 - 1/1 is 1
ok 2 - 1/2 is 1/2
ok 3 - 1/3 is 1/3
ok 4 - 1/4 is 1/4
ok 5 - 1/5 is 1/5
ok 6 - 1/6 is 1/6
ok 7 - 1/7 is 1/7
ok 8 - 1/8 is 1/8
ok 9 - 1/9 is 1/9
ok 10 - 1/10 is 1/10
ok 11 - 2/1 is 2
ok 12 - 2/2 is 1
ok 13 - 2/3 is 2/3
ok 14 - 2/4 is 1/2
ok 15 - 2/5 is 2/5
ok 16 - 2/6 is 1/3
ok 17 - 2/7 is 2/7
ok 18 - 2/8 is 1/4
ok 19 - 2/9 is 2/9
ok 20 - 2/10 is 1/5
ok 21 - 3/1 is 3
ok 22 - 3/2 is 3/2
ok 23 - 3/3 is 1
ok 24 - 3/4 is 3/4
ok 25 - 3/5 is 3/5
ok 26 - 3/6 is 1/2
ok 27 - 3/7 is 3/7
ok 28 - 3/8 is 3/8
ok 29 - 3/9 is 1/3
ok 30 - 3/10 is 3/10
ok 31 - 4/1 is 4
ok 32 - 4/2 is 2
ok 33 - 4/3 is 4/3
ok 34 - 4/4 is 1
ok 35 - 4/5 is 4/5
ok 36 - 4/6 is 2/3
ok 37 - 4/7 is 4/7
ok 38 - 4/8 is 1/2
ok 39 - 4/9 is 4/9
ok 40 - 4/10 is 2/5
ok 41 - 5/1 is 5
ok 42 - 5/2 is 5/2
ok 43 - 5/3 is 5/3
ok 44 - 5/4 is 5/4
ok 45 - 5/5 is 1
ok 46 - 5/6 is 5/6
ok 47 - 5/7 is 5/7
ok 48 - 5/8 is 5/8
ok 49 - 5/9 is 5/9
ok 50 - 5/10 is 1/2
ok 51 - 6/1 is 6
ok 52 - 6/2 is 3
ok 53 - 6/3 is 2
ok 54 - 6/4 is 3/2
ok 55 - 6/5 is 6/5
ok 56 - 6/6 is 1
ok 57 - 6/7 is 6/7
ok 58 - 6/8 is 3/4
ok 59 - 6/9 is 2/3
ok 60 - 6/10 is 3/5
ok 61 - 7/1 is 7
ok 62 - 7/2 is 7/2
ok 63 - 7/3 is 7/3
ok 64 - 7/4 is 7/4
ok 65 - 7/5 is 7/5
ok 66 - 7/6 is 7/6
ok 67 - 7/7 is 1
ok 68 - 7/8 is 7/8
ok 69 - 7/9 is 7/9
ok 70 - 7/10 is 7/10
ok 71 - 8/1 is 8
ok 72 - 8/2 is 4
ok 73 - 8/3 is 8/3
ok 74 - 8/4 is 2
ok 75 - 8/5 is 8/5
ok 76 - 8/6 is 4/3
ok 77 - 8/7 is 8/7
ok 78 - 8/8 is 1
ok 79 - 8/9 is 8/9
ok 80 - 8/10 is 4/5
ok 81 - 9/1 is 9
ok 82 - 9/2 is 9/2
ok 83 - 9/3 is 3
ok 84 - 9/4 is 9/4
ok 85 - 9/5 is 9/5
ok 86 - 9/6 is 3/2
ok 87 - 9/7 is 9/7
ok 88 - 9/8 is 9/8
ok 89 - 9/9 is 1
ok 90 - 9/10 is 9/10
ok 91 - 10/1 is 10
ok 92 - 10/2 is 5
ok 93 - 10/3 is 10/3
ok 94 - 10/4 is 5/2
ok 95 - 10/5 is 2
ok 96 - 10/6 is 5/3
ok 97 - 10/7 is 10/7
ok 98 - 10/8 is 5/4
ok 99 - 10/9 is 10/9
ok 100 - 10/10 is 1

这是程序:

#!/usr/bin/env perl
#
# coprimes - test suite to use unary coprimality algorithm
# 
# Tom Christiansen <tchrist@perl.com>
# Sun Apr 17 12:18:19 MDT 2011

use strict;
use warnings;

my $DEFAULT = 2*3*5;
my $max = @ARGV ? shift : $DEFAULT;

use Test::More;
plan tests => $max ** 2;

my $rx = qr{
    ^
    (?|   1+       / (1)
      | (11+)  \1* / \1+
    )
    $
}x;

for my $i ( 1 .. $max ) {
    for my $j ( 1 .. $max ) {
        my $test;
        if (((1 x $i) . "/" . (1 x $j)) =~ /$rx/) {
            my $cf = length($1);
            $test = $i / $cf;
            $test .= "/" . $j/$cf unless $j/$cf == 1;
        } else {
            $test = "$i/$j";
        }
        cmp_ok($test, "eq", reduce($i, $j), "$i/$j is $test");
    }
}

sub reduce {
    my ($a, $b) = @_;
    use Math::BigRat;
    my $f = new Math::BigRat "$a/$b";
    return "$f";
}

【讨论】:

  • +1,但您正在做正则表达式之外的部分工作(转换为一元)。
  • 但问题的重点是在 inside 正则表达式中做这些事情,即使它完全不可读
  • @tchrist:这也是作弊。他要求的是正则表达式,而不是 Perl。
  • @Slaks, @garm0nboz1a:我现在已经完全按照原始规范完成了这个问题,没有任何“作弊”,因为你如此粗鲁地贬低了这项工作。只需if ($frac =~ $reducible_rx) { ⋯ } else { ⋯ } 即可。如果您更喜欢逆向逻辑来制作不可约的正则表达式,只需反转模式中的(*PASS)/(*FAIL) 分支。完成。
  • 但您仍在正则表达式中使用 Perl。
【解决方案2】:

不,这是不可能的。像一个优秀的计算机科学家一样,我会忽略工具 regex 的细节,并假设您在询问是否有正则表达式。我对正则表达式的功能没有足够的知识来确保它仅限于正则表达式。抛开这个警告,继续表演。

重新措辞我们得到:

L 成为语言{"a/b"|其中ab 是以基数编码的自然数rab 是互质数}L 是正规的吗?

假设这样的语言是正规的。然后有一个 DFA 可以决定 L 的成员资格。让N 是这样一个 DFA 的状态数。有无数个素数。由于素数的数量是无限的,所以任意多个素数大于基数rN 数字中可编码的最大数。 (注意:最大的数显然是rN 的幂。我用这个奇怪的措辞来说明如何容纳一元。)选择大于这个数的N+1 素数。所有这些数字都至少使用N+1 数字(在基数r 中)进行编码。枚举这些素数 p₀pₙ。让sᵢ 成为pᵢ 在读取/ 后立即处于的状态。根据鸽子洞原理,有N 状态和N+1 sᵢ 状态,因此至少存在一对索引(j,k) 使得sⱼ = sₖ。所以从 DFA 的初始状态开始,输入 pₖ/pⱼ/ 导致相同的状态 sⱼ(或 sₖ)和 pⱼpₖ 是不同的素数。

L 必须接受所有不同的素数对 p/q,因为它们是互质的,并且拒绝所有由它们自己划分的素数 p/p,因为 pp 不互质。现在该语言接受pⱼ = pₖ,因此存在从sⱼ 使用字符串pₖ 到接受状态的状态序列,调用此序列β。让α 是从初始状态开始读取pₖ 的状态序列。从字符串pₖ/pₖ 的初始状态开始的DFA 状态序列必须α 后跟β 相同。该序列从初始状态开始,进入sₖ(通过读取输入pₖ),并通过读取pₖ 达到接受状态。 DFA 接受 pₖ/pₖpₖ/pₖL 中。 pₖ 不与 pₖ 互质,因此 pₖ/pₖ 不在 L 中。矛盾。因此语言L是不规则的,或者不存在正则表达式。

【讨论】:

  • 说实话,我不相信这个证明。但直觉也证实了这个结论:同素性测试似乎需要比 DFA(因此,经典(学术)正则表达式)所能提供的更强大的计算模型。同时,上面的实际解决方案表明这并非完全不可能。这引起了巨大的兴趣,即在这种情况下,理论与实践之间的区别在哪里。
  • 对于那些感兴趣的人——“正则表达式”工具严格来说比经典正则表达式更强大,因为(至少)包含了反向引用。
【解决方案3】:

如果你用一元写数字,并使用“:”作为除号,我认为这匹配可约分数:

/^1+:1$|^(11+):\1$|^(11+?)\2+:\2\2+$/

然后您可以使用 !~ 查找不匹配的字符串。

基于此:http://montreal.pm.org/tech/neil_kandalgaonkar.shtml

【讨论】:

    【解决方案4】:

    你可以知道,以(0,5)结尾的数可以被(5)整除,或者以(2,4,6,8,0)结尾的数可以被2整除。

    对于 3,4,6,7,8,9 作为除数,我不认为有可能,对于任意除数也不行。

    我想你知道方法,决定被 3 整除 - 建立递归交叉和,它必须被 3 整除,以使数字可整除。因此,您可以从数字中消除所有 3、6 和 9,以及 0。对于任意数字,您可以这样处理:

    • 删除每个 0369
    • 将 47 更改为 1,(因为 4%3 和 7%3 = 1)
    • 将58改为2,原因见上
    • 每2到11换一次
    • 将每组 111 更改为空。

    如果结果为空,则该数能被 3 整除:

    echo ${RANDOM}${RANDOM}${RANDOM} | sed 's/[0369]//g;s/[47]/1/g;s/[58]/2/g;s/2/11/g;s/1\{3\}//g'
    

    类似的方法可能适用于 9,您有类似的规则。但是对于任意除数的一般方法?

    【讨论】:

    • 查看我的答案,了解为任意除数构建 DFA。每个除数单独都是正则的,但是一个所有除数的表达式是不可能的。
    • 是的,我赞成你帖子的一小部分,我理解。 :)
    猜你喜欢
    • 2023-04-02
    • 2011-06-13
    • 1970-01-01
    • 2021-10-29
    • 1970-01-01
    相关资源
    最近更新 更多