Enigmatic Code

Programming Enigma Puzzles

Enigma 1543: Pentagony

From New Scientist #2706, 2nd May 2009 [link]

From a point inside a pentagon I have drawn lines (all of different lengths of less than 25 centimetres) to its five corners. The lines and the sides of the pentagon are all whole numbers of centimetres long. Furthermore, all five angles at the point are whole numbers of degrees, and none is a right angle.

How long is the perimeter of the pentagon?

[enigma1543]

3 responses to “Enigma 1543: Pentagony

  1. Jim Randell 9 April 2012 at 9:26 pm

    Here’s my original Perl code. It runs in 21ms.

    use Enigma qw/distinct square/;
    
    # length of spokes, let's assume a is the smallest and b is smaller than e
    my ($a, $b, $c, $d, $e);
    
    # angle between spokes
    my ($ab, $bc, $cd, $de, $ea);
    
    # sides of the pentagon
    my ($AB, $BC, $CD, $DE, $EA);
    
    # the perimiter
    my $P;
    
    # cos(deg2rad($deg)) is only rational for: 0, 60, 90, 120, 180
    # so we only really need to consider angles of 60 and 120.
    my @angles = ( 60, 120 );
    my %cos2 = ( 60 => 1, 120 => -1 ); # cosine * 2
    
    sub line {
      my ($x, $y, $xy) = @_;
      my $z = $x * $x + $y * $y - $x * $y * $cos2{$xy};
      return square($z) ? int(sqrt($z) + 0.5) : undef;
    }
    
    for $a (1..21) {
      for $b ($a+1..24) {
        for $ab (@angles) {
          my $A = line($a, $b, $ab);
          next unless $A;
    
          for $c ($a+1..25) {
            next unless distinct($c, $b);
            for $bc (@angles) {
              my $B = line($b, $c, $bc);
              next unless $B;
    
              for $d ($a+1..25) {
                next unless distinct($d, $b, $c);
                for $cd (@angles) {
                  my $C = line($c, $d, $cd);
                  next unless $C;
    
                  for $e ($b+1..25) {
                    next unless distinct($e, $b, $c, $d);
                    for $de (@angles) {
                      my $D = line($d, $e, $de);
                      next unless $D;
    
                      $ea = 360 - ($ab + $bc + $cd + $de);
                      next unless exists $cos2{$ea};
                      my $E = line($e, $a, $ea);
                      next unless $E;
    
                      $P = $A + $B + $C + $D + $E;
                      print "$P [$a,$b,$c,$d,$e / $ab,$bc,$cd,$de,$ea / $A+$B+$C+$D+$E]\n";
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
    

    Solution: The perimeter of the pentagon is 99cm.

    • Jim Randell 9 April 2012 at 9:30 pm

      And here’s a version in Python. It finds all possible integer sided triangles and then tries to link them together into a pentagon. It runs in 40ms.

      from collections import defaultdict
      from enigma import is_square
      
      # create triangles with integer sides where two sides are less than 25,
      # (and different) and the angle between them is an integer and the
      # third side is an integer
      
      # angles where cos(radians(x)) is rational (excluding 0, 90, 180)
      # cos(radians(60)) = 0.5, cos(radians(120)) = -0.5
      angles = ( 60, 120 )
      cos2 = { 60: 1, 120: -1 } # 2 * cosine - to keep things integers
      
      # determine possible triangles
      triangles = defaultdict(list)
      for a in range(1, 24):
        for b in range(a+1, 25):
          for angle in angles:
            c = is_square(pow(a, 2) + pow(b, 2) - a * b * cos2[angle])
            if c is None: continue
            triangles[a].append((a, b, c, angle))
            triangles[b].append((b, a, c, angle))
      
      # fit 5 triangles together in a pentagon
      def search(s, l):
        if len(l) == 4:
          for t in triangles[s[-1]]:
            # final spoke is the same as the first
            if t[1] != s[0]: continue
            # and (for uniqueness) assume second spoke is smaller than last
            if not(s[1] < s[-1]): continue
            # and the sum of the angles should be 360
            if t[3] + sum(x[3] for x in l) != 360: continue
            # sum the perimeter
            print(t[2] + sum(x[2] for x in l), l + [t])
          return
        for t in triangles[s[-1]]:
          # spokes are distinct, and s[0] should be the smallest
          if t[1] in s or t[1] < s[0]: continue
          search(s + [t[1]], l + [t])
      
      for a in triangles:
        search([a], [])
      
  2. Jim Randell 9 April 2012 at 9:55 pm

    Here’s a diagram of the pentagon:

Leave a Comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: