Skip to main content
10 of 14
add 33 byte version
Deadcode
  • 11.9k
  • 1
  • 66
  • 52

Perl / Java / PCRE / .NET, 35 bytes

((\2x|^x)+)\1(\3xx|x(?=\2$))+$|^x?$

This beats primo's solution by 4 bytes while supporting the exact same set of regex engines.

Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2
Try it online! - .NET

It works by first finding two consecutive triangular numbers whose sum is \$n\$ (using the equivalent of (\1x|^x)+ to find the smaller one), so that \$\sqrt n\$ will be the triangular root of the larger triangular number. Then it tries to match that root as itself being a perfect square, using the equivalent of (\1xx|^x)+$.

If it did not need to match \$0^4\$, the regex would be 34 bytes, and if it didn't need to match \$1^4\$ either, it would be 30 bytes.

                   # tail = N = input number
    (              # \1 = a triangular number T_0; tail -= \1;
                   # \2 = triangular root of \1
        (
            \2x    # On iterations after the first, \2 = \2 + 1
        |
            ^x     # On the first iteration,        \2 = 1
        )+         # Iterate the above any nonzero number of times
    )
    # T_1, the next consecutive triangular number after \1, is \1 + \2 + 1.
    # N is a perfect square iff tail == T_1.
    \1             # tail -= \1
    (
        \3xx       # On iterations after the first, \3 = \3 + 2
    |
        x          # On iterations after the first, \3 = 1
        (?=\2$)    # Assert that this is the first iteration, while
                   # simultaneously asserting that N is a perfect square,
                   # because if tail == \2 here, it will have equalled \2+1
                   # before this loop began.
    )+             # Iterate the above any nonzero number of times
    $              # Assert tail == 0
|
    ^x?$           # Allow us to match N=0 or N=1, which can't be matched above

Perl / Java / PCRE2 v10.34 or later / .NET, 33 bytes

((\2x|^x?)+)\1(\3xx|x(?=\2$)|^)+$

This drops 2 bytes at the cost of sacrificing support for PCRE1 and older versions of PCRE2, because they automatically force capture groups with nested backreference(s) to be atomic – which would prevent the ^x? from ever matching \$0\$ instead of \$1\$, blocking \$1^4\$ from being matched.

If it did not need to match \$0^4\$, the regex would be 31 bytes.

Try it online! - Perl
Try it online! - Java
Attempt This Online! - PCRE2 v10.40+
Try it online! - .NET

Perl / Java / PCRE, 39 bytes

^((((\3|^x)xx)*)x?+)(?=(\1*)\2+$)\1*$\5

Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2

This is the same as the 40 byte regex below, but drops .NET support by using a possessive quantifier, x?+ instead of |^$, to match \$0^4\$. This works because x?+ will always consume 1 x if it can, and will only consume 0 xs if \$\it\text{tail}=0\$, which can happen in two ways:

  • If the input was \$0\$, in which case \1 and \2 will have both captured \$0\$, and the second half of the regex will match.
  • If the input was nonzero, in which case \2 will have captured the entire nonzero input value, making it impossible for the second half of the regex to match, as \2+ won't be able to match with \$\it\text{tail}=0\$.

Perl / Java / PCRE / .NET, 42 40 bytes

^((((\3|^x)xx)*)x)(?=(\1*)\2+$)\1*$\5|^$

Try it online! - Perl
Try it online! - Java
Try it online! - PCRE1
Try it online! / Attempt This Online! - PCRE2
Try it online! - .NET

This uses the equivalent of ^(\1xx|^x)*$ to match a perfect square using a nested backreference (made more complicated by needing to capture that square minus \$1\$), and then uses a square-testing algorithm which works thanks to the Chinese remainder theorem (which only requires ECMAScript or better, and is the reason for needing to capture the square minus \$1\$), to assert that the captured perfect square is the square root of the inputted number. The latter algorithm is explained in this post, and this exact square-testing regex is used in the second answer in this post.

    ^                    # tail = N = input number
    (                    # \1 = the largest perfect square for which the
                         #      subsequent expression matches; tail -= \1
        (                # \2 = \1 - 1
            (            # \3 = nested backreference
                # On the first iteration,   \3 =  1 + 2 = 3;
                # on subsequent iterations, \3 = \3 + 2
                (\3|^x)
                xx
            )*           # iterate \3 any number of times (may be zero); \2
                         # becomes the total of all these iterations
        )
        x                # \1 = \2 + 1
    )
    # Assert that N == \1^2
    (?=
        (\1*)\2+$        # iff \1*\1 == N, the first match here must capture \5=0
    )
    \1*$\5               # assert \1 divides N-\1, and \5==0
|
    ^$                   # Allow us to match N=0, which can't be matched above
Deadcode
  • 11.9k
  • 1
  • 66
  • 52