Heronian triangles: Difference between revisions
(→{{header|Perl 6}}: bug fixed, remove workaround, add works-with) |
(→{{header|Perl 6}}: Perl translation) |
||
Line 29: | Line 29: | ||
<small>'''Note''': when generating triangles it may help to restrict <math>a <= b <= c</math></small> |
<small>'''Note''': when generating triangles it may help to restrict <math>a <= b <= c</math></small> |
||
=={{header|Perl}}== |
|||
{{trans|Perl 6}} |
|||
<lang perl>use strict; |
|||
use warnings; |
|||
use List::Util qw(max); |
|||
sub gcd { $_[1] == 0 ? $_[0] : gcd($_[1], $_[0] % $_[1]) } |
|||
sub hero { |
|||
my ($a, $b, $c) = @_[0,1,2]; |
|||
my $s = ($a + $b + $c) / 2; |
|||
sqrt $s*($s - $a)*($s - $b)*($s - $c); |
|||
} |
|||
sub heronian_area { |
|||
my $hero = hero my ($a, $b, $c) = @_[0,1,2]; |
|||
sprintf("%.0f", $hero) eq $hero ? $hero : 0 |
|||
} |
|||
sub primitive_heronian_area { |
|||
my ($a, $b, $c) = @_[0,1,2]; |
|||
heronian_area($a, $b, $c) if 1 == gcd $a, gcd $b, $c; |
|||
} |
|||
sub show { |
|||
print " Area Perimeter Sides\n"; |
|||
for (@_) { |
|||
my ($area, $perim, $c, $b, $a) = @$_; |
|||
printf "%7d %9d %d×%d×%d\n", $area, $perim, $a, $b, $c; |
|||
} |
|||
} |
|||
sub main { |
|||
my $maxside = shift // 200; |
|||
my $first = shift // 10; |
|||
my $witharea = shift // 210; |
|||
my @h; |
|||
for my $c (1 .. $maxside) { |
|||
for my $b (1 .. $c) { |
|||
for my $a ($c - $b + 1 .. $b) { |
|||
if (my $area = primitive_heronian_area $a, $b, $c) { |
|||
push @h, [$area, $a+$b+$c, $c, $b, $a]; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@h = sort { |
|||
$a->[0] <=> $b->[0] |
|||
or |
|||
$a->[1] <=> $b->[1] |
|||
or |
|||
max(@$a[2,3,4]) <=> max(@$b[2,3,4]) |
|||
} @h; |
|||
printf "Primitive Heronian triangles with sides up to %d: %d\n", |
|||
$maxside, |
|||
scalar @h; |
|||
print "First:\n"; |
|||
show @h[0 .. $first - 1]; |
|||
print "Area $witharea:\n"; |
|||
show grep { $_->[0] == $witharea } @h; |
|||
} |
|||
&main();</lang> |
|||
{{out}} |
|||
<pre>Primitive Heronian triangles with sides up to 200: 517 |
|||
First: |
|||
Area Perimeter Sides |
|||
6 12 3×4×5 |
|||
12 16 5×5×6 |
|||
12 18 5×5×8 |
|||
24 32 4×13×15 |
|||
30 30 5×12×13 |
|||
36 36 9×10×17 |
|||
36 54 3×25×26 |
|||
42 42 7×15×20 |
|||
60 36 10×13×13 |
|||
60 40 8×15×17 |
|||
Area 210: |
|||
Area Perimeter Sides |
|||
210 70 17×25×28 |
|||
210 70 20×21×29 |
|||
210 84 12×35×37 |
|||
210 84 17×28×39 |
|||
210 140 7×65×68 |
|||
210 300 3×148×149</pre> |
|||
=={{header|Perl 6}}== |
=={{header|Perl 6}}== |
||
{{works with|rakudo|2015-01-03}} |
{{works with|rakudo|2015-01-03}} |
Revision as of 09:25, 3 January 2015
Hero's formula for the area of a triangle given the length of its three sides a, b, and c is given by:
where s is half the perimeter of the triangle; that is,
Heronian triangles are triangles whose sides and area are all integers.
- An example is the triangle with sides 3, 4, 5 whose area is 6 (and whose perimeter is 12).
Note that any triangle whose sides are all an integer multiple of 3,4,5; such as 6,8,10, will also be a heronian triangle.
Define a Primitive Heronian triangle as a heronian triangle where the greatest common divisor of all three sides is 1. this will exclude, for example triangle 6,8,10
The task is to:
- Create a named function/method/proceedure/... that implements Hero's formula.
- Use the function to generate all the primitive heronian triangles with sides <= 200.
- Show the count of how many triangles are found.
- Order the triangles by first increasing area, then by increasing perimeter, then by increasing maximum side lengths
- Show the first ten ordered triangles in a table of sides, perimeter, and area.
- Show a similar ordered table for those triangles with area = 210
Show all output here.
Note: when generating triangles it may help to restrict
Perl
<lang perl>use strict; use warnings; use List::Util qw(max);
sub gcd { $_[1] == 0 ? $_[0] : gcd($_[1], $_[0] % $_[1]) }
sub hero {
my ($a, $b, $c) = @_[0,1,2]; my $s = ($a + $b + $c) / 2; sqrt $s*($s - $a)*($s - $b)*($s - $c);
}
sub heronian_area {
my $hero = hero my ($a, $b, $c) = @_[0,1,2]; sprintf("%.0f", $hero) eq $hero ? $hero : 0
}
sub primitive_heronian_area {
my ($a, $b, $c) = @_[0,1,2]; heronian_area($a, $b, $c) if 1 == gcd $a, gcd $b, $c;
}
sub show {
print " Area Perimeter Sides\n"; for (@_) { my ($area, $perim, $c, $b, $a) = @$_;
printf "%7d %9d %d×%d×%d\n", $area, $perim, $a, $b, $c;
}
}
sub main {
my $maxside = shift // 200; my $first = shift // 10; my $witharea = shift // 210; my @h; for my $c (1 .. $maxside) {
for my $b (1 .. $c) { for my $a ($c - $b + 1 .. $b) { if (my $area = primitive_heronian_area $a, $b, $c) { push @h, [$area, $a+$b+$c, $c, $b, $a]; } } }
} @h = sort {
$a->[0] <=> $b->[0] or $a->[1] <=> $b->[1] or max(@$a[2,3,4]) <=> max(@$b[2,3,4])
} @h; printf "Primitive Heronian triangles with sides up to %d: %d\n", $maxside, scalar @h; print "First:\n"; show @h[0 .. $first - 1]; print "Area $witharea:\n"; show grep { $_->[0] == $witharea } @h;
}
&main();</lang>
- Output:
Primitive Heronian triangles with sides up to 200: 517 First: Area Perimeter Sides 6 12 3×4×5 12 16 5×5×6 12 18 5×5×8 24 32 4×13×15 30 30 5×12×13 36 36 9×10×17 36 54 3×25×26 42 42 7×15×20 60 36 10×13×13 60 40 8×15×17 Area 210: Area Perimeter Sides 210 70 17×25×28 210 70 20×21×29 210 84 12×35×37 210 84 17×28×39 210 140 7×65×68 210 300 3×148×149
Perl 6
<lang perl6>sub hero($a, $b, $c) {
my $s = ($a + $b + $c) / 2; my $a2 = $s * ($s - $a) * ($s - $b) * ($s - $c); $a2.sqrt;
}
sub heronian-area($a, $b, $c) {
given hero $a, $b, $c { .floor == .ceiling ?? .floor !! 0 }
}
sub primitive-heronian-area($a, $b, $c) {
heronian-area $a, $b, $c if 1 == [gcd] $a, $b, $c;
}
sub show {
say " Area Perimeter Sides"; for @_ -> [$area, $perim, $c, $b, $a] {
printf "%6d %6d %12s\n", $area, $perim, "$a×$b×$c";
}
}
sub MAIN ($maxside = 200, $first = 10, $witharea = 210) {
my \h = sort gather for 1 .. $maxside -> $c { for 1 .. $c -> $b { for $c - $b + 1 .. $b -> $a { if primitive-heronian-area($a,$b,$c) -> $area { take [$area, $a+$b+$c, $c, $b, $a]; } } } }
say "Primitive Heronian triangles with sides up to $maxside: ", +h;
say "\nFirst $first:"; show h[^$first];
say "\nArea $witharea:"; show h.grep: *[0] == $witharea;
}</lang>
- Output:
Primitive Heronian triangles with sides up to 200: 517 First 10: Area Perimeter Sides 6 12 3×4×5 12 16 5×5×6 12 18 5×5×8 24 32 4×13×15 30 30 5×12×13 36 36 9×10×17 36 54 3×25×26 42 42 7×15×20 60 36 10×13×13 60 40 8×15×17 Area 210: Area Perimeter Sides 210 70 17×25×28 210 70 20×21×29 210 84 12×35×37 210 84 17×28×39 210 140 7×65×68 210 300 3×148×149
Python
<lang python>from math import sqrt from fractions import gcd from itertools import product
def hero(a, b, c):
s = (a + b + c) / 2 a2 = s*(s-a)*(s-b)*(s-c) return sqrt(a2) if a2 > 0 else 0
def is_heronian(a, b, c):
a = hero(a, b, c) return a > 0 and a.is_integer()
def gcd3(x, y, z):
return gcd(gcd(x, y), z)
if __name__ == '__main__':
maxside = 200 h = [(a, b, c) for a,b,c in product(range(1, maxside + 1), repeat=3) if a <= b <= c and a + b > c and gcd3(a, b, c) == 1 and is_heronian(a, b, c)] h.sort(key = lambda x: (hero(*x), sum(x), x[::-1])) # By increasing area, perimeter, then sides print('Primitive Heronian triangles with sides up to %i:' % maxside, len(h)) print('\nFirst ten when ordered by increasing area, then perimeter,then maximum sides:') print('\n'.join(' %14r perim: %3i area: %i' % (sides, sum(sides), hero(*sides)) for sides in h[:10])) print('\nAll with area 210 subject to the previous ordering:') print('\n'.join(' %14r perim: %3i area: %i' % (sides, sum(sides), hero(*sides)) for sides in h if hero(*sides) == 210))</lang>
- Output:
Primitive Heronian triangles with sides up to 200: 517 First ten when ordered by increasing area, then perimeter,then maximum sides: (3, 4, 5) perim: 12 area: 6 (5, 5, 6) perim: 16 area: 12 (5, 5, 8) perim: 18 area: 12 (4, 13, 15) perim: 32 area: 24 (5, 12, 13) perim: 30 area: 30 (9, 10, 17) perim: 36 area: 36 (3, 25, 26) perim: 54 area: 36 (7, 15, 20) perim: 42 area: 42 (10, 13, 13) perim: 36 area: 60 (8, 15, 17) perim: 40 area: 60 All with area 210 subject to the previous ordering: (17, 25, 28) perim: 70 area: 210 (20, 21, 29) perim: 70 area: 210 (12, 35, 37) perim: 84 area: 210 (17, 28, 39) perim: 84 area: 210 (7, 65, 68) perim: 140 area: 210 (3, 148, 149) perim: 300 area: 210