Regex (Perl/PCRE+(?^=)RME), 44 bytes
((?>(\1x+?)(?^=\2+$))|^x\B)+$(?^!(\2x+)\3+$) This is a port of my 48 byte .NET regex below, using lookinto instead of variable-length lookbehind. This makes it far faster, and able to scan all numbers from 0 to 9000 in about 7 seconds on my PC.
# No anchor needed, because \1 cannot be initially captured if # matching is started anywhere besides the beginning. # Sum as many divisors of N as we can (using the current position as the sum), # without skipping any of them: ( (?> # Atomic group – once this finishes matching, lock in the match # and prevent it from being backtracked into. (\1x+?) # \2 = the smallest number larger than the previous value of \1 # (which is identical to the previous value of \2) for which the # following is true; add \2 to the running total sum of divisors. (?^=\2+$) # Assert that \2 is a divisor of N ) | ^x # This must always be the first step. Add 1 to the sum of divisors, # thus setting \1 = 1 so the next iteration can keep going \B # Exclude 1 as a divisor if N == 1. We don't have to do this for # N > 1, because this loop will never be able to add N to the # already summed divisors (as 1 will always be one of them). )+$ # Require that the sum equal N exactly. If this fails, the loop # won't be able to find a match by backtracking, because everything # inside the loop is done atomically. # Assert that there are no more proper divisors of N that haven't already been summed: (?^! # Assert that the following, evaluated right-to-left, is not true: (\2x+) # \3 = any number greater than \2 \3+$ # Assert \3 is a proper divisor of N ) Regex (Perl/PCRE+(?^=)RME), 45 bytes
^(?=(x(?=(x*))(?^=(?(?=\2+$)(\3?+\2))))+$)\3$ This is a port of the largest-to-smallest 47 byte .NET regex below, using lookinto instead of variable-length lookbehind.
Regex (Perl/PCRE+(?^=)RME), 47 bytes
^(?=((?^0=(x*))(?^=(?(?=\2+$)(\3?+\2)))x)+$)\3$ This is a port of the smallest-to-largest 47 byte .NET regex below, using lookinto instead of variable-length lookbehind. This one is less conducive to being ported, and I had to use (?^0=), lookinto accessing the in-progress current match. I've not decided how to finalize the current behavior of this, so it's likely this version will not work with a future version of lookinto.
Regex (.NET), 47 bytes
^(?=((?<=(?=(?(\3+$)((?>\2?)\3)))(^x*))x)+$)\2$ ^(?=(x(?=(x*$)(?<=(?(^\2+)(\2(?>\3?))))))+$)\3$ This is based on my 44 byte abundant numbers regex, with is in turn based on Martin Ender's 45 byte abundant numbers regex. It was trivial to adapt to matching perfect numbers; just two small changes were needed, with a third change made for aesthetic purposes.
# For the purpose of these comments, the input number will be referred to as N. ^(?= # Attempt to add up all the divisors of N. (x # Cycle through all positive values of tail that are less than N, # testing each one to see if it is a divisor of N. Start at N-1. (?= # Do the below operations in a lookahead, so that upon popping back # out of it our position will remaing the same as it is here. (x*$) # \2 = tail, a potential divisor; go to end to that the following # lookbehind can operate on N as a whole. (?<= # Switch to right-to-left evaluation so that we can operate both # on N and the potential divisor \2. This requires variable-length # lookbehind, a .NET feature. Please read these comments in the # order indicated, from [Step 1] to [Step 4]. (?(^\2+) # [Step 1] If \2 is a divisor of N, then... ( # [Step 2] Add it to \3, the running total sum of divisors: # \3 = \3 + \2 \2 # [Step 4] Iff we run out of space here, i.e. iff the sum would # exceed N at this point, the match will fail, and the # summing of divisors will be halted. It can't backtrack, # because everything in the loop is done atomically. (?>\3?) # [Step 3] Since \3 is a nested backref, it will fail to match on # the first iteration. The "?" accounts for this, making # it add zero to itself on the first iteration. This must # be done before adding \2, to ensure there is enough room # for the "?" not to cause the match to become zero-length # even if \3 has a value. ) ) ) ) )+ # Using "+" instead of "*" here is an aesthetic choice, to make it # clear we don't want to match zero. It doesn't actually make a # difference (besides making the regex ever so slightly more # efficient) because \3 would be unset anyway for N=0. $ # We can only reach this point if all proper divisors of N, all the # way down to \2 = 1, been successfully summed into \3. ) \3$ # Require the sum of divisors to be exactly equal to N. Regex (.NET), 48 bytes
((?=.*(\1x+$)(?<=^\2+))\2|^x\B)+$(?<!^\3+(x+\2)) This is based on my 43 byte abundant numbers regex. It was interesting and somewhat tricky to adapt into a fully golfed perfect numbers regex.
# No anchor needed, because \1 cannot be initially captured if # matching is started anywhere besides the beginning. # Sum as many divisors of N as we can (using the current position as the sum), # without skipping any of them: ( (?= # Atomic lookahead: .*(\1x+$) # \2 = the smallest number larger than the previous value of \1 # (which is identical to the previous value of \2) for which the # following is true. We can avoid using "x+?" because the ".*" # before this implicitly forces values to be tested in increasing # order from the smallest. (?<=^\2+) # Assert that \2 is a divisor of N ) \2 # Add \2 to the running total sum of divisors | ^x # This must always be the first step. Add 1 to the sum of divisors, # thus setting \1 = 1 so the next iteration can keep going \B # Exclude 1 as a divisor if N == 1. We don't have to do this for # N > 1, because this loop will never be able to add N to the # already summed divisors (as 1 will always be one of them). )+$ # Require that the sum equal N exactly. If this fails, the loop # won't be able to find a match by backtracking, because everything # inside the loop is done atomically. # Assert that there are no more proper divisors of N that haven't already been summed: (?<! # Assert that the following, evaluated right-to-left, is not true: ^\3+ # [Step 2] is a proper divisor of N (x+\2) # [Step 1] Any value of \3 > \2 ) Regex (PCRE2), 64 bytes
This is a direct port of the 48 byte .NET regex, emulating variable-length lookbehind using a recursive subroutine call.
(?=((?=.*(\1x+$)((?<=(?=^\2+$|(?3)).)))\2|^x\B)+$)(?!(\1x+)\4+$) (?= ( (?= .*(\1x+$) ((?<= # (?3) calls this (?= # Circumvent the constant-width limitation of lookbehinds # in PCRE by using a lookahead inside the lookbehind ^\2+$ # This is the payload of the emulated variable-length # lookbehind, same as the one in the .NET regex | (?3) # Recursive call - this is the only alternative that can # match until we reach the beginning of the string ) . # Go back one character at a time, trying the above # lookahead for a match each time )) ) \2 | ^x \B )+$ ) (?! (\1x+) \4+$ ) It isn't possible to directly port the 47 byte regex to PCRE, because it changes the value of a capture group inside the lookbehind, and upon the return of any subroutine in PCRE, all capture groups are reset to the values they had upon entering the subroutine.
Regex (ECMAScript), 53 bytes – Even perfect numbers
If it is ever proved that there are no odd perfect numbers, the following would be a robust answer, but for now it is noncompeting, as it only matches even perfect numbers. It was written by Grimmy on 2019-03-15.
^(?=(x(x*?))(\1\1)+$)((x*)(?=\5$))+(?!(xx+)\6+$)\1\2$ This uses the fact that every even perfect number is of the form \$2^{n-1} (2^n-1)\$ where \$2^n-1\$ is prime. For \$2^n-1\$ to be prime, it is necessary that \$n\$ itself be prime, so the regex does not need to do a \$log_2\$ test on \$2^n\$ to verify \$n\$ is prime. (I have used \$n\$ here instead of \$p\$ to make that clear.)
^ # N = tail = input (?=(x(x*?))(\1\1)+$) # Assert that the largest odd divisor of N is >= 3 (due to # having a "+" here instead of "*"; this is not necessary, but # speeds up the regex's non-match when N is a a power of 2); # \1 = N / {largest odd divisor of N} # == {largest power of 2 divisor of N}; \2 = \1 - 1 ((x*)(?=\5$))+ # tail = N / {largest power of 2 divisor of N} # == {largest odd divisor of N} (?!(xx+)\6+$) # Assert tail is prime \1\2$ # Assert tail == \1*2 - 1; if this fails to match, it will # backtrack into the "((x*)(?=\5$))+" loop (effectively # multiplying by 2 repeatedly), but this will always fail # to match because every subsequent match attempt will be # an even number, and "\1\2$" can only be odd.