Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does this regex find triangular numbers?

Part of a series of educational regex articles, this is a gentle introduction to the concept of nested references.

The first few triangular numbers are:

 1 = 1  3 = 1 + 2  6 = 1 + 2 + 3 10 = 1 + 2 + 3 + 4 15 = 1 + 2 + 3 + 4 + 5 

There are many ways to check if a number is triangular. There's this interesting technique that uses regular expressions as follows:

  • Given n, we first create a string of length n filled with the same character
  • We then match this string against the pattern ^(\1.|^.)+$
    • n is triangular if and only if this pattern matches the string

Here are some snippets to show that this works in several languages:

PHP (on ideone.com)

$r = '/^(\1.|^.)+$/';  foreach (range(0,50) as $n) {   if (preg_match($r, str_repeat('o', $n))) {      print("$n ");   } } 

Java (on ideone.com)

for (int n = 0; n <= 50; n++) {     String s = new String(new char[n]);     if (s.matches("(\\1.|^.)+")) {         System.out.print(n + " ");     } } 

C# (on ideone.com)

Regex r = new Regex(@"^(\1.|^.)+$");  for (int n = 0; n <= 50; n++) {     if (r.IsMatch("".PadLeft(n))) {        Console.Write("{0} ", n);     } } 

So this regex seems to work, but can someone explain how?

Similar questions

  • How to determine if a number is a prime with regex?
like image 701
polygenelubricants Avatar asked Sep 02 '10 13:09

polygenelubricants


People also ask

How does a regex pattern work?

A regex pattern matches a target string. The pattern is composed of a sequence of atoms. An atom is a single point within the regex pattern which it tries to match to the target string. The simplest atom is a literal, but grouping parts of the pattern to match an atom will require using ( ) as metacharacters.

What is the 100th triangular number?

List Of Triangular Numbers. 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55, 66, 78, 91, 105, 120,136, 153, 171, 190, 210, 231, 253, 276, 300, 325, 351, 378, 406, 435, 465, 496, 528, 561, 595, 630, 666, 703, 741, 780, 820, 861, 903, 946, 990, 1035, 1081, 1128, 1176, 1225, 1275, 1326, 1378, 1431, and so on.

What are triangular numbers used for?

Triangular numbers provide many wonderful contexts for mathematical thinking and problem solving. Triangular numbers are figurate numbers because they represent counting numbers as a geometric configu- ration of equally spaced points.


1 Answers

Explanation

Here's a schematic breakdown of the pattern:

from beginning… |         …to end |         | ^(\1.|^.)+$  \______/|___match   group 1    one-or-more times 

The (…) brackets define capturing group 1, and this group is matched repeatedly with +. This subpattern is anchored with ^ and $ to see if it can match the entire string.

Group 1 tries to match this|that alternates:

  • \1., that is, what group 1 matched (self reference!), plus one of "any" character,
  • or ^., that is, just "any" one character at the beginning

Note that in group 1, we have a reference to what group 1 matched! This is a nested/self reference, and is the main idea introduced in this example. Keep in mind that when a capturing group is repeated, generally it only keeps the last capture, so the self reference in this case essentially says:

"Try to match what I matched last time, plus one more. That's what I'll match this time."

Similar to a recursion, there has to be a "base case" with self references. At the first iteration of the +, group 1 had not captured anything yet (which is NOT the same as saying that it starts off with an empty string). Hence the second alternation is introduced, as a way to "initialize" group 1, which is that it's allowed to capture one character when it's at the beginning of the string.

So as it is repeated with +, group 1 first tries to match 1 character, then 2, then 3, then 4, etc. The sum of these numbers is a triangular number.


Further explorations

Note that for simplification, we used strings that consists of the same repeating character as our input. Now that we know how this pattern works, we can see that this pattern can also match strings like "1121231234", "aababc", etc.

Note also that if we find that n is a triangular number, i.e. n = 1 + 2 + … + k, the length of the string captured by group 1 at the end will be k.

Both of these points are shown in the following C# snippet (also seen on ideone.com):

Regex r = new Regex(@"^(\1.|^.)+$");  Console.WriteLine(r.IsMatch("aababc"));     // True Console.WriteLine(r.IsMatch("1121231234")); // True Console.WriteLine(r.IsMatch("iLoveRegEx")); // False  for (int n = 0; n <= 50; n++) {     Match m = r.Match("".PadLeft(n));     if (m.Success) {        Console.WriteLine("{0} = sum(1..{1})", n, m.Groups[1].Length);     } } // 1 = sum(1..1) // 3 = sum(1..2) // 6 = sum(1..3) // 10 = sum(1..4) // 15 = sum(1..5) // 21 = sum(1..6) // 28 = sum(1..7) // 36 = sum(1..8) // 45 = sum(1..9) 

Flavor notes

Not all flavors support nested references. Always familiarize yourself with the quirks of the flavor that you're working with (and consequently, it almost always helps to provide this information whenever you're asking regex-related questions).

In most flavors, the standard regex matching mechanism tries to see if a pattern can match any part of the input string (possibly, but not necessarily, the entire input). This means that you should remember to always anchor your pattern with ^ and $ whenever necessary.

Java is slightly different in that String.matches, Pattern.matches and Matcher.matches attempt to match a pattern against the entire input string. This is why the anchors can be omitted in the above snippet.

Note that in other contexts, you may need to use \A and \Z anchors instead. For example, in multiline mode, ^ and $ match the beginning and end of each line in the input.

One last thing is that in .NET regex, you CAN actually get all the intermediate captures made by a repeated capturing group. In most flavors, you can't: all intermediate captures are lost and you only get to keep the last.

Related questions

  • (Java) method matches not work well - with examples on how to do prefix/suffix/infix matching
  • Is there a regex flavor that allows me to count the number of repetitions matched by * and + (.NET!)

Bonus material: Using regex to find power of twos!!!

With very slight modification, you can use the same techniques presented here to find power of twos.

Here's the basic mathematical property that you want to take advantage of:

  • 1 = 1
  • 2 = (1) + 1
  • 4 = (1+2) + 1
  • 8 = (1+2+4) + 1
  • 16 = (1+2+4+8) + 1
  • 32 = (1+2+4+8+16) + 1

The solution is given below (but do try to solve it yourself first!!!!)

(see on ideone.com in PHP, Java, and C#):

^(\1\1|^.)*.$

like image 106
polygenelubricants Avatar answered Oct 04 '22 16:10

polygenelubricants