Law of cosines - triples: Difference between revisions
(Added C++ solution) |
m (→{{header|Wren}}: Changed to Wren S/H) |
||
(28 intermediate revisions by 18 users not shown) | |||
Line 35: | Line 35: | ||
* [https://youtu.be/p-0SOWbzUYI?t=12m11s Visualising Pythagoras: ultimate proofs and crazy contortions] Mathlogger Video |
* [https://youtu.be/p-0SOWbzUYI?t=12m11s Visualising Pythagoras: ultimate proofs and crazy contortions] Mathlogger Video |
||
<br><br> |
<br><br> |
||
=={{header|11l}}== |
|||
{{trans|Python}} |
|||
<syntaxhighlight lang="11l">-V n = 13 |
|||
F method1(n) |
|||
V squares = (0..n).map(x -> x ^ 2) |
|||
V sqrset = Set(squares) |
|||
Set[(Int, Int, Int)] tri90, tri60, tri120 |
|||
L(a) 1..n |
|||
V a2 = squares[a] |
|||
L(b) 1..a |
|||
V b2 = squares[b] |
|||
V c2 = a2 + b2 |
|||
I c2 C sqrset |
|||
tri90.add(tuple_sorted((a, b, Int(sqrt(c2))))) |
|||
V ab = a * b |
|||
c2 -= ab |
|||
I c2 C sqrset |
|||
tri60.add(tuple_sorted((a, b, Int(sqrt(c2))))) |
|||
c2 += 2 * ab |
|||
I c2 C sqrset |
|||
tri120.add(tuple_sorted((a, b, Int(sqrt(c2))))) |
|||
R [sorted(Array(tri90)), |
|||
sorted(Array(tri60)), |
|||
sorted(Array(tri120))] |
|||
print(‘Integer triangular triples for sides 1..#.:’.format(n)) |
|||
L(angle, triples) zip([90, 60, 120], method1(n)) |
|||
print(" #3° has #. solutions:\n #.".format(angle, triples.len, triples)) |
|||
V t60 = method1(10'000)[1] |
|||
V notsame = sum(t60.filter((a, b, c) -> a != b | b != c).map((a, b, c) -> 1)) |
|||
print(‘Extra credit: ’notsame)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Integer triangular triples for sides 1..13: |
|||
90° has 3 solutions: |
|||
[(3, 4, 5), (5, 12, 13), (6, 8, 10)] |
|||
60° has 15 solutions: |
|||
[(1, 1, 1), (2, 2, 2), (3, 3, 3), (3, 7, 8), (4, 4, 4), (5, 5, 5), (5, 7, 8), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10), (11, 11, 11), (12, 12, 12), (13, 13, 13)] |
|||
120° has 2 solutions: |
|||
[(3, 5, 7), (7, 8, 13)] |
|||
Extra credit: 18394 |
|||
</pre> |
|||
=={{header|Action!}}== |
|||
<syntaxhighlight lang="action!">PROC Test(INT max,angle,coeff) |
|||
BYTE count,a,b,c |
|||
PrintF("gamma=%B degrees:%E",angle) |
|||
count=0 |
|||
FOR a=1 TO max |
|||
DO |
|||
FOR b=1 TO a |
|||
DO |
|||
FOR c=1 TO max |
|||
DO |
|||
IF a*a+b*b-coeff*a*b=c*c THEN |
|||
PrintF("(%B,%B,%B) ",a,b,c) |
|||
count==+1 |
|||
FI |
|||
OD |
|||
OD |
|||
OD |
|||
PrintF("%Enumber of triangles is %B%E%E",count) |
|||
RETURN |
|||
PROC Main() |
|||
Test(13,90,0) |
|||
Test(13,60,1) |
|||
Test(13,120,-1) |
|||
RETURN</syntaxhighlight> |
|||
{{out}} |
|||
[https://gitlab.com/amarok8bit/action-rosetta-code/-/raw/master/images/Law_of_cosines_-_triples.png Screenshot from Atari 8-bit computer] |
|||
<pre> |
|||
gamma=90 degrees: |
|||
(4,3,5) (8,6,10) (12,5,13) |
|||
number of triangles is 3 |
|||
gamma=60 degrees: |
|||
(1,1,1) (2,2,2) (3,3,3) (4,4,4) (5,5,5) (6,6,6) (7,7,7) (8,3,7) (8,5,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) |
|||
number of triangles is 15 |
|||
gamma=120 degrees: |
|||
(5,3,7) (8,7,13) |
|||
number of triangles is 2 |
|||
</pre> |
|||
=={{header|Ada}}== |
=={{header|Ada}}== |
||
< |
<syntaxhighlight lang="ada">with Ada.Text_IO; |
||
with Ada.Containers.Ordered_Maps; |
with Ada.Containers.Ordered_Maps; |
||
Line 106: | Line 196: | ||
Count_Triangles; |
Count_Triangles; |
||
Extra_Credit (Limit => 10_000); |
Extra_Credit (Limit => 10_000); |
||
end Law_Of_Cosines;</ |
end Law_Of_Cosines;</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 138: | Line 228: | ||
=={{header|ALGOL 68}}== |
=={{header|ALGOL 68}}== |
||
< |
<syntaxhighlight lang="algol68">BEGIN |
||
# find all integer sided 90, 60 and 120 degree triangles by finding integer solutions for # |
# find all integer sided 90, 60 and 120 degree triangles by finding integer solutions for # |
||
# a^2 + b^2 = c^2, a^2 + b^2 - ab = c^2, a^2 + b^2 + ab = c^2 where a, b, c in 1 .. 13 # |
# a^2 + b^2 = c^2, a^2 + b^2 - ab = c^2, a^2 + b^2 + ab = c^2 where a, b, c in 1 .. 13 # |
||
Line 185: | Line 275: | ||
print triangles( 90 ); |
print triangles( 90 ); |
||
print triangles( 120 ) |
print triangles( 120 ) |
||
END</ |
END</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 214: | Line 304: | ||
=={{header|AWK}}== |
=={{header|AWK}}== |
||
<syntaxhighlight lang="awk"> |
|||
<lang AWK> |
|||
# syntax: GAWK -f LAW_OF_COSINES_-_TRIPLES.AWK |
# syntax: GAWK -f LAW_OF_COSINES_-_TRIPLES.AWK |
||
# converted from C |
# converted from C |
||
Line 248: | Line 338: | ||
} |
} |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 284: | Line 374: | ||
=={{header|C}}== |
=={{header|C}}== |
||
=== A brute force algorithm, O(N^3) === |
=== A brute force algorithm, O(N^3) === |
||
<syntaxhighlight lang="c">/* |
|||
<lang C>/* |
|||
* RossetaCode: Law of cosines - triples |
* RossetaCode: Law of cosines - triples |
||
* |
* |
||
Line 327: | Line 417: | ||
return 0; |
return 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 336: | Line 426: | ||
=== An algorithm with O(N^2) cost === |
=== An algorithm with O(N^2) cost === |
||
<syntaxhighlight lang="c">/* |
|||
<lang C>/* |
|||
* RossetaCode: Law of cosines - triples |
* RossetaCode: Law of cosines - triples |
||
* |
* |
||
Line 380: | Line 470: | ||
return 0; |
return 0; |
||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{output}} |
{{output}} |
||
<pre> |
<pre> |
||
Line 391: | Line 481: | ||
=={{header|C++}}== |
=={{header|C++}}== |
||
< |
<syntaxhighlight lang="cpp">#include <cmath> |
||
#include <iostream> |
#include <iostream> |
||
#include <tuple> |
#include <tuple> |
||
Line 460: | Line 550: | ||
int c2 = a * a + b * b - a * b; |
int c2 = a * a + b * b - a * b; |
||
int c = isqrt(c2); |
int c = isqrt(c2); |
||
if |
if (c <= max2 && c * c == c2) |
||
++count; |
++count; |
||
} |
} |
||
Line 467: | Line 557: | ||
<< min << " to " << max2 << " where the sides are not all of the same length.\n"; |
<< min << " to " << max2 << " where the sides are not all of the same length.\n"; |
||
return 0; |
return 0; |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 481: | Line 571: | ||
There are 18394 solutions for gamma = 60 degrees in the range 1 to 10000 where the sides are not all of the same length. |
There are 18394 solutions for gamma = 60 degrees in the range 1 to 10000 where the sides are not all of the same length. |
||
</pre> |
|||
=={{header|C sharp}}== |
|||
<syntaxhighlight lang="csharp">using System; |
|||
using System.Collections.Generic; |
|||
using static System.Linq.Enumerable; |
|||
public static class LawOfCosinesTriples |
|||
{ |
|||
public static void Main2() { |
|||
PrintTriples(60, 13); |
|||
PrintTriples(90, 13); |
|||
PrintTriples(120, 13); |
|||
PrintTriples(60, 10_000, true, false); |
|||
} |
|||
private static void PrintTriples(int degrees, int maxSideLength, bool notAllTheSameLength = false, bool print = true) { |
|||
string s = $"{degrees} degree triangles in range 1..{maxSideLength}"; |
|||
if (notAllTheSameLength) s += " where not all sides are the same"; |
|||
Console.WriteLine(s); |
|||
int count = 0; |
|||
var triples = FindTriples(degrees, maxSideLength); |
|||
if (notAllTheSameLength) triples = triples.Where(NotAllTheSameLength); |
|||
foreach (var triple in triples) { |
|||
count++; |
|||
if (print) Console.WriteLine(triple); |
|||
} |
|||
Console.WriteLine($"{count} solutions"); |
|||
} |
|||
private static IEnumerable<(int a, int b, int c)> FindTriples(int degrees, int maxSideLength) { |
|||
double radians = degrees * Math.PI / 180; |
|||
int coefficient = (int)Math.Round(Math.Cos(radians) * -2, MidpointRounding.AwayFromZero); |
|||
int maxSideLengthSquared = maxSideLength * maxSideLength; |
|||
return |
|||
from a in Range(1, maxSideLength) |
|||
from b in Range(1, a) |
|||
let cc = a * a + b * b + a * b * coefficient |
|||
where cc <= maxSideLengthSquared |
|||
let c = (int)Math.Sqrt(cc) |
|||
where c * c == cc |
|||
select (a, b, c); |
|||
} |
|||
private static bool NotAllTheSameLength((int a, int b, int c) triple) => triple.a != triple.b || triple.a != triple.c; |
|||
}</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
60 degree triangles in range 1..13 |
|||
(1, 1, 1) |
|||
(2, 2, 2) |
|||
(3, 3, 3) |
|||
(4, 4, 4) |
|||
(5, 5, 5) |
|||
(6, 6, 6) |
|||
(7, 7, 7) |
|||
(8, 3, 7) |
|||
(8, 5, 7) |
|||
(8, 8, 8) |
|||
(9, 9, 9) |
|||
(10, 10, 10) |
|||
(11, 11, 11) |
|||
(12, 12, 12) |
|||
(13, 13, 13) |
|||
15 solutions |
|||
90 degree triangles in range 1..13 |
|||
(4, 3, 5) |
|||
(8, 6, 10) |
|||
(12, 5, 13) |
|||
3 solutions |
|||
120 degree triangles in range 1..13 |
|||
(5, 3, 7) |
|||
(8, 7, 13) |
|||
2 solutions |
|||
60 degree triangles in range 1..10000 where not all sides are the same |
|||
18394 solutions</pre> |
|||
=={{header|Delphi}}== |
|||
{{works with|Delphi|6.0}} |
|||
{{libheader|SysUtils,StdCtrls}} |
|||
<syntaxhighlight lang="Delphi"> |
|||
procedure FindTriples(Memo: TMemo; Max,Angle,Coeff: integer); |
|||
var Count,A,B,C: integer; |
|||
var S: string; |
|||
begin |
|||
Memo.Lines.Add(Format('Gamma= %d°',[Angle])); |
|||
Count:=0; |
|||
S:=''; |
|||
for A:=1 to Max do |
|||
for B:=1 to A do |
|||
for C:=1 to Max do |
|||
if A*A+B*B-Coeff*A*B=C*C then |
|||
begin |
|||
Inc(Count); |
|||
S:=S+Format('(%d,%d,%d) ',[A,B,C]); |
|||
if (Count mod 3)=0 then S:=S+CRLF; |
|||
end; |
|||
Memo.Lines.Add(Format('Number of triangles = %d',[Count])); |
|||
Memo.Lines.Add(S); |
|||
end; |
|||
procedure LawOfCosines(Memo: TMemo); |
|||
begin |
|||
FindTriples(Memo,13,90,0); |
|||
FindTriples(Memo,13,60,1); |
|||
FindTriples(Memo,13,120,-1); |
|||
end; |
|||
</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
Gamma= 90° |
|||
Number of triangles = 3 |
|||
(4,3,5) (8,6,10) (12,5,13) |
|||
Gamma= 60° |
|||
Number of triangles = 15 |
|||
(1,1,1) (2,2,2) (3,3,3) |
|||
(4,4,4) (5,5,5) (6,6,6) |
|||
(7,7,7) (8,3,7) (8,5,7) |
|||
(8,8,8) (9,9,9) (10,10,10) |
|||
(11,11,11) (12,12,12) (13,13,13) |
|||
Gamma= 120° |
|||
Number of triangles = 2 |
|||
(5,3,7) (8,7,13) |
|||
Elapsed Time: 9.768 ms. |
|||
</pre> |
</pre> |
||
=={{header|Factor}}== |
=={{header|Factor}}== |
||
< |
<syntaxhighlight lang="factor">USING: backtrack formatting kernel locals math math.ranges |
||
sequences sets sorting ; |
sequences sets sorting ; |
||
IN: rosetta-code.law-of-cosines |
IN: rosetta-code.law-of-cosines |
||
Line 503: | Line 728: | ||
[ * + ] 120 |
[ * + ] 120 |
||
[ 2drop 0 - ] 90 |
[ 2drop 0 - ] 90 |
||
[ * - ] 60 [ show-solutions ] 2tri@</ |
[ * - ] 60 [ show-solutions ] 2tri@</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 530: | Line 755: | ||
{ 13 13 13 } |
{ 13 13 13 } |
||
} |
} |
||
</pre> |
|||
=={{header|Fortran}}== |
|||
<syntaxhighlight lang="fortran">MODULE LAW_OF_COSINES |
|||
IMPLICIT NONE |
|||
CONTAINS |
|||
! Calculate the third side of a triangle using the cosine rule |
|||
REAL FUNCTION COSINE_SIDE(SIDE_A, SIDE_B, ANGLE) |
|||
INTEGER, INTENT(IN) :: SIDE_A, SIDE_B |
|||
REAL(8), INTENT(IN) :: ANGLE |
|||
COSINE_SIDE = SIDE_A**2 + SIDE_B**2 - 2*SIDE_A*SIDE_B*COS(ANGLE) |
|||
COSINE_SIDE = COSINE_SIDE**0.5 |
|||
END FUNCTION COSINE_SIDE |
|||
! Convert an angle in degrees to radians |
|||
REAL(8) FUNCTION DEG2RAD(ANGLE) |
|||
REAL(8), INTENT(IN) :: ANGLE |
|||
REAL(8), PARAMETER :: PI = 4.0D0*DATAN(1.D0) |
|||
DEG2RAD = ANGLE*(PI/180) |
|||
END FUNCTION DEG2RAD |
|||
! Sort an array of integers |
|||
FUNCTION INT_SORTED(ARRAY) RESULT(SORTED) |
|||
INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY |
|||
INTEGER, DIMENSION(SIZE(ARRAY)) :: SORTED, TEMP |
|||
INTEGER :: MAX_VAL, DIVIDE |
|||
SORTED = ARRAY |
|||
TEMP = ARRAY |
|||
DIVIDE = SIZE(ARRAY) |
|||
DO WHILE (DIVIDE .NE. 1) |
|||
MAX_VAL = MAXVAL(SORTED(1:DIVIDE)) |
|||
TEMP(DIVIDE) = MAX_VAL |
|||
TEMP(MAXLOC(SORTED(1:DIVIDE))) = SORTED(DIVIDE) |
|||
SORTED = TEMP |
|||
DIVIDE = DIVIDE - 1 |
|||
END DO |
|||
END FUNCTION INT_SORTED |
|||
! Append an integer to the end of an array of integers |
|||
SUBROUTINE APPEND(ARRAY, ELEMENT) |
|||
INTEGER, DIMENSION(:), ALLOCATABLE, INTENT(INOUT) :: ARRAY |
|||
INTEGER, DIMENSION(:), ALLOCATABLE :: TEMP |
|||
INTEGER :: ELEMENT |
|||
INTEGER :: I, ISIZE |
|||
IF (ALLOCATED(ARRAY)) THEN |
|||
ISIZE = SIZE(ARRAY) |
|||
ALLOCATE(TEMP(ISIZE+1)) |
|||
DO I=1, ISIZE |
|||
TEMP(I) = ARRAY(I) |
|||
END DO |
|||
TEMP(ISIZE+1) = ELEMENT |
|||
DEALLOCATE(ARRAY) |
|||
CALL MOVE_ALLOC(TEMP, ARRAY) |
|||
ELSE |
|||
ALLOCATE(ARRAY(1)) |
|||
ARRAY(1) = ELEMENT |
|||
END IF |
|||
END SUBROUTINE APPEND |
|||
! Check if an array of integers contains a subset |
|||
LOGICAL FUNCTION CONTAINS_ARR(ARRAY, ELEMENT) |
|||
INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY |
|||
INTEGER, DIMENSION(:) :: ELEMENT |
|||
INTEGER, DIMENSION(SIZE(ELEMENT)) :: TEMP, SORTED_ELEMENT |
|||
INTEGER :: I, COUNTER, J |
|||
COUNTER = 0 |
|||
ELEMENT = INT_SORTED(ELEMENT) |
|||
DO I=1,SIZE(ARRAY),SIZE(ELEMENT) |
|||
TEMP = ARRAY(I:I+SIZE(ELEMENT)-1) |
|||
DO J=1,SIZE(ELEMENT) |
|||
IF (ELEMENT(J) .EQ. TEMP(J)) THEN |
|||
COUNTER = COUNTER + 1 |
|||
END IF |
|||
END DO |
|||
IF (COUNTER .EQ. SIZE(ELEMENT)) THEN |
|||
CONTAINS_ARR = .TRUE. |
|||
RETURN |
|||
END IF |
|||
END DO |
|||
CONTAINS_ARR = .FALSE. |
|||
END FUNCTION CONTAINS_ARR |
|||
! Count and print cosine triples for the given angle in degrees |
|||
INTEGER FUNCTION COSINE_TRIPLES(MIN_NUM, MAX_NUM, ANGLE, PRINT_RESULTS) RESULT(COUNTER) |
|||
INTEGER, INTENT(IN) :: MIN_NUM, MAX_NUM |
|||
REAL(8), INTENT(IN) :: ANGLE |
|||
LOGICAL, INTENT(IN) :: PRINT_RESULTS |
|||
INTEGER, DIMENSION(:), ALLOCATABLE :: CANDIDATES |
|||
INTEGER, DIMENSION(3) :: CANDIDATE |
|||
INTEGER :: A, B |
|||
REAL :: C |
|||
COUNTER = 0 |
|||
DO A = MIN_NUM, MAX_NUM |
|||
DO B = MIN_NUM, MAX_NUM |
|||
C = COSINE_SIDE(A, B, DEG2RAD(ANGLE)) |
|||
IF (C .GT. MAX_NUM .OR. MOD(C, 1.) .NE. 0) THEN |
|||
CYCLE |
|||
END IF |
|||
CANDIDATE(1) = A |
|||
CANDIDATE(2) = B |
|||
CANDIDATE(3) = C |
|||
IF (.NOT. CONTAINS_ARR(CANDIDATES, CANDIDATE)) THEN |
|||
COUNTER = COUNTER + 1 |
|||
CALL APPEND(CANDIDATES, CANDIDATE(1)) |
|||
CALL APPEND(CANDIDATES, CANDIDATE(2)) |
|||
CALL APPEND(CANDIDATES, CANDIDATE(3)) |
|||
IF (PRINT_RESULTS) THEN |
|||
WRITE(*,'(A,I0,A,I0,A,I0,A)') " (", CANDIDATE(1), ", ", CANDIDATE(2), ", ", CANDIDATE(3), ")" |
|||
END IF |
|||
END IF |
|||
END DO |
|||
END DO |
|||
END FUNCTION COSINE_TRIPLES |
|||
END MODULE LAW_OF_COSINES |
|||
! Program prints the cosine triples for the angles 90, 60 and 120 degrees |
|||
! by using the cosine rule to find the third side of each candidate and |
|||
! checking that this is an integer. Candidates are appended to an array |
|||
! after the sides have been sorted into ascending order |
|||
! the array is repeatedly checked to ensure there are no duplicates. |
|||
PROGRAM LOC |
|||
USE LAW_OF_COSINES |
|||
REAL(8), DIMENSION(3) :: TEST_ANGLES = (/90., 60., 120./) |
|||
INTEGER :: I, COUNTER |
|||
DO I = 1,SIZE(TEST_ANGLES) |
|||
WRITE(*, '(F0.0, A)') TEST_ANGLES(I), " degree triangles: " |
|||
COUNTER = COSINE_TRIPLES(1, 13, TEST_ANGLES(I), .TRUE.) |
|||
WRITE(*,'(A, I0)') "TOTAL: ", COUNTER |
|||
WRITE(*,*) NEW_LINE('A') |
|||
END DO |
|||
END PROGRAM LOC</syntaxhighlight> |
|||
<pre> |
|||
90. degree triangles: |
|||
(3, 4, 5) |
|||
(5, 12, 13) |
|||
(6, 8, 10) |
|||
TOTAL: 3 |
|||
60. degree triangles: |
|||
(1, 1, 1) |
|||
(2, 2, 2) |
|||
(3, 3, 3) |
|||
(3, 7, 8) |
|||
(4, 4, 4) |
|||
(5, 5, 5) |
|||
(6, 6, 6) |
|||
(7, 7, 7) |
|||
(3, 7, 8) |
|||
(8, 8, 8) |
|||
(9, 9, 9) |
|||
(10, 10, 10) |
|||
(11, 11, 11) |
|||
(12, 12, 12) |
|||
(13, 13, 13) |
|||
TOTAL: 15 |
|||
120. degree triangles: |
|||
(3, 5, 7) |
|||
(7, 8, 13) |
|||
TOTAL: 2 |
|||
</pre> |
</pre> |
||
=={{header|FreeBASIC}}== |
=={{header|FreeBASIC}}== |
||
< |
<syntaxhighlight lang="freebasic">' version 03-03-2019 |
||
' compile with: fbc -s console |
' compile with: fbc -s console |
||
Line 606: | Line 1,018: | ||
Print : Print "hit any key to end program" |
Print : Print "hit any key to end program" |
||
Sleep |
Sleep |
||
End</ |
End</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> 15: 60 degree triangles |
<pre> 15: 60 degree triangles |
||
Line 641: | Line 1,053: | ||
=={{header|Go}}== |
=={{header|Go}}== |
||
< |
<syntaxhighlight lang="go">package main |
||
import "fmt" |
import "fmt" |
||
Line 714: | Line 1,126: | ||
fmt.Print(" For an angle of 60 degrees") |
fmt.Print(" For an angle of 60 degrees") |
||
fmt.Println(" there are", len(solutions), "solutions.") |
fmt.Println(" there are", len(solutions), "solutions.") |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 735: | Line 1,147: | ||
=={{header|Haskell}}== |
=={{header|Haskell}}== |
||
< |
<syntaxhighlight lang="haskell">import qualified Data.Map.Strict as Map |
||
import qualified Data.Set as Set |
import qualified Data.Set as Set |
||
import Data.Monoid ((<>)) |
import Data.Monoid ((<>)) |
||
Line 783: | Line 1,195: | ||
[120, 90, 60]) |
[120, 90, 60]) |
||
putStrLn "60 degrees - uneven triangles of maximum side 10000. Total:" |
putStrLn "60 degrees - uneven triangles of maximum side 10000. Total:" |
||
print $ length $ triangles f60ne 10000</ |
print $ length $ triangles f60ne 10000</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>Triangles of maximum side 13 |
<pre>Triangles of maximum side 13 |
||
Line 819: | Line 1,231: | ||
=={{header|J}}== |
=={{header|J}}== |
||
'''Solution:''' |
'''Solution:''' |
||
< |
<syntaxhighlight lang="j">load 'trig stats' |
||
RHS=: *: NB. right-hand-side of Cosine Law |
RHS=: *: NB. right-hand-side of Cosine Law |
||
LHS=: +/@:*:@] - cos@rfd@[ * 2 * */@] NB. Left-hand-side of Cosine Law |
LHS=: +/@:*:@] - cos@rfd@[ * 2 * */@] NB. Left-hand-side of Cosine Law |
||
Line 828: | Line 1,240: | ||
idx=. (RHS oppside) i. x LHS"1 adjsides |
idx=. (RHS oppside) i. x LHS"1 adjsides |
||
adjsides ((#~ idx ~: #) ,. ({~ idx -. #)@]) oppside |
adjsides ((#~ idx ~: #) ,. ({~ idx -. #)@]) oppside |
||
)</ |
)</syntaxhighlight> |
||
'''Example:''' |
'''Example:''' |
||
< |
<syntaxhighlight lang="j"> 60 90 120 solve&.> 13 |
||
+--------+-------+------+ |
+--------+-------+------+ |
||
| 1 1 1|3 4 5|3 5 7| |
| 1 1 1|3 4 5|3 5 7| |
||
Line 849: | Line 1,261: | ||
+--------+-------+------+ |
+--------+-------+------+ |
||
60 #@(solve -. _3 ]\ 3 # >:@i.@]) 10000 NB. optional extra credit |
60 #@(solve -. _3 ]\ 3 # >:@i.@]) 10000 NB. optional extra credit |
||
18394</ |
18394</syntaxhighlight> |
||
=={{header|Java}}== |
=={{header|Java}}== |
||
< |
<syntaxhighlight lang="java"> |
||
public class LawOfCosines { |
public class LawOfCosines { |
||
Line 897: | Line 1,309: | ||
} |
} |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 933: | Line 1,345: | ||
=={{header|JavaScript}}== |
=={{header|JavaScript}}== |
||
< |
<syntaxhighlight lang="javascript">(() => { |
||
'use strict'; |
'use strict'; |
||
Line 1,062: | Line 1,474: | ||
// MAIN --- |
// MAIN --- |
||
return main(); |
return main(); |
||
})();</ |
})();</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>Triangles of maximum side 13: |
<pre>Triangles of maximum side 13: |
||
Line 1,095: | Line 1,507: | ||
18394 |
18394 |
||
[Finished in 3.444s]</pre> |
[Finished in 3.444s]</pre> |
||
=={{header|jq}}== |
|||
'''Adapted from [[#Wren|Wren]]''' |
|||
{{works with|jq}} |
|||
'''Works with gojq, the Go implementation of jq''' |
|||
To save space, we define `squares` as a hash rather than as a JSON array. |
|||
<syntaxhighlight lang="jq">def squares(n): |
|||
reduce range(1; 1+n) as $i ({}; .[$i*$i|tostring] = $i); |
|||
# if count, then just count |
|||
def solve(angle; maxLen; allowSame; count): |
|||
squares(maxLen) as $squares |
|||
| def qsqrt($n): |
|||
$squares[$n|tostring] as $sqrt |
|||
| if $sqrt then $sqrt else null end; |
|||
reduce range(1; maxLen+1) as $a ({}; |
|||
reduce range($a; maxLen+1) as $b (.; |
|||
.lhs = $a*$a + $b*$b |
|||
| if angle != 90 |
|||
then if angle == 60 |
|||
then .lhs += ( - $a*$b) |
|||
elif angle == 120 |
|||
then .lhs += $a*$b |
|||
else "Angle must be 60, 90 or 120 degrees" | error |
|||
end |
|||
else . |
|||
end |
|||
| qsqrt(.lhs) as $c |
|||
| if $c != null |
|||
then if allowSame or $a != $b or $b != $c |
|||
then .solutions += if count then 1 else [[$a, $b, $c]] end |
|||
else . |
|||
end |
|||
else . |
|||
end |
|||
) |
|||
) |
|||
| .solutions ; |
|||
def task1($angles): |
|||
"For sides in the range [1, 13] where they can all be of the same length:\n", |
|||
($angles[] |
|||
| . as $angle |
|||
| solve($angle; 13; true; false) |
|||
| " For an angle of \($angle) degrees, there are \(length) solutions, namely:", .); |
|||
def task2(degrees; n): |
|||
"For sides in the range [1, \(n)] where they cannot ALL be of the same length:", |
|||
(solve(degrees; n; false; true) |
|||
| " For an angle of \(degrees) degrees, there are \(.) solutions.") ; |
|||
task1([90, 60, 120]), "", task2(60; 10000)</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
For sides in the range [1, 13] where they can all be of the same length: |
|||
For an angle of 90 degrees, there are 3 solutions, namely: |
|||
[[3,4,5],[5,12,13],[6,8,10]] |
|||
For an angle of 60 degrees, there are 15 solutions, namely: |
|||
[[1,1,1],[2,2,2],[3,3,3],[3,8,7],[4,4,4],[5,5,5],[5,8,7],[6,6,6],[7,7,7],[8,8,8],[9,9,9],[10,10,10],[11,11,11],[12,12,12],[13,13,13]] |
|||
For an angle of 120 degrees, there are 2 solutions, namely: |
|||
[[3,5,7],[7,8,13]] |
|||
For sides in the range [1, 10000] where they cannot ALL be of the same length: |
|||
For an angle of 60 degrees, there are 18394 solutions. |
|||
</pre> |
|||
=={{header|Julia}}== |
=={{header|Julia}}== |
||
{{trans|zkl}} |
{{trans|zkl}} |
||
< |
<syntaxhighlight lang="julia">sqdict(n) = Dict([(x*x, x) for x in 1:n]) |
||
numnotsame(arrarr) = sum(map(x -> !all(y -> y == x[1], x), arrarr)) |
numnotsame(arrarr) = sum(map(x -> !all(y -> y == x[1], x), arrarr)) |
||
Line 1,125: | Line 1,607: | ||
println("Angle 120:"); for t in tri120 println(t) end |
println("Angle 120:"); for t in tri120 println(t) end |
||
println("\nFor sizes N through 10000, there are $(numnotsame(filtertriangles(10000)[1])) 60 degree triples with nonequal sides.") |
println("\nFor sizes N through 10000, there are $(numnotsame(filtertriangles(10000)[1])) 60 degree triples with nonequal sides.") |
||
</ |
</syntaxhighlight> {{output}} <pre> |
||
Integer triples for 1 <= side length <= 13: |
Integer triples for 1 <= side length <= 13: |
||
Line 1,158: | Line 1,640: | ||
=={{header|Kotlin}}== |
=={{header|Kotlin}}== |
||
{{trans|Go}} |
{{trans|Go}} |
||
< |
<syntaxhighlight lang="scala">// Version 1.2.70 |
||
val squares13 = mutableMapOf<Int, Int>() |
val squares13 = mutableMapOf<Int, Int>() |
||
Line 1,225: | Line 1,707: | ||
print(" For an angle of 60 degrees") |
print(" For an angle of 60 degrees") |
||
println(" there are ${solutions.size} solutions.") |
println(" there are ${solutions.size} solutions.") |
||
}</ |
}</syntaxhighlight> |
||
{{output}} |
{{output}} |
||
Line 1,244: | Line 1,726: | ||
For an angle of 60 degrees there are 18394 solutions. |
For an angle of 60 degrees there are 18394 solutions. |
||
</pre> |
</pre> |
||
=={{header|Lua}}== |
|||
<syntaxhighlight lang="lua">function solve(angle, maxlen, filter) |
|||
local squares, roots, solutions = {}, {}, {} |
|||
local cos2 = ({[60]=-1,[90]=0,[120]=1})[angle] |
|||
for i = 1, maxlen do squares[i], roots[i^2] = i^2, i end |
|||
for a = 1, maxlen do |
|||
for b = a, maxlen do |
|||
local lhs = squares[a] + squares[b] + cos2*a*b |
|||
local c = roots[lhs] |
|||
if c and (not filter or filter(a,b,c)) then |
|||
solutions[#solutions+1] = {a=a,b=b,c=c} |
|||
end |
|||
end |
|||
end |
|||
print(angle.."° on 1.."..maxlen.." has "..#solutions.." solutions") |
|||
if not filter then |
|||
for i,v in ipairs(solutions) do print("",v.a,v.b,v.c) end |
|||
end |
|||
end |
|||
solve(90, 13) |
|||
solve(60, 13) |
|||
solve(120, 13) |
|||
function fexcr(a,b,c) return a~=b or b~=c end |
|||
solve(60, 10000, fexcr) -- extra credit |
|||
solve(90, 10000, fexcr) -- more extra credit |
|||
solve(120, 10000, fexcr) -- even more extra credit</syntaxhighlight> |
|||
{{out}} |
|||
<pre>90° on 1..13 has 3 solutions |
|||
3 4 5 |
|||
5 12 13 |
|||
6 8 10 |
|||
60° on 1..13 has 15 solutions |
|||
1 1 1 |
|||
2 2 2 |
|||
3 3 3 |
|||
3 8 7 |
|||
4 4 4 |
|||
5 5 5 |
|||
5 8 7 |
|||
6 6 6 |
|||
7 7 7 |
|||
8 8 8 |
|||
9 9 9 |
|||
10 10 10 |
|||
11 11 11 |
|||
12 12 12 |
|||
13 13 13 |
|||
120° on 1..13 has 2 solutions |
|||
3 5 7 |
|||
7 8 13 |
|||
60° on 1..10000 has 18394 solutions |
|||
90° on 1..10000 has 12471 solutions |
|||
120° on 1..10000 has 10374 solutions</pre> |
|||
=={{header|Mathematica}}/{{header|Wolfram Language}}== |
|||
<syntaxhighlight lang="mathematica">Solve[{a^2+b^2==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers] |
|||
Length[%] |
|||
Solve[{a^2+b^2-a b==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers] |
|||
Length[%] |
|||
Solve[{a^2+b^2+a b==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers] |
|||
Length[%] |
|||
Solve[{a^2+b^2-a b==c^2,1<=a<=10000,1<=b<=10000,1<=c<=10000,a<=b,a!=b,b!=c,a!=c},{a,b,c},Integers]//Length</syntaxhighlight> |
|||
{{out}} |
|||
<pre>{{a->3,b->4,c->5},{a->5,b->12,c->13},{a->6,b->8,c->10}} |
|||
3 |
|||
{{a->1,b->1,c->1},{a->2,b->2,c->2},{a->3,b->3,c->3},{a->3,b->8,c->7},{a->4,b->4,c->4},{a->5,b->5,c->5},{a->5,b->8,c->7},{a->6,b->6,c->6},{a->7,b->7,c->7},{a->8,b->8,c->8},{a->9,b->9,c->9},{a->10,b->10,c->10},{a->11,b->11,c->11},{a->12,b->12,c->12},{a->13,b->13,c->13}} |
|||
15 |
|||
{{a->3,b->5,c->7},{a->7,b->8,c->13}} |
|||
2 |
|||
18394</pre> |
|||
=={{header|Nim}}== |
=={{header|Nim}}== |
||
< |
<syntaxhighlight lang="nim">import strformat |
||
import tables |
import tables |
||
Line 1,304: | Line 1,857: | ||
echo "\nFor sides in the range [1, 10000] where they cannot ALL be of the same length:\n" |
echo "\nFor sides in the range [1, 10000] where they cannot ALL be of the same length:\n" |
||
var solutions = solve(60, 10000, false) |
var solutions = solve(60, 10000, false) |
||
echo fmt" For an angle of 60 degrees there are {len(solutions)} solutions."</ |
echo fmt" For an angle of 60 degrees there are {len(solutions)} solutions."</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,330: | Line 1,883: | ||
=={{header|Perl}}== |
=={{header|Perl}}== |
||
{{trans|Raku}} |
{{trans|Raku}} |
||
< |
<syntaxhighlight lang="perl">use utf8; |
||
binmode STDOUT, "utf8:"; |
binmode STDOUT, "utf8:"; |
||
use Sort::Naturally; |
use Sort::Naturally; |
||
Line 1,363: | Line 1,916: | ||
} |
} |
||
printf "Non-equilateral n=10000/60°: %d\n", scalar triples(10000,60);</ |
printf "Non-equilateral n=10000/60°: %d\n", scalar triples(10000,60);</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Integer triangular triples for sides 1..13: |
<pre>Integer triangular triples for sides 1..13: |
||
Line 1,372: | Line 1,925: | ||
=={{header|Phix}}== |
=={{header|Phix}}== |
||
Using a simple flat sequence of 100 million elements (well within the language limits) proved significantly faster than a dictionary (5x or so). |
Using a simple flat sequence of 100 million elements (well within the desktop language limits, but beyond JavaScript) proved significantly faster than a dictionary (5x or so). |
||
<!--<syntaxhighlight lang="phix">(phixonline)--> |
|||
<lang Phix>sequence squares = repeat(0,10000*10000) |
|||
<span style="color: #008080;">with</span> <span style="color: #008080;">javascript_semantics</span> |
|||
for c=1 to 10000 do |
|||
<span style="color: #000080;font-style:italic;">--constant lim = iff(platform()=JS?13:10000)</span> |
|||
squares[c*c] = c |
|||
<span style="color: #008080;">constant</span> <span style="color: #000000;">lim</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">10000</span> |
|||
end for |
|||
<span style="color: #000080;font-style:italic;">--sequence squares = repeat(0,lim*lim)</span> |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">squares</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #7060A8;">platform</span><span style="color: #0000FF;">()=</span><span style="color: #004600;">JS</span><span style="color: #0000FF;">?{}:</span><span style="color: #7060A8;">repeat</span><span style="color: #0000FF;">(</span><span style="color: #000000;">0</span><span style="color: #0000FF;">,</span><span style="color: #000000;">lim</span><span style="color: #0000FF;">*</span><span style="color: #000000;">lim</span><span style="color: #0000FF;">))</span> |
|||
function solve(integer angle, maxlen, bool samelen=true) |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">lim</span> <span style="color: #008080;">do</span> |
|||
sequence res = {} |
|||
<span style="color: #000000;">squares</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c</span><span style="color: #0000FF;">*</span><span style="color: #000000;">c</span><span style="color: #0000FF;">]</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">c</span> |
|||
for a=1 to maxlen do |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
integer a2 = a*a |
|||
for b=a to maxlen do |
|||
<span style="color: #008080;">function</span> <span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #004080;">integer</span> <span style="color: #000000;">angle</span><span style="color: #0000FF;">,</span> <span style="color: #000000;">maxlen</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">samelen</span><span style="color: #0000FF;">=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span> |
|||
integer c2 = a2+b*b |
|||
<span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #0000FF;">{}</span> |
|||
if angle!=90 then |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">=</span><span style="color: #000000;">1</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxlen</span> <span style="color: #008080;">do</span> |
|||
if angle=60 then c2 -= a*b |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">a2</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">*</span><span style="color: #000000;">a</span> |
|||
elsif angle=120 then c2 += a*b |
|||
<span style="color: #008080;">for</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">=</span><span style="color: #000000;">a</span> <span style="color: #008080;">to</span> <span style="color: #000000;">maxlen</span> <span style="color: #008080;">do</span> |
|||
else crash("angle must be 60/90/120") |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">c2</span> <span style="color: #0000FF;">=</span> <span style="color: #000000;">a2</span><span style="color: #0000FF;">+</span><span style="color: #000000;">b</span><span style="color: #0000FF;">*</span><span style="color: #000000;">b</span> |
|||
end if |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">angle</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">90</span> <span style="color: #008080;">then</span> |
|||
end if |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">angle</span><span style="color: #0000FF;">=</span><span style="color: #000000;">60</span> <span style="color: #008080;">then</span> <span style="color: #000000;">c2</span> <span style="color: #0000FF;">-=</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">*</span><span style="color: #000000;">b</span> |
|||
integer c = iff(c2>length(squares)?0:squares[c2]) |
|||
<span style="color: #008080;">elsif</span> <span style="color: #000000;">angle</span><span style="color: #0000FF;">=</span><span style="color: #000000;">120</span> <span style="color: #008080;">then</span> <span style="color: #000000;">c2</span> <span style="color: #0000FF;">+=</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">*</span><span style="color: #000000;">b</span> |
|||
if c!=0 and c<=maxlen then |
|||
<span style="color: #008080;">else</span> <span style="color: #7060A8;">crash</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"angle must be 60/90/120"</span><span style="color: #0000FF;">)</span> |
|||
if samelen or a!=b or b!=c then |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
res = append(res,{a,b,c}) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #004080;">integer</span> <span style="color: #000000;">c</span> <span style="color: #0000FF;">=</span> <span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">c2</span><span style="color: #0000FF;">></span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">squares</span><span style="color: #0000FF;">)?</span><span style="color: #000000;">0</span><span style="color: #0000FF;">:</span><span style="color: #000000;">squares</span><span style="color: #0000FF;">[</span><span style="color: #000000;">c2</span><span style="color: #0000FF;">])</span> |
|||
end if |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">c</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">0</span> <span style="color: #008080;">and</span> <span style="color: #000000;">c</span><span style="color: #0000FF;"><=</span><span style="color: #000000;">maxlen</span> <span style="color: #008080;">then</span> |
|||
end for |
|||
<span style="color: #008080;">if</span> <span style="color: #000000;">samelen</span> <span style="color: #008080;">or</span> <span style="color: #000000;">a</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">b</span> <span style="color: #008080;">or</span> <span style="color: #000000;">b</span><span style="color: #0000FF;">!=</span><span style="color: #000000;">c</span> <span style="color: #008080;">then</span> |
|||
end for |
|||
<span style="color: #000000;">res</span> <span style="color: #0000FF;">=</span> <span style="color: #7060A8;">append</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">,{</span><span style="color: #000000;">a</span><span style="color: #0000FF;">,</span><span style="color: #000000;">b</span><span style="color: #0000FF;">,</span><span style="color: #000000;">c</span><span style="color: #0000FF;">})</span> |
|||
return res |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
end function |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">if</span> |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
procedure show(string fmt,sequence res, bool full=true) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">for</span> |
|||
printf(1,fmt,{length(res),iff(full?sprint(res):"")}) |
|||
<span style="color: #008080;">return</span> <span style="color: #000000;">res</span> |
|||
end procedure |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">function</span> |
|||
puts(1,"Integer triangular triples for sides 1..13:\n") |
|||
<span style="color: #008080;">procedure</span> <span style="color: #000000;">show</span><span style="color: #0000FF;">(</span><span style="color: #004080;">string</span> <span style="color: #000000;">fmt</span><span style="color: #0000FF;">,</span><span style="color: #004080;">sequence</span> <span style="color: #000000;">res</span><span style="color: #0000FF;">,</span> <span style="color: #004080;">bool</span> <span style="color: #000000;">full</span><span style="color: #0000FF;">=</span><span style="color: #004600;">true</span><span style="color: #0000FF;">)</span> |
|||
show("Angle 60 has %2d solutions: %s\n",solve( 60,13)) |
|||
<span style="color: #7060A8;">printf</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #000000;">fmt</span><span style="color: #0000FF;">,{</span><span style="color: #7060A8;">length</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">),</span><span style="color: #008080;">iff</span><span style="color: #0000FF;">(</span><span style="color: #000000;">full</span><span style="color: #0000FF;">?</span><span style="color: #7060A8;">sprint</span><span style="color: #0000FF;">(</span><span style="color: #000000;">res</span><span style="color: #0000FF;">):</span><span style="color: #008000;">""</span><span style="color: #0000FF;">)})</span> |
|||
show("Angle 90 has %2d solutions: %s\n",solve( 90,13)) |
|||
<span style="color: #008080;">end</span> <span style="color: #008080;">procedure</span> |
|||
show("Angle 120 has %2d solutions: %s\n",solve(120,13)) |
|||
show("Non-equilateral angle 60 triangles for sides 1..10000: %d%s\n",solve(60,10000,false),false)</lang> |
|||
<span style="color: #7060A8;">puts</span><span style="color: #0000FF;">(</span><span style="color: #000000;">1</span><span style="color: #0000FF;">,</span><span style="color: #008000;">"Integer triangular triples for sides 1..13:\n"</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000000;">show</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Angle 60 has %2d solutions: %s\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span> <span style="color: #000000;">60</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000000;">show</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Angle 90 has %2d solutions: %s\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span> <span style="color: #000000;">90</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000000;">show</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Angle 120 has %2d solutions: %s\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #000000;">120</span><span style="color: #0000FF;">,</span><span style="color: #000000;">13</span><span style="color: #0000FF;">))</span> |
|||
<span style="color: #000080;font-style:italic;">--if platform()!=JS then</span> |
|||
<span style="color: #000000;">show</span><span style="color: #0000FF;">(</span><span style="color: #008000;">"Non-equilateral angle 60 triangles for sides 1..10000: %d%s\n"</span><span style="color: #0000FF;">,</span><span style="color: #000000;">solve</span><span style="color: #0000FF;">(</span><span style="color: #000000;">60</span><span style="color: #0000FF;">,</span><span style="color: #000000;">10000</span><span style="color: #0000FF;">,</span><span style="color: #004600;">false</span><span style="color: #0000FF;">),</span><span style="color: #004600;">false</span><span style="color: #0000FF;">)</span> |
|||
<span style="color: #000080;font-style:italic;">--end if</span> |
|||
<!--</syntaxhighlight>--> |
|||
{{out}} |
{{out}} |
||
<pre style="font-size: 11px"> |
<pre style="font-size: 11px"> |
||
Line 1,417: | Line 1,978: | ||
Angle 120 has 2 solutions: {{3,5,7},{7,8,13}} |
Angle 120 has 2 solutions: {{3,5,7},{7,8,13}} |
||
Non-equilateral angle 60 triangles for sides 1..10000: 18394 |
Non-equilateral angle 60 triangles for sides 1..10000: 18394 |
||
</pre> |
|||
As noted I had to resort to a little trickery to get that last line to run in JavaScript, should a future version impose stricter bounds checking that will stop working. |
|||
=={{header|Prolog}}== |
|||
{{works with|SWI Prolog}} |
|||
<syntaxhighlight lang="prolog">find_solutions(Limit, Solutions):- |
|||
find_solutions(Limit, Solutions, Limit, []). |
|||
find_solutions(_, S, 0, S):- |
|||
!. |
|||
find_solutions(Limit, Solutions, A, S):- |
|||
find_solutions1(Limit, A, A, S1, S), |
|||
A_next is A - 1, |
|||
find_solutions(Limit, Solutions, A_next, S1). |
|||
find_solutions1(Limit, _, B, Triples, Triples):- |
|||
B > Limit, |
|||
!. |
|||
find_solutions1(Limit, A, B, [Triple|Triples], T):- |
|||
is_solution(Limit, A, B, Triple), |
|||
!, |
|||
B_next is B + 1, |
|||
find_solutions1(Limit, A, B_next, Triples, T). |
|||
find_solutions1(Limit, A, B, Triples, T):- |
|||
B_next is B + 1, |
|||
find_solutions1(Limit, A, B_next, Triples, T). |
|||
is_solution(Limit, A, B, t(Angle, A, B, C)):- |
|||
X is A * A + B * B, |
|||
Y is A * B, |
|||
( |
|||
Angle = 90, C is round(sqrt(X)), X is C * C |
|||
; |
|||
Angle = 120, C2 is X + Y, C is round(sqrt(C2)), C2 is C * C |
|||
; |
|||
Angle = 60, C2 is X - Y, C is round(sqrt(C2)), C2 is C * C |
|||
), |
|||
C =< Limit, |
|||
!. |
|||
write_triples(Angle, Solutions):- |
|||
find_triples(Angle, Solutions, List, 0, Count), |
|||
writef('There are %w solutions for gamma = %w:\n', [Count, Angle]), |
|||
write_triples1(List), |
|||
nl. |
|||
find_triples(_, [], [], Count, Count):- |
|||
!. |
|||
find_triples(Angle, [Triple|Triples], [Triple|Result], C, Count):- |
|||
Triple = t(Angle, _, _, _), |
|||
!, |
|||
C1 is C + 1, |
|||
find_triples(Angle, Triples, Result, C1, Count). |
|||
find_triples(Angle, [_|Triples], Result, C, Count):- |
|||
find_triples(Angle, Triples, Result, C, Count). |
|||
write_triples1([]):-!. |
|||
write_triples1([t(_, A, B, C)]):- |
|||
writef('(%w,%w,%w)\n', [A, B, C]), |
|||
!. |
|||
write_triples1([t(_, A, B, C)|Triples]):- |
|||
writef('(%w,%w,%w) ', [A, B, C]), |
|||
write_triples1(Triples). |
|||
main:- |
|||
find_solutions(13, Solutions), |
|||
write_triples(60, Solutions), |
|||
write_triples(90, Solutions), |
|||
write_triples(120, Solutions).</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
There are 15 solutions for gamma = 60: |
|||
(1,1,1) (2,2,2) (3,3,3) (3,8,7) (4,4,4) (5,5,5) (5,8,7) (6,6,6) (7,7,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) |
|||
There are 3 solutions for gamma = 90: |
|||
(3,4,5) (5,12,13) (6,8,10) |
|||
There are 2 solutions for gamma = 120: |
|||
(3,5,7) (7,8,13) |
|||
</pre> |
</pre> |
||
=={{header|Python}}== |
=={{header|Python}}== |
||
===Sets=== |
===Sets=== |
||
< |
<syntaxhighlight lang="python">N = 13 |
||
def method1(N=N): |
def method1(N=N): |
||
Line 1,449: | Line 2,090: | ||
_, t60, _ = method1(10_000) |
_, t60, _ = method1(10_000) |
||
notsame = sum(1 for a, b, c in t60 if a != b or b != c) |
notsame = sum(1 for a, b, c in t60 if a != b or b != c) |
||
print('Extra credit:', notsame)</ |
print('Extra credit:', notsame)</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
Line 1,464: | Line 2,105: | ||
A variant Python draft based on dictionaries. |
A variant Python draft based on dictionaries. |
||
(Test functions are passed as parameters to the main function.) |
(Test functions are passed as parameters to the main function.) |
||
< |
<syntaxhighlight lang="python">from itertools import (starmap) |
||
Line 1,544: | Line 2,185: | ||
if __name__ == '__main__': |
if __name__ == '__main__': |
||
main()</ |
main()</syntaxhighlight> |
||
{{Out}} |
{{Out}} |
||
<pre>Triangles of maximum side 13 |
<pre>Triangles of maximum side 13 |
||
Line 1,577: | Line 2,218: | ||
60 degrees - uneven triangles of maximum side 10000. Total: |
60 degrees - uneven triangles of maximum side 10000. Total: |
||
18394</pre> |
18394</pre> |
||
=={{header|Quackery}}== |
|||
<syntaxhighlight lang="Quackery"> [ dup 1 |
|||
[ 2dup > while |
|||
+ 1 >> |
|||
2dup / again ] |
|||
drop nip ] is sqrt ( n --> n ) |
|||
[ dup * ] is squared ( n --> n ) |
|||
[ dup sqrt squared = ] is square ( n --> b ) |
|||
[ say "90 degrees" cr |
|||
0 temp put |
|||
13 times |
|||
[ i^ 1+ dup times |
|||
[ i^ 1+ squared |
|||
over squared + |
|||
dup square |
|||
over sqrt 14 < and |
|||
iff |
|||
[ i^ 1+ echo sp |
|||
over echo sp |
|||
sqrt echo cr |
|||
1 temp tally ] |
|||
else drop ] |
|||
drop ] |
|||
temp take echo |
|||
say " solutions" cr cr ] is 90deg ( --> ) |
|||
[ say "60 degrees" cr |
|||
0 temp put |
|||
13 times |
|||
[ i^ 1+ dup times |
|||
[ i^ 1+ |
|||
2dup * dip |
|||
[ squared |
|||
over squared + ] |
|||
- dup square |
|||
over sqrt 14 < and |
|||
iff |
|||
[ i^ 1+ echo sp |
|||
over echo sp |
|||
sqrt echo cr |
|||
1 temp tally ] |
|||
else drop ] |
|||
drop ] |
|||
temp take echo |
|||
say " solutions" cr cr ] is 60deg ( --> ) |
|||
[ say "120 degrees" cr |
|||
0 temp put |
|||
13 times |
|||
[ i^ 1+ dup times |
|||
[ i^ 1+ |
|||
2dup * dip |
|||
[ squared |
|||
over squared + ] |
|||
+ dup square |
|||
over sqrt 14 < and |
|||
iff |
|||
[ i^ 1+ echo sp |
|||
over echo sp |
|||
sqrt echo cr |
|||
1 temp tally ] |
|||
else drop ] |
|||
drop ] |
|||
temp take echo |
|||
say " solutions" cr cr ] is 120deg ( --> ) |
|||
90deg 60deg 120deg</syntaxhighlight> |
|||
{{out}} |
|||
<pre>90 degrees |
|||
3 4 5 |
|||
6 8 10 |
|||
5 12 13 |
|||
3 solutions found |
|||
60 degrees |
|||
1 1 1 |
|||
2 2 2 |
|||
3 3 3 |
|||
4 4 4 |
|||
5 5 5 |
|||
6 6 6 |
|||
7 7 7 |
|||
3 8 7 |
|||
5 8 7 |
|||
8 8 8 |
|||
9 9 9 |
|||
10 10 10 |
|||
11 11 11 |
|||
12 12 12 |
|||
13 13 13 |
|||
15 solutions |
|||
120 degrees |
|||
3 5 7 |
|||
7 8 13 |
|||
2 solutions |
|||
</pre> |
|||
=={{header|R}}== |
|||
This looks a bit messy, but it really pays off when you see how nicely the output prints. |
|||
<syntaxhighlight lang="rsplus">inputs <- cbind(combn(1:13, 2), rbind(seq_len(13), seq_len(13))) |
|||
inputs <- cbind(A = inputs[1, ], B = inputs[2, ])[sort.list(inputs[1, ]),] |
|||
Pythagoras <- inputs[, "A"]^2 + inputs[, "B"]^2 |
|||
AtimesB <- inputs[, "A"] * inputs[, "B"] |
|||
CValues <- sqrt(cbind("C (90º)" = Pythagoras, |
|||
"C (60º)" = Pythagoras - AtimesB, |
|||
"C (120º)" = Pythagoras + AtimesB)) |
|||
CValues[!t(apply(CValues, MARGIN = 1, function(x) x %in% 1:13))] <- NA |
|||
output <- cbind(inputs, CValues)[!apply(CValues, MARGIN = 1, function(x) all(is.na(x))),] |
|||
rownames(output) <- paste0("Solution ", seq_len(nrow(output)), ":") |
|||
print(output, na.print = "") |
|||
cat("There are", |
|||
sum(!is.na(output[, 3])), "solutions in the 90º case,", |
|||
sum(!is.na(output[, 4])), "solutions in the 60º case, and", |
|||
sum(!is.na(output[, 5])), "solutions in the 120º case.")</syntaxhighlight> |
|||
{{out}} |
|||
<pre> A B C (90º) C (60º) C (120º) |
|||
Solution 1: 1 1 1 |
|||
Solution 2: 2 2 2 |
|||
Solution 3: 3 4 5 |
|||
Solution 4: 3 5 7 |
|||
Solution 5: 3 8 7 |
|||
Solution 6: 3 3 3 |
|||
Solution 7: 4 4 4 |
|||
Solution 8: 5 8 7 |
|||
Solution 9: 5 12 13 |
|||
Solution 10: 5 5 5 |
|||
Solution 11: 6 8 10 |
|||
Solution 12: 6 6 6 |
|||
Solution 13: 7 8 13 |
|||
Solution 14: 7 7 7 |
|||
Solution 15: 8 8 8 |
|||
Solution 16: 9 9 9 |
|||
Solution 17: 10 10 10 |
|||
Solution 18: 11 11 11 |
|||
Solution 19: 12 12 12 |
|||
Solution 20: 13 13 13 |
|||
There are 3 solutions in the 90º case, 15 solutions in the 60º case, and 2 solutions in the 120º case.</pre> |
|||
=={{header|Raku}}== |
=={{header|Raku}}== |
||
(formerly Perl 6) |
(formerly Perl 6) |
||
In each routine, <tt>race</tt> is used to allow concurrent operations, requiring the use of the atomic increment operator, <tt>⚛++</tt>, to safely update <tt>@triples</tt>, which must be declared fixed-sized, as an auto-resizing array is not thread-safe. At exit, default values in <tt>@triples</tt> are filtered out with the test <code>!eqv Any</code>. |
In each routine, <tt>race</tt> is used to allow concurrent operations, requiring the use of the atomic increment operator, <tt>⚛++</tt>, to safely update <tt>@triples</tt>, which must be declared fixed-sized, as an auto-resizing array is not thread-safe. At exit, default values in <tt>@triples</tt> are filtered out with the test <code>!eqv Any</code>. |
||
<lang |
<syntaxhighlight lang="raku" line>multi triples (60, $n) { |
||
my %sq = (1..$n).map: { .² => $_ }; |
my %sq = (1..$n).map: { .² => $_ }; |
||
my atomicint $i = 0; |
my atomicint $i = 0; |
||
Line 1,627: | Line 2,413: | ||
my @itt = triples($angle, $n); |
my @itt = triples($angle, $n); |
||
if $angle == 60 { push @itt, "$_ $_ $_" for 1..$n } |
if $angle == 60 { push @itt, "$_ $_ $_" for 1..$n } |
||
printf "Angle %3d° has %2d solutions: %s\n", $angle, +@itt, @itt.sort( |
printf "Angle %3d° has %2d solutions: %s\n", $angle, +@itt, @itt.sort(&naturally).join(', '); |
||
} |
} |
||
my ($angle, $count) = 60, 10_000; |
my ($angle, $count) = 60, 10_000; |
||
say "\nExtra credit:"; |
say "\nExtra credit:"; |
||
say "$angle° integer triples in the range 1..$count where the sides are not all the same length: ", +triples($angle, $count);</ |
say "$angle° integer triples in the range 1..$count where the sides are not all the same length: ", +triples($angle, $count);</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre>Integer triangular triples for sides 1..13: |
<pre>Integer triangular triples for sides 1..13: |
||
Line 1,643: | Line 2,429: | ||
=={{header|REXX}}== |
=={{header|REXX}}== |
||
(Using some optimization and memoization.) |
|||
Instead of coding a general purpose subroutine (or function) to solve all of the |
Instead of coding a general purpose subroutine (or function) to solve all of the |
||
task's requirements, it was decided to |
task's requirements, it was decided to |
||
Line 1,661: | Line 2,448: | ||
displayed (using the |
displayed (using the |
||
<br>absolute value of the negative number). |
<br>absolute value of the negative number). |
||
< |
<syntaxhighlight lang="rexx">/*REXX pgm finds integer sided triangles that satisfy Law of cosines for 60º, 90º, 120º.*/ |
||
parse arg |
parse arg os1 os2 os3 os4 . /*obtain optional arguments from the CL*/ |
||
if |
if os1=='' | os1=="," then os1= 13; s1=abs(os1) /*Not specified? Then use the default.*/ |
||
if |
if os2=='' | os2=="," then os2= 13; s2=abs(os2) /* " " " " " " */ |
||
if |
if os3=='' | os3=="," then os3= 13; s3=abs(os3) /* " " " " " " */ |
||
if os4=='' | os4=="," then os4= -0; s4=abs(os4) /* " " " " " " */ |
|||
w= max( length(s1), length(s2), length(s3) ) /*W is used to align the side lengths.*/ |
|||
@.= /*@: array holds squares, max of sides*/ |
|||
do j=1 for max(s1, s2, s3, s4); @.j= j * j /*use memoization.*/ |
|||
end /*j*/ |
|||
if s1>0 then call s1 /*handle the triangle case for 120º. */ |
|||
if s2>0 then call s2 /*handle the triangle case for 90º. */ |
|||
if s3>0 then call s3 /*handle the triangle case for 60º. */ |
|||
if s4>0 then call s4 /*handle the case for unique sides. */ |
|||
exit 0 /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
end /*a*/ |
|||
commas: parse arg ?; do jc=length(?)-3 to 1 by -3; ?=insert(',', ?, jc); end; return ? |
|||
call foot s1 |
|||
dAng: w= length(s); ang= ' 'd"º " uq' '; ss= s * s; @sol= " solutions found for"; return |
|||
end |
|||
foot: say right(commas(#) @sol ang "(sides up to" commas(arg(1) +0)')', 65); say; return |
|||
head: #= 0; parse arg d,uq,s; @= ','; call dAng; say center(ang, 65, '═'); return |
|||
if s2>0 then do; call head 90 /*────90º: a² + b² ≡ c² */ |
|||
show: #=#+1; arg p; if p>0 then say ' ('right(a,w)@ right(b,w)@ right(c,w)")"; return |
|||
do a=1 for s2; aa = a*a |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
do b=a+1 to s2; x= aa + b*b |
|||
s1: call head 120,,s1 /*────────── 120º: a² + b² + ab ≡ c² */ |
|||
do a=1 for s1; ap1= a + 1 |
|||
do b=ap1 for s1-ap1+1; x= @.a + @.b + a*b; if x>ss then iterate a |
|||
do c=b+1 for s1-b+1 until @.c>x |
|||
if x==@.c then do; call show os1; iterate b; end |
|||
end /*c*/ |
|||
end |
end /*b*/ |
||
end /*a*/ |
|||
call foot s1; return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
do a=1 for s3; aa = a*a |
|||
s2: call head 90,,s2 /*────────── 90º: a² + b² ≡ c² */ |
|||
do a=1 for s2; ap1= a + 1 |
|||
if x |
do b=ap1 for s2-ap1+1; x= @.a + @.b; if x>ss then iterate a |
||
do c=b+1 for s2-b+2 until @.c>x |
|||
if x==@.c then do; call show os2; iterate b; end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s2; return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
s3: call head 60,,s3 /*────────── 60º: a² + b² ─ ab ≡ c² */ |
|||
do a=1 for s3; s3ma= s3 - a + 1 |
|||
do b=a for s3ma; x= @.a + @.b - a*b; if x>ss then iterate a |
|||
do c=a for s3ma until @.c>x |
|||
if x==@.c then do; call show os3; iterate b; end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s2; return |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
||
s4: call head 60, 'unique', os4 /*────────── 60º: a² + b² ─ ab ≡ c² */ |
|||
foot: say right(# ' solutions found for' angle "(sides up to" arg(1)')', 65); say; return |
|||
do a=1 for s4; ap1= a + 1; s4map1= s4 - ap1 + 1 |
|||
do b=ap1 for s4map1; x= @.a + @.b - a*b; if x>ss then iterate a |
|||
do c=ap1 for s4map1 until @.c>x |
|||
if x==@.c then do; call show os4; iterate b; end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s4; return</syntaxhighlight> |
|||
{{out|output|text= when using the default number of sides for the input: <tt> 13 </tt>}} |
{{out|output|text= when using the default number of sides for the input: <tt> 13 </tt>}} |
||
<pre> |
<pre> |
||
Line 1,736: | Line 2,539: | ||
15 solutions found for 60º (sides up to 13) |
15 solutions found for 60º (sides up to 13) |
||
</pre> |
</pre> |
||
{{out|output|text= when using the inputs of: <tt> 0 0 0 -10000 </tt>}} |
|||
Note that the first three computations are bypassed because of the three zero ('''0''') numbers, the negative ten thousand indicates to find all the triangles with sides <u>up</u> to 10,000, but not list the triangles, it just reports the ''number'' of solutions found. |
|||
===using memoization=== |
|||
<pre> |
|||
<lang rexx>/*REXX pgm finds integer sided triangles that satisfy Law of cosines for 60º, 90º, 120º.*/ |
|||
══════════════════════════ 60º unique ══════════════════════════ |
|||
parse arg s1 s2 s3 s4 . /*obtain optional arguments from the CL*/ |
|||
18,394 solutions found for 60º unique (sides up to 10,000) |
|||
if s1=='' | s1=="," then s1= 13 /*Not specified? Then use the default.*/ |
|||
</pre> |
|||
if s2=='' | s2=="," then s2= 13 /* " " " " " " */ |
|||
if s3=='' | s3=="," then s3= 13 /* " " " " " " */ |
|||
if s4=='' | s4=="," then s4= -10000 /* " " " " " " */ |
|||
parse value s1 s2 s3 s4 with os1 os2 os3 os4 . /*obtain the original values for sides.*/ |
|||
s1=abs(s1); s2=abs(s2); s3=abs(s3); s4=abs(s4) /*use absolute values for the # sides. */ |
|||
@.= |
|||
do j=1 for max(s1, s2, s3, s4); @.j = j*j |
|||
end /*j*/ /*build memoization array for squaring.*/ |
|||
=={{header|RPL}}== |
|||
if s1>0 then do; call head 120,,os1 /*────120º: a² + b² + ab ≡ c² */ |
|||
{{works with|HP|28}} |
|||
do a=1 for s1 |
|||
≪ → formula max |
|||
do b=a+1 to s1; x= @.a + @.b + a*b |
|||
≪ { } |
|||
if x>z then iterate a |
|||
1 max '''FOR''' a a max '''FOR''' b |
|||
do c=b+1 to s1 until @.c>x |
|||
formula EVAL √ RND |
|||
if x==@.c then do; call show; iterate b; end |
|||
'''IF''' DUP max ≤ OVER FP NOT AND |
|||
'''THEN''' a b ROT 3 →LIST 1 →LIST + |
|||
'''ELSE''' DROP '''END''' |
|||
end /*a*/ |
|||
'''NEXT NEXT''' |
|||
≫ ≫ ‘<span style="color:blue">TASK</span>’ STO |
|||
end |
|||
'a^2+b^2' 13 <span style="color:blue">TASK</span> |
|||
if s2>0 then do; call head 90,, os2 /*────90º: a² + b² ≡ c² */ |
|||
'a^2+b^2-a*b' 13 <span style="color:blue">TASK</span> |
|||
do a=1 for s2 |
|||
'a^2+b^2+a*b' 13 <span style="color:blue">TASK</span> |
|||
do b=a+1 to s2; x= @.a + @.b |
|||
{{out}} |
|||
if x>z then iterate a |
|||
do c=b+1 to s2 until @.c>x |
|||
if x==@.c then do; call show; iterate b; end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s2 |
|||
end |
|||
if s3>0 then do; call head 60,, os3 /*────60º: a² + b² ─ ab ≡ c² */ |
|||
do a=1 for s3 |
|||
do b=a to s3; x= @.a + @.b - a*b |
|||
if x>z then iterate a |
|||
do c=a to s3 until @.c>x |
|||
if x==@.c then do; call show; iterate b; end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s3 |
|||
end |
|||
if s4>0 then do; call head 60, 'unique', os4 /*────60º: a² + b² ─ ab ≡ c² */ |
|||
do a=1 for s4 |
|||
do b=a to s4; x= @.a + @.b - a*b |
|||
if x>z then iterate a |
|||
do c=a to s4 until @.c>x |
|||
if x==@.c then do; if a==b&a==c then iterate b |
|||
call show; iterate b |
|||
end |
|||
end /*c*/ |
|||
end /*b*/ |
|||
end /*a*/ |
|||
call foot s4 |
|||
end |
|||
exit /*stick a fork in it, we're all done. */ |
|||
/*──────────────────────────────────────────────────────────────────────────────────────*/ |
|||
foot: say right(# ' solutions found for' ang "(sides up to" arg(1)')', 65); say; return |
|||
head: #=0; arg d,,s;z=s*s;w=length(s); ang=' 'd"º " arg(2); say center(ang,65,'═'); return |
|||
show: #= # + 1; if s>0 then say ' ('right(a,w)"," right(b,w)"," right(c,w)')'; return</lang> |
|||
{{out|output|text= when using the inputs of: <tt> 0 0 0 -10000 </tt>}} |
|||
Note that the first three computations are bypassed because of the three zero ('''0''') numbers, the negative ten thousand indicates to find all the triangles with sides up to 10,000, but not list the triangles, it just reports the ''number'' of triangles found. |
|||
<pre> |
<pre> |
||
3: { { 4 3 5 } { 8 6 10 } { 12 5 13 } } |
|||
══════════════════════════ 60º unique═══════════════════════════ |
|||
2: { { 1 1 1 } { 2 2 2 } { 3 3 3 } { 4 4 4 } { 5 5 5 } { 6 6 6 } { 7 7 7 } { 8 3 7 } { 8 5 7 } { 8 8 8 } { 9 9 9 } { 10 10 10 } { 11 11 11 } { 12 12 12 } { 13 13 13 } } |
|||
18394 solutions found for 60º unique (sides up to 10000) |
|||
1: { { 5 3 7 } { 8 7 13 } } |
|||
</pre> |
</pre> |
||
=={{header|Ruby}}== |
=={{header|Ruby}}== |
||
< |
<syntaxhighlight lang="ruby">grouped = (1..13).to_a.repeated_permutation(3).group_by do |a,b,c| |
||
sumaabb, ab = a*a + b*b, a*b |
sumaabb, ab = a*a + b*b, a*b |
||
case c*c |
case c*c |
||
Line 1,829: | Line 2,586: | ||
puts v.inspect, "\n" |
puts v.inspect, "\n" |
||
end |
end |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>For an angle of 60 there are 15 solutions: |
<pre>For an angle of 60 there are 15 solutions: |
||
Line 1,841: | Line 2,598: | ||
</pre> |
</pre> |
||
Extra credit: |
Extra credit: |
||
< |
<syntaxhighlight lang="ruby">n = 10_000 |
||
ar = (1..n).to_a |
ar = (1..n).to_a |
||
squares = {} |
squares = {} |
||
Line 1,848: | Line 2,605: | ||
puts "There are #{count} 60° triangles with unequal sides of max size #{n}." |
puts "There are #{count} 60° triangles with unequal sides of max size #{n}." |
||
</syntaxhighlight> |
|||
</lang> |
|||
{{out}} |
{{out}} |
||
<pre>There are 18394 60° triangles with unequal sides of max size 10000. |
<pre>There are 18394 60° triangles with unequal sides of max size 10000. |
||
</pre> |
|||
=={{header|Wren}}== |
|||
{{trans|Go}} |
|||
<syntaxhighlight lang="wren">var squares13 = {} |
|||
var squares10000 = {} |
|||
var initMaps = Fn.new { |
|||
for (i in 1..13) squares13[i*i] = i |
|||
for (i in 1..10000) squares10000[i*i] = i |
|||
} |
|||
var solve = Fn.new { |angle, maxLen, allowSame| |
|||
var solutions = [] |
|||
for (a in 1..maxLen) { |
|||
for (b in a..maxLen) { |
|||
var lhs = a*a + b*b |
|||
if (angle != 90) { |
|||
if (angle == 60) { |
|||
lhs = lhs - a*b |
|||
} else if (angle == 120) { |
|||
lhs = lhs + a*b |
|||
} else { |
|||
Fiber.abort("Angle must be 60, 90 or 120 degrees") |
|||
} |
|||
} |
|||
if (maxLen == 13) { |
|||
var c = squares13[lhs] |
|||
if (c != null) { |
|||
if (allowSame || a != b || b != c) { |
|||
solutions.add([a, b, c]) |
|||
} |
|||
} |
|||
} else if (maxLen == 10000) { |
|||
var c = squares10000[lhs] |
|||
if (c != null) { |
|||
if (allowSame || a != b || b != c) { |
|||
solutions.add([a, b, c]) |
|||
} |
|||
} |
|||
} else { |
|||
Fiber.abort("Maximum length must be either 13 or 10000") |
|||
} |
|||
} |
|||
} |
|||
return solutions |
|||
} |
|||
initMaps.call() |
|||
System.write("For sides in the range [1, 13] ") |
|||
System.print("where they can all be of the same length:-\n") |
|||
var angles = [90, 60, 120] |
|||
var solutions = [] |
|||
for (angle in angles) { |
|||
solutions = solve.call(angle, 13, true) |
|||
System.write(" For an angle of %(angle) degrees") |
|||
System.print(" there are %(solutions.count) solutions, namely:") |
|||
System.print(" %(solutions)") |
|||
System.print() |
|||
} |
|||
System.write("For sides in the range [1, 10000] ") |
|||
System.print("where they cannot ALL be of the same length:-\n") |
|||
solutions = solve.call(60, 10000, false) |
|||
System.write(" For an angle of 60 degrees") |
|||
System.print(" there are %(solutions.count) solutions.")</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
For sides in the range [1, 13] where they can all be of the same length:- |
|||
For an angle of 90 degrees there are 3 solutions, namely: |
|||
[[3, 4, 5], [5, 12, 13], [6, 8, 10]] |
|||
For an angle of 60 degrees there are 15 solutions, namely: |
|||
[[1, 1, 1], [2, 2, 2], [3, 3, 3], [3, 8, 7], [4, 4, 4], [5, 5, 5], [5, 8, 7], [6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9], [10, 10, 10], [11, 11, 11], [12, 12, 12], [13, 13, 13]] |
|||
For an angle of 120 degrees there are 2 solutions, namely: |
|||
[[3, 5, 7], [7, 8, 13]] |
|||
For sides in the range [1, 10000] where they cannot ALL be of the same length:- |
|||
For an angle of 60 degrees there are 18394 solutions. |
|||
</pre> |
|||
=={{header|XPL0}}== |
|||
<syntaxhighlight lang="xpl0">proc LawCos(Eqn); |
|||
int Eqn; |
|||
int Cnt, A, B, C; |
|||
proc Show; |
|||
[Cnt:= Cnt+1; |
|||
IntOut(0, A); ChOut(0, ^ ); |
|||
IntOut(0, B); ChOut(0, ^ ); |
|||
IntOut(0, C); CrLf(0); |
|||
]; |
|||
[Cnt:= 0; |
|||
for A:= 1 to 13 do |
|||
for B:= 1 to A do |
|||
for C:= 1 to 13 do |
|||
case Eqn of |
|||
1: if A*A + B*B = C*C then Show; |
|||
2: if A*A + B*B - A*B = C*C then Show; |
|||
3: if A*A + B*B + A*B = C*C then Show |
|||
other []; |
|||
IntOut(0, Cnt); Text(0, " results^m^j"); |
|||
]; |
|||
proc ExtraCredit; |
|||
int Cnt, A, B, C, C2; |
|||
[Cnt:= 0; |
|||
for A:= 1 to 10_000 do |
|||
for B:= 1 to A-1 do |
|||
[C2:= A*A + B*B - A*B; |
|||
C:= sqrt(C2); |
|||
if C*C = C2 then Cnt:= Cnt+1; |
|||
]; |
|||
Text(0, "Extra credit: "); |
|||
IntOut(0, Cnt); |
|||
CrLf(0); |
|||
]; |
|||
int Case; |
|||
[for Case:= 1 to 3 do LawCos(Case); |
|||
ExtraCredit; |
|||
]</syntaxhighlight> |
|||
{{out}} |
|||
<pre> |
|||
4 3 5 |
|||
8 6 10 |
|||
12 5 13 |
|||
3 results |
|||
1 1 1 |
|||
2 2 2 |
|||
3 3 3 |
|||
4 4 4 |
|||
5 5 5 |
|||
6 6 6 |
|||
7 7 7 |
|||
8 3 7 |
|||
8 5 7 |
|||
8 8 8 |
|||
9 9 9 |
|||
10 10 10 |
|||
11 11 11 |
|||
12 12 12 |
|||
13 13 13 |
|||
15 results |
|||
5 3 7 |
|||
8 7 13 |
|||
2 results |
|||
Extra credit: 18394 |
|||
</pre> |
</pre> |
||
=={{header|zkl}}== |
=={{header|zkl}}== |
||
< |
<syntaxhighlight lang="zkl">fcn tritri(N=13){ |
||
sqrset:=[0..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n }); |
sqrset:=[0..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n }); |
||
tri90, tri60, tri120 := List(),List(),List(); |
tri90, tri60, tri120 := List(),List(),List(); |
||
Line 1,875: | Line 2,785: | ||
tri.sort(fcn(t1,t2){ t1[0]<t2[0] }) |
tri.sort(fcn(t1,t2){ t1[0]<t2[0] }) |
||
.apply("concat",",").apply("(%s)".fmt).concat(",") |
.apply("concat",",").apply("(%s)".fmt).concat(",") |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">N:=13; |
||
println("Integer triangular triples for sides 1..%d:".fmt(N)); |
println("Integer triangular triples for sides 1..%d:".fmt(N)); |
||
foreach angle, triples in (T(60,90,120).zip(tritri(N))){ |
foreach angle, triples in (T(60,90,120).zip(tritri(N))){ |
||
println(" %3d\U00B0; has %d solutions:\n %s" |
println(" %3d\U00B0; has %d solutions:\n %s" |
||
.fmt(angle,triples.len(),triToStr(triples))); |
.fmt(angle,triples.len(),triToStr(triples))); |
||
}</ |
}</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
||
Line 1,893: | Line 2,803: | ||
</pre> |
</pre> |
||
Extra credit: |
Extra credit: |
||
< |
<syntaxhighlight lang="zkl">fcn tri60(N){ // special case 60* |
||
sqrset:=[1..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n }); |
sqrset:=[1..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n }); |
||
n60:=0; |
n60:=0; |
||
Line 1,901: | Line 2,811: | ||
} |
} |
||
n60 |
n60 |
||
}</ |
}</syntaxhighlight> |
||
< |
<syntaxhighlight lang="zkl">N:=10_000; |
||
println(("60\U00b0; triangle where side lengths are unique,\n" |
println(("60\U00b0; triangle where side lengths are unique,\n" |
||
" side lengths 1..%,d, there are %,d solutions.").fmt(N,tri60(N)));</ |
" side lengths 1..%,d, there are %,d solutions.").fmt(N,tri60(N)));</syntaxhighlight> |
||
{{out}} |
{{out}} |
||
<pre> |
<pre> |
Latest revision as of 09:52, 17 December 2023
You are encouraged to solve this task according to the task description, using any language you may know.
The Law of cosines states that for an angle γ, (gamma) of any triangle, if the sides adjacent to the angle are A and B and the side opposite is C; then the lengths of the sides are related by this formula:
A2 + B2 - 2ABcos(γ) = C2
- Specific angles
For an angle of of 90º this becomes the more familiar "Pythagoras equation":
A2 + B2 = C2
For an angle of 60º this becomes the less familiar equation:
A2 + B2 - AB = C2
And finally for an angle of 120º this becomes the equation:
A2 + B2 + AB = C2
- Task
- Find all integer solutions (in order) to the three specific cases, distinguishing between each angle being considered.
- Restrain all sides to the integers 1..13 inclusive.
- Show how many results there are for each of the three angles mentioned above.
- Display results on this page.
Note: Triangles with the same length sides but different order are to be treated as the same.
- Optional Extra credit
- How many 60° integer triples are there for sides in the range 1..10_000 where the sides are not all of the same length.
- Related Task
- See also
- Visualising Pythagoras: ultimate proofs and crazy contortions Mathlogger Video
11l
-V n = 13
F method1(n)
V squares = (0..n).map(x -> x ^ 2)
V sqrset = Set(squares)
Set[(Int, Int, Int)] tri90, tri60, tri120
L(a) 1..n
V a2 = squares[a]
L(b) 1..a
V b2 = squares[b]
V c2 = a2 + b2
I c2 C sqrset
tri90.add(tuple_sorted((a, b, Int(sqrt(c2)))))
V ab = a * b
c2 -= ab
I c2 C sqrset
tri60.add(tuple_sorted((a, b, Int(sqrt(c2)))))
c2 += 2 * ab
I c2 C sqrset
tri120.add(tuple_sorted((a, b, Int(sqrt(c2)))))
R [sorted(Array(tri90)),
sorted(Array(tri60)),
sorted(Array(tri120))]
print(‘Integer triangular triples for sides 1..#.:’.format(n))
L(angle, triples) zip([90, 60, 120], method1(n))
print(" #3° has #. solutions:\n #.".format(angle, triples.len, triples))
V t60 = method1(10'000)[1]
V notsame = sum(t60.filter((a, b, c) -> a != b | b != c).map((a, b, c) -> 1))
print(‘Extra credit: ’notsame)
- Output:
Integer triangular triples for sides 1..13: 90° has 3 solutions: [(3, 4, 5), (5, 12, 13), (6, 8, 10)] 60° has 15 solutions: [(1, 1, 1), (2, 2, 2), (3, 3, 3), (3, 7, 8), (4, 4, 4), (5, 5, 5), (5, 7, 8), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10), (11, 11, 11), (12, 12, 12), (13, 13, 13)] 120° has 2 solutions: [(3, 5, 7), (7, 8, 13)] Extra credit: 18394
Action!
PROC Test(INT max,angle,coeff)
BYTE count,a,b,c
PrintF("gamma=%B degrees:%E",angle)
count=0
FOR a=1 TO max
DO
FOR b=1 TO a
DO
FOR c=1 TO max
DO
IF a*a+b*b-coeff*a*b=c*c THEN
PrintF("(%B,%B,%B) ",a,b,c)
count==+1
FI
OD
OD
OD
PrintF("%Enumber of triangles is %B%E%E",count)
RETURN
PROC Main()
Test(13,90,0)
Test(13,60,1)
Test(13,120,-1)
RETURN
- Output:
Screenshot from Atari 8-bit computer
gamma=90 degrees: (4,3,5) (8,6,10) (12,5,13) number of triangles is 3 gamma=60 degrees: (1,1,1) (2,2,2) (3,3,3) (4,4,4) (5,5,5) (6,6,6) (7,7,7) (8,3,7) (8,5,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) number of triangles is 15 gamma=120 degrees: (5,3,7) (8,7,13) number of triangles is 2
Ada
with Ada.Text_IO;
with Ada.Containers.Ordered_Maps;
procedure Law_Of_Cosines is
type Angle_Kind is (Angle_60, Angle_90, Angle_120);
function Is_Triangle (A, B, C : in Positive;
Angle : in Angle_Kind) return Boolean
is
A2 : constant Positive := A**2;
B2 : constant Positive := B**2;
C2 : constant Positive := C**2;
AB : constant Positive := A * B;
begin
case Angle is
when Angle_60 => return A2 + B2 - AB = C2;
when Angle_90 => return A2 + B2 = C2;
when Angle_120 => return A2 + B2 + AB = C2;
end case;
end Is_Triangle;
procedure Count_Triangles is
use Ada.Text_IO;
Count : Natural;
begin
for Angle in Angle_Kind loop
Count := 0;
Put_Line (Angle'Image & " triangles");
for A in 1 ..13 loop
for B in 1 .. A loop
for C in 1 .. 13 loop
if Is_Triangle (A, B, C, Angle) then
Put_Line (A'Image & B'Image & C'Image);
Count := Count + 1;
end if;
end loop;
end loop;
end loop;
Put_Line ("There are " & Count'Image & " " & Angle'Image &" triangles");
end loop;
end Count_Triangles;
procedure Extra_Credit (Limit : in Natural) is
use Ada.Text_IO;
package Square_Maps is new Ada.Containers.Ordered_Maps (Natural, Boolean);
Squares : Square_Maps.Map;
Count : Natural := 0;
begin
for C in 1 .. Limit loop
Squares.Insert (C**2, True);
end loop;
for A in 1 .. Limit loop
for B in 1 .. A loop
if Squares.Contains (A**2 + B**2 - A * B) then
Count := Count + 1;
end if;
end loop;
end loop;
Put_Line ("There are " & Natural'(Count - Limit)'Image &
" " & Angle_60'Image &" triangles between 1 and " & Limit'Image & ".");
end Extra_Credit;
begin
Count_Triangles;
Extra_Credit (Limit => 10_000);
end Law_Of_Cosines;
- Output:
ANGLE_60 triangles 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 3 7 8 5 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 There are 15 ANGLE_60 triangles ANGLE_90 triangles 4 3 5 8 6 10 12 5 13 There are 3 ANGLE_90 triangles ANGLE_120 triangles 5 3 7 8 7 13 There are 2 ANGLE_120 triangles There are 18394 ANGLE_60 triangles between 1 and 10000.
ALGOL 68
BEGIN
# find all integer sided 90, 60 and 120 degree triangles by finding integer solutions for #
# a^2 + b^2 = c^2, a^2 + b^2 - ab = c^2, a^2 + b^2 + ab = c^2 where a, b, c in 1 .. 13 #
INT max side = 13; # max triangle side to consider #
INT max square = max side * max side; # max triangle side squared to consider #
[ 1 : max square ]INT root; # table of square roots #
FOR s TO UPB root DO root[ s ] := 0 OD;
FOR s TO max side DO root[ s * s ] := s OD;
INT tcount := 0;
[ 1 : max square ]INT ta, tb, tc, tangle;
# prints solutions for the specified angle #
PROC print triangles = ( INT angle )VOID:
BEGIN
INT scount := 0;
FOR t TO tcount DO IF tangle[ t ] = angle THEN scount +:= 1 FI OD;
print( ( whole( scount, -4 ), " ", whole( angle, -3 ), " degree triangles:", newline ) );
FOR t TO tcount DO
IF tangle[ t ] = angle THEN
print( ( " ", whole( ta[ t ], -3 ), whole( tb[ t ], -3 ), whole( tc[ t ], -3 ), newline ) )
FI
OD
END # print triangles # ;
# stores the triangle with sides a, b, root[ c2 ] and the specified angle, #
# if it is a solution #
PROC try triangle = ( INT a, b, c2, angle )VOID:
IF c2 <= max square THEN
# the third side is small enough #
INT c = root[ c2 ];
IF c /= 0 THEN
# the third side is the square of an integer #
tcount +:= 1;
ta[ tcount ] := a; tb[ tcount ] := b; tc[ tcount ] := root[ c2 ];
tangle[ tcount ] := angle
FI
FI # try triangle # ;
# find all triangles #
FOR a TO max side DO
FOR b FROM a TO max side DO
try triangle( a, b, ( a * a ) + ( b * b ) - ( a * b ), 60 );
try triangle( a, b, ( a * a ) + ( b * b ), 90 );
try triangle( a, b, ( a * a ) + ( b * b ) + ( a * b ), 120 )
OD
OD;
# print the solutions #
print triangles( 60 );
print triangles( 90 );
print triangles( 120 )
END
- Output:
15 60 degree triangles: 1 1 1 2 2 2 3 3 3 3 8 7 4 4 4 5 5 5 5 8 7 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 3 90 degree triangles: 3 4 5 5 12 13 6 8 10 2 120 degree triangles: 3 5 7 7 8 13
AWK
# syntax: GAWK -f LAW_OF_COSINES_-_TRIPLES.AWK
# converted from C
BEGIN {
description[1] = "90 degrees, a*a + b*b = c*c"
description[2] = "60 degrees, a*a + b*b - a*b = c*c"
description[3] = "120 degrees, a*a + b*b + a*b = c*c"
split("0,1,-1",coeff,",")
main(13,1,0)
main(1000,0,1) # 10,000 takes too long
exit(0)
}
function main(max_side_length,show_sides,no_dups, a,b,c,count,k) {
printf("\nmaximum side length: %d\n",max_side_length)
for (k=1; k<=3; k++) {
count = 0
for (a=1; a<=max_side_length; a++) {
for (b=1; b<=a; b++) {
for (c=1; c<=max_side_length; c++) {
if (a*a + b*b - coeff[k] * a*b == c*c) {
if (no_dups && (a == b || b == c)) {
continue
}
count++
if (show_sides) {
printf(" %d %d %d\n",a,b,c)
}
}
}
}
}
printf("%d triangles, %s\n",count,description[k])
}
}
- Output:
maximum side length: 13 4 3 5 8 6 10 12 5 13 3 triangles, 90 degrees, a*a + b*b = c*c 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 3 7 8 5 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 15 triangles, 60 degrees, a*a + b*b - a*b = c*c 5 3 7 8 7 13 2 triangles, 120 degrees, a*a + b*b + a*b = c*c maximum side length: 1000 881 triangles, 90 degrees, a*a + b*b = c*c 1260 triangles, 60 degrees, a*a + b*b - a*b = c*c 719 triangles, 120 degrees, a*a + b*b + a*b = c*c
C
A brute force algorithm, O(N^3)
/*
* RossetaCode: Law of cosines - triples
*
* An quick and dirty brute force solutions with O(N^3) cost.
* Anyway it is possible set MAX_SIDE_LENGTH equal to 10000
* and use fast computer to obtain the "extra credit" badge.
*
* Obviously, there are better algorithms.
*/
#include <stdio.h>
#include <math.h>
#define MAX_SIDE_LENGTH 13
//#define DISPLAY_TRIANGLES 1
int main(void)
{
static char description[3][80] = {
"gamma = 90 degrees, a*a + b*b == c*c",
"gamma = 60 degrees, a*a + b*b - a*b == c*c",
"gamma = 120 degrees, a*a + b*b + a*b == c*c"
};
static int coeff[3] = { 0, 1, -1 };
for (int k = 0; k < 3; k++)
{
int counter = 0;
for (int a = 1; a <= MAX_SIDE_LENGTH; a++)
for (int b = 1; b <= a; b++)
for (int c = 1; c <= MAX_SIDE_LENGTH; c++)
if (a * a + b * b - coeff[k] * a * b == c * c)
{
counter++;
#ifdef DISPLAY_TRIANGLES
printf(" %d %d %d\n", a, b, c);
#endif
}
printf("%s, number of triangles = %d\n", description[k], counter);
}
return 0;
}
- Output:
gamma = 90 degrees, a*a + b*b == c*c, number of triangles = 3 gamma = 60 degrees, a*a + b*b - a*b == c*c, number of triangles = 15 gamma = 120 degrees, a*a + b*b + a*b == c*c, number of triangles = 2
An algorithm with O(N^2) cost
/*
* RossetaCode: Law of cosines - triples
*
* A solutions with O(N^2) cost.
*/
#include <stdio.h>
#include <math.h>
#define MAX_SIDE_LENGTH 10000
//#define DISPLAY_TRIANGLES
int main(void)
{
static char description[3][80] = {
"gamma = 90 degrees, a*a + b*b == c*c",
"gamma = 60 degrees, a*a + b*b - a*b == c*c",
"gamma = 120 degrees, a*a + b*b + a*b == c*c"
};
static int coeff[3] = { 0, 1, -1 };
printf("MAX SIDE LENGTH = %d\n\n", MAX_SIDE_LENGTH);
for (int k = 0; k < 3; k++)
{
int counter = 0;
for (int a = 1; a <= MAX_SIDE_LENGTH; a++)
for (int b = 1; b <= a; b++)
{
int cc = a * a + b * b - coeff[k] * a * b;
int c = (int)(sqrt(cc) + 0.5);
if (c <= MAX_SIDE_LENGTH && c * c == cc)
{
#ifdef DISPLAY_TRIANGLES
printf("%d %d %d\n", a, b, c);
#endif
counter++;
}
}
printf("%s, number of triangles = %d\n", description[k], counter);
}
return 0;
}
- Output:
MAX SIDE LENGTH = 10000 gamma = 90 degrees, a*a + b*b == c*c, number of triangles = 12471 gamma = 60 degrees, a*a + b*b - a*b == c*c, number of triangles = 28394 gamma = 120 degrees, a*a + b*b + a*b == c*c, number of triangles = 10374
C++
#include <cmath>
#include <iostream>
#include <tuple>
#include <vector>
using triple = std::tuple<int, int, int>;
void print_triple(std::ostream& out, const triple& t) {
out << '(' << std::get<0>(t) << ',' << std::get<1>(t) << ',' << std::get<2>(t) << ')';
}
void print_vector(std::ostream& out, const std::vector<triple>& vec) {
if (vec.empty())
return;
auto i = vec.begin();
print_triple(out, *i++);
for (; i != vec.end(); ++i) {
out << ' ';
print_triple(out, *i);
}
out << "\n\n";
}
int isqrt(int n) {
return static_cast<int>(std::sqrt(n));
}
int main() {
const int min = 1, max = 13;
std::vector<triple> solutions90, solutions60, solutions120;
for (int a = min; a <= max; ++a) {
int a2 = a * a;
for (int b = a; b <= max; ++b) {
int b2 = b * b, ab = a * b;
int c2 = a2 + b2;
int c = isqrt(c2);
if (c <= max && c * c == c2)
solutions90.emplace_back(a, b, c);
else {
c2 = a2 + b2 - ab;
c = isqrt(c2);
if (c <= max && c * c == c2)
solutions60.emplace_back(a, b, c);
else {
c2 = a2 + b2 + ab;
c = isqrt(c2);
if (c <= max && c * c == c2)
solutions120.emplace_back(a, b, c);
}
}
}
}
std::cout << "There are " << solutions60.size() << " solutions for gamma = 60 degrees:\n";
print_vector(std::cout, solutions60);
std::cout << "There are " << solutions90.size() << " solutions for gamma = 90 degrees:\n";
print_vector(std::cout, solutions90);
std::cout << "There are " << solutions120.size() << " solutions for gamma = 120 degrees:\n";
print_vector(std::cout, solutions120);
const int max2 = 10000;
int count = 0;
for (int a = min; a <= max2; ++a) {
for (int b = a + 1; b <= max2; ++b) {
int c2 = a * a + b * b - a * b;
int c = isqrt(c2);
if (c <= max2 && c * c == c2)
++count;
}
}
std::cout << "There are " << count << " solutions for gamma = 60 degrees in the range "
<< min << " to " << max2 << " where the sides are not all of the same length.\n";
return 0;
}
- Output:
There are 15 solutions for gamma = 60 degrees: (1,1,1) (2,2,2) (3,3,3) (3,8,7) (4,4,4) (5,5,5) (5,8,7) (6,6,6) (7,7,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) There are 3 solutions for gamma = 90 degrees: (3,4,5) (5,12,13) (6,8,10) There are 2 solutions for gamma = 120 degrees: (3,5,7) (7,8,13) There are 18394 solutions for gamma = 60 degrees in the range 1 to 10000 where the sides are not all of the same length.
C#
using System;
using System.Collections.Generic;
using static System.Linq.Enumerable;
public static class LawOfCosinesTriples
{
public static void Main2() {
PrintTriples(60, 13);
PrintTriples(90, 13);
PrintTriples(120, 13);
PrintTriples(60, 10_000, true, false);
}
private static void PrintTriples(int degrees, int maxSideLength, bool notAllTheSameLength = false, bool print = true) {
string s = $"{degrees} degree triangles in range 1..{maxSideLength}";
if (notAllTheSameLength) s += " where not all sides are the same";
Console.WriteLine(s);
int count = 0;
var triples = FindTriples(degrees, maxSideLength);
if (notAllTheSameLength) triples = triples.Where(NotAllTheSameLength);
foreach (var triple in triples) {
count++;
if (print) Console.WriteLine(triple);
}
Console.WriteLine($"{count} solutions");
}
private static IEnumerable<(int a, int b, int c)> FindTriples(int degrees, int maxSideLength) {
double radians = degrees * Math.PI / 180;
int coefficient = (int)Math.Round(Math.Cos(radians) * -2, MidpointRounding.AwayFromZero);
int maxSideLengthSquared = maxSideLength * maxSideLength;
return
from a in Range(1, maxSideLength)
from b in Range(1, a)
let cc = a * a + b * b + a * b * coefficient
where cc <= maxSideLengthSquared
let c = (int)Math.Sqrt(cc)
where c * c == cc
select (a, b, c);
}
private static bool NotAllTheSameLength((int a, int b, int c) triple) => triple.a != triple.b || triple.a != triple.c;
}
- Output:
60 degree triangles in range 1..13 (1, 1, 1) (2, 2, 2) (3, 3, 3) (4, 4, 4) (5, 5, 5) (6, 6, 6) (7, 7, 7) (8, 3, 7) (8, 5, 7) (8, 8, 8) (9, 9, 9) (10, 10, 10) (11, 11, 11) (12, 12, 12) (13, 13, 13) 15 solutions 90 degree triangles in range 1..13 (4, 3, 5) (8, 6, 10) (12, 5, 13) 3 solutions 120 degree triangles in range 1..13 (5, 3, 7) (8, 7, 13) 2 solutions 60 degree triangles in range 1..10000 where not all sides are the same 18394 solutions
Delphi
procedure FindTriples(Memo: TMemo; Max,Angle,Coeff: integer);
var Count,A,B,C: integer;
var S: string;
begin
Memo.Lines.Add(Format('Gamma= %d°',[Angle]));
Count:=0;
S:='';
for A:=1 to Max do
for B:=1 to A do
for C:=1 to Max do
if A*A+B*B-Coeff*A*B=C*C then
begin
Inc(Count);
S:=S+Format('(%d,%d,%d) ',[A,B,C]);
if (Count mod 3)=0 then S:=S+CRLF;
end;
Memo.Lines.Add(Format('Number of triangles = %d',[Count]));
Memo.Lines.Add(S);
end;
procedure LawOfCosines(Memo: TMemo);
begin
FindTriples(Memo,13,90,0);
FindTriples(Memo,13,60,1);
FindTriples(Memo,13,120,-1);
end;
- Output:
Gamma= 90° Number of triangles = 3 (4,3,5) (8,6,10) (12,5,13) Gamma= 60° Number of triangles = 15 (1,1,1) (2,2,2) (3,3,3) (4,4,4) (5,5,5) (6,6,6) (7,7,7) (8,3,7) (8,5,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) Gamma= 120° Number of triangles = 2 (5,3,7) (8,7,13) Elapsed Time: 9.768 ms.
Factor
USING: backtrack formatting kernel locals math math.ranges
sequences sets sorting ;
IN: rosetta-code.law-of-cosines
:: triples ( quot -- seq )
[
V{ } clone :> seen
13 [1,b] dup dup [ amb-lazy ] tri@ :> ( a b c )
a sq b sq + a b quot call( x x x -- x ) c sq =
{ b a c } seen member? not and
must-be-true { a b c } dup seen push
] bag-of ;
: show-solutions ( quot angle -- )
[ triples { } like dup length ] dip rot
"%d solutions for %d degrees:\n%u\n\n" printf ;
[ * + ] 120
[ 2drop 0 - ] 90
[ * - ] 60 [ show-solutions ] 2tri@
- Output:
2 solutions for 120 degrees: { { 3 5 7 } { 7 8 13 } } 3 solutions for 90 degrees: { { 3 4 5 } { 5 12 13 } { 6 8 10 } } 15 solutions for 60 degrees: { { 1 1 1 } { 2 2 2 } { 3 3 3 } { 3 8 7 } { 4 4 4 } { 5 5 5 } { 5 8 7 } { 6 6 6 } { 7 7 7 } { 8 8 8 } { 9 9 9 } { 10 10 10 } { 11 11 11 } { 12 12 12 } { 13 13 13 } }
Fortran
MODULE LAW_OF_COSINES
IMPLICIT NONE
CONTAINS
! Calculate the third side of a triangle using the cosine rule
REAL FUNCTION COSINE_SIDE(SIDE_A, SIDE_B, ANGLE)
INTEGER, INTENT(IN) :: SIDE_A, SIDE_B
REAL(8), INTENT(IN) :: ANGLE
COSINE_SIDE = SIDE_A**2 + SIDE_B**2 - 2*SIDE_A*SIDE_B*COS(ANGLE)
COSINE_SIDE = COSINE_SIDE**0.5
END FUNCTION COSINE_SIDE
! Convert an angle in degrees to radians
REAL(8) FUNCTION DEG2RAD(ANGLE)
REAL(8), INTENT(IN) :: ANGLE
REAL(8), PARAMETER :: PI = 4.0D0*DATAN(1.D0)
DEG2RAD = ANGLE*(PI/180)
END FUNCTION DEG2RAD
! Sort an array of integers
FUNCTION INT_SORTED(ARRAY) RESULT(SORTED)
INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY
INTEGER, DIMENSION(SIZE(ARRAY)) :: SORTED, TEMP
INTEGER :: MAX_VAL, DIVIDE
SORTED = ARRAY
TEMP = ARRAY
DIVIDE = SIZE(ARRAY)
DO WHILE (DIVIDE .NE. 1)
MAX_VAL = MAXVAL(SORTED(1:DIVIDE))
TEMP(DIVIDE) = MAX_VAL
TEMP(MAXLOC(SORTED(1:DIVIDE))) = SORTED(DIVIDE)
SORTED = TEMP
DIVIDE = DIVIDE - 1
END DO
END FUNCTION INT_SORTED
! Append an integer to the end of an array of integers
SUBROUTINE APPEND(ARRAY, ELEMENT)
INTEGER, DIMENSION(:), ALLOCATABLE, INTENT(INOUT) :: ARRAY
INTEGER, DIMENSION(:), ALLOCATABLE :: TEMP
INTEGER :: ELEMENT
INTEGER :: I, ISIZE
IF (ALLOCATED(ARRAY)) THEN
ISIZE = SIZE(ARRAY)
ALLOCATE(TEMP(ISIZE+1))
DO I=1, ISIZE
TEMP(I) = ARRAY(I)
END DO
TEMP(ISIZE+1) = ELEMENT
DEALLOCATE(ARRAY)
CALL MOVE_ALLOC(TEMP, ARRAY)
ELSE
ALLOCATE(ARRAY(1))
ARRAY(1) = ELEMENT
END IF
END SUBROUTINE APPEND
! Check if an array of integers contains a subset
LOGICAL FUNCTION CONTAINS_ARR(ARRAY, ELEMENT)
INTEGER, DIMENSION(:), INTENT(IN) :: ARRAY
INTEGER, DIMENSION(:) :: ELEMENT
INTEGER, DIMENSION(SIZE(ELEMENT)) :: TEMP, SORTED_ELEMENT
INTEGER :: I, COUNTER, J
COUNTER = 0
ELEMENT = INT_SORTED(ELEMENT)
DO I=1,SIZE(ARRAY),SIZE(ELEMENT)
TEMP = ARRAY(I:I+SIZE(ELEMENT)-1)
DO J=1,SIZE(ELEMENT)
IF (ELEMENT(J) .EQ. TEMP(J)) THEN
COUNTER = COUNTER + 1
END IF
END DO
IF (COUNTER .EQ. SIZE(ELEMENT)) THEN
CONTAINS_ARR = .TRUE.
RETURN
END IF
END DO
CONTAINS_ARR = .FALSE.
END FUNCTION CONTAINS_ARR
! Count and print cosine triples for the given angle in degrees
INTEGER FUNCTION COSINE_TRIPLES(MIN_NUM, MAX_NUM, ANGLE, PRINT_RESULTS) RESULT(COUNTER)
INTEGER, INTENT(IN) :: MIN_NUM, MAX_NUM
REAL(8), INTENT(IN) :: ANGLE
LOGICAL, INTENT(IN) :: PRINT_RESULTS
INTEGER, DIMENSION(:), ALLOCATABLE :: CANDIDATES
INTEGER, DIMENSION(3) :: CANDIDATE
INTEGER :: A, B
REAL :: C
COUNTER = 0
DO A = MIN_NUM, MAX_NUM
DO B = MIN_NUM, MAX_NUM
C = COSINE_SIDE(A, B, DEG2RAD(ANGLE))
IF (C .GT. MAX_NUM .OR. MOD(C, 1.) .NE. 0) THEN
CYCLE
END IF
CANDIDATE(1) = A
CANDIDATE(2) = B
CANDIDATE(3) = C
IF (.NOT. CONTAINS_ARR(CANDIDATES, CANDIDATE)) THEN
COUNTER = COUNTER + 1
CALL APPEND(CANDIDATES, CANDIDATE(1))
CALL APPEND(CANDIDATES, CANDIDATE(2))
CALL APPEND(CANDIDATES, CANDIDATE(3))
IF (PRINT_RESULTS) THEN
WRITE(*,'(A,I0,A,I0,A,I0,A)') " (", CANDIDATE(1), ", ", CANDIDATE(2), ", ", CANDIDATE(3), ")"
END IF
END IF
END DO
END DO
END FUNCTION COSINE_TRIPLES
END MODULE LAW_OF_COSINES
! Program prints the cosine triples for the angles 90, 60 and 120 degrees
! by using the cosine rule to find the third side of each candidate and
! checking that this is an integer. Candidates are appended to an array
! after the sides have been sorted into ascending order
! the array is repeatedly checked to ensure there are no duplicates.
PROGRAM LOC
USE LAW_OF_COSINES
REAL(8), DIMENSION(3) :: TEST_ANGLES = (/90., 60., 120./)
INTEGER :: I, COUNTER
DO I = 1,SIZE(TEST_ANGLES)
WRITE(*, '(F0.0, A)') TEST_ANGLES(I), " degree triangles: "
COUNTER = COSINE_TRIPLES(1, 13, TEST_ANGLES(I), .TRUE.)
WRITE(*,'(A, I0)') "TOTAL: ", COUNTER
WRITE(*,*) NEW_LINE('A')
END DO
END PROGRAM LOC
90. degree triangles: (3, 4, 5) (5, 12, 13) (6, 8, 10) TOTAL: 3 60. degree triangles: (1, 1, 1) (2, 2, 2) (3, 3, 3) (3, 7, 8) (4, 4, 4) (5, 5, 5) (6, 6, 6) (7, 7, 7) (3, 7, 8) (8, 8, 8) (9, 9, 9) (10, 10, 10) (11, 11, 11) (12, 12, 12) (13, 13, 13) TOTAL: 15 120. degree triangles: (3, 5, 7) (7, 8, 13) TOTAL: 2
FreeBASIC
' version 03-03-2019
' compile with: fbc -s console
#Define max 13
#Define Format(_x) Right(" " + Str(_x), 4)
Dim As UInteger a, b, c, a2, b2, c2, c60 , c90, c120
Dim As String s60, s90, s120
For a = 1 To max
a2 = a * a
For b = a To max
b2 = b * b
' 60 degrees
c2 = a2 + b * b - a * b
c = Sqr(c2)
If c * c = c2 AndAlso c <= max Then
s60 += Format(a) + Format(b) + Format(c) + Chr(10, 13)
c60 += 1
End If
' 90 degrees
c2 = a2 + b * b
c = Sqr(c2)
If c * c = c2 AndAlso c <= max Then
s90 += Format(a) + Format(b) + Format(c) + Chr(10, 13)
c90 += 1
End If
' 120 degrees
c2 = a2 + b * b + a * b
c = Sqr(c2)
If c * c = c2 AndAlso c <= max Then
s120 += Format(a) + Format(b) + Format(c) + Chr(10, 13)
c120 += 1
End If
Next
Next
Print Using "###: 60 degree triangles"; c60
Print s60
Print
Print Using "###: 90 degree triangles"; c90
Print s90
Print
Print Using "###: 120 degree triangles"; c120
Print s120
Print
#Undef max
#Define max 10000
c60 = 0
For a = 1 To max
a2 = a * a
For b = a +1 To max
c2 = a2 + b * (b - a)
c = Sqr(c2)
If c * c = c2 AndAlso c <= max Then
c60 += 1
End If
Next
Next
Print "For 60 degree triangles in the range [1, 10000]"
Print "There are "; c60; " triangles that have different length for a, b and c"
' empty keyboard buffer
While InKey <> "" : Wend
Print : Print "hit any key to end program"
Sleep
End
- Output:
15: 60 degree triangles 1 1 1 2 2 2 3 3 3 3 8 7 4 4 4 5 5 5 5 8 7 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 3: 90 degree triangles 3 4 5 5 12 13 6 8 10 2: 120 degree triangles 3 5 7 7 8 13 For 60 degree triangles in the range [1, 10000] There are 18394 triangles that have different length for a, b and c
Go
package main
import "fmt"
type triple struct{ a, b, c int }
var squares13 = make(map[int]int, 13)
var squares10000 = make(map[int]int, 10000)
func init() {
for i := 1; i <= 13; i++ {
squares13[i*i] = i
}
for i := 1; i <= 10000; i++ {
squares10000[i*i] = i
}
}
func solve(angle, maxLen int, allowSame bool) []triple {
var solutions []triple
for a := 1; a <= maxLen; a++ {
for b := a; b <= maxLen; b++ {
lhs := a*a + b*b
if angle != 90 {
switch angle {
case 60:
lhs -= a * b
case 120:
lhs += a * b
default:
panic("Angle must be 60, 90 or 120 degrees")
}
}
switch maxLen {
case 13:
if c, ok := squares13[lhs]; ok {
if !allowSame && a == b && b == c {
continue
}
solutions = append(solutions, triple{a, b, c})
}
case 10000:
if c, ok := squares10000[lhs]; ok {
if !allowSame && a == b && b == c {
continue
}
solutions = append(solutions, triple{a, b, c})
}
default:
panic("Maximum length must be either 13 or 10000")
}
}
}
return solutions
}
func main() {
fmt.Print("For sides in the range [1, 13] ")
fmt.Println("where they can all be of the same length:-\n")
angles := []int{90, 60, 120}
var solutions []triple
for _, angle := range angles {
solutions = solve(angle, 13, true)
fmt.Printf(" For an angle of %d degrees", angle)
fmt.Println(" there are", len(solutions), "solutions, namely:")
fmt.Printf(" %v\n", solutions)
fmt.Println()
}
fmt.Print("For sides in the range [1, 10000] ")
fmt.Println("where they cannot ALL be of the same length:-\n")
solutions = solve(60, 10000, false)
fmt.Print(" For an angle of 60 degrees")
fmt.Println(" there are", len(solutions), "solutions.")
}
- Output:
For sides in the range [1, 13] where they can all be of the same length:- For an angle of 90 degrees there are 3 solutions, namely: [{3 4 5} {5 12 13} {6 8 10}] For an angle of 60 degrees there are 15 solutions, namely: [{1 1 1} {2 2 2} {3 3 3} {3 8 7} {4 4 4} {5 5 5} {5 8 7} {6 6 6} {7 7 7} {8 8 8} {9 9 9} {10 10 10} {11 11 11} {12 12 12} {13 13 13}] For an angle of 120 degrees there are 2 solutions, namely: [{3 5 7} {7 8 13}] For sides in the range [1, 10000] where they cannot ALL be of the same length:- For an angle of 60 degrees there are 18394 solutions.
Haskell
import qualified Data.Map.Strict as Map
import qualified Data.Set as Set
import Data.Monoid ((<>))
triangles
:: (Map.Map Int Int -> Int -> Int -> Int -> Int -> Maybe Int)
-> Int
-> [(Int, Int, Int)]
triangles f n =
let mapRoots = Map.fromList $ ((,) =<< (^ 2)) <$> [1 .. n]
in Set.elems $
foldr
(\(suma2b2, a, b) triSet ->
(case f mapRoots suma2b2 (a * b) a b of
Just c -> Set.insert (a, b, c) triSet
_ -> triSet))
(Set.fromList [])
([1 .. n] >>=
(\a -> (flip (,,) a =<< (a * a +) . (>>= id) (*)) <$> [1 .. a]))
-- TESTS ------------------------------------------------------------------------
f90, f60, f60ne, f120 :: Map.Map Int Int -> Int -> Int -> Int -> Int -> Maybe Int
f90 dct x2 ab a b = Map.lookup x2 dct
f60 dct x2 ab a b = Map.lookup (x2 - ab) dct
f120 dct x2 ab a b = Map.lookup (x2 + ab) dct
f60ne dct x2 ab a b
| a == b = Nothing
| otherwise = Map.lookup (x2 - ab) dct
main :: IO ()
main = do
putStrLn
(unlines $
"Triangles of maximum side 13\n" :
zipWith
(\f n ->
let solns = triangles f 13
in show (length solns) <> " solutions for " <> show n <>
" degrees:\n" <>
unlines (show <$> solns))
[f120, f90, f60]
[120, 90, 60])
putStrLn "60 degrees - uneven triangles of maximum side 10000. Total:"
print $ length $ triangles f60ne 10000
- Output:
Triangles of maximum side 13 2 solutions for 120 degrees: (5,3,7) (8,7,13) 3 solutions for 90 degrees: (4,3,5) (8,6,10) (12,5,13) 15 solutions for 60 degrees: (1,1,1) (2,2,2) (3,3,3) (4,4,4) (5,5,5) (6,6,6) (7,7,7) (8,3,7) (8,5,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) 60 degrees - uneven triangles of maximum side 10000. Total: 18394
J
Solution:
load 'trig stats'
RHS=: *: NB. right-hand-side of Cosine Law
LHS=: +/@:*:@] - cos@rfd@[ * 2 * */@] NB. Left-hand-side of Cosine Law
solve=: 4 :0
adjsides=. >: 2 combrep y
oppside=. >: i. y
idx=. (RHS oppside) i. x LHS"1 adjsides
adjsides ((#~ idx ~: #) ,. ({~ idx -. #)@]) oppside
)
Example:
60 90 120 solve&.> 13
+--------+-------+------+
| 1 1 1|3 4 5|3 5 7|
| 2 2 2|5 12 13|7 8 13|
| 3 3 3|6 8 10| |
| 3 8 7| | |
| 4 4 4| | |
| 5 5 5| | |
| 5 8 7| | |
| 6 6 6| | |
| 7 7 7| | |
| 8 8 8| | |
| 9 9 9| | |
|10 10 10| | |
|11 11 11| | |
|12 12 12| | |
|13 13 13| | |
+--------+-------+------+
60 #@(solve -. _3 ]\ 3 # >:@i.@]) 10000 NB. optional extra credit
18394
Java
public class LawOfCosines {
public static void main(String[] args) {
generateTriples(13);
generateTriples60(10000);
}
private static void generateTriples(int max) {
for ( int coeff : new int[] {0, -1, 1} ) {
int count = 0;
System.out.printf("Max side length %d, formula: a*a + b*b %s= c*c%n", max, coeff == 0 ? "" : (coeff<0 ? "-" : "+") + " a*b ");
for ( int a = 1 ; a <= max ; a++ ) {
for ( int b = 1 ; b <= a ; b++ ) {
int val = a*a + b*b + coeff*a*b;
int c = (int) (Math.sqrt(val) + .5d);
if ( c > max ) {
break;
}
if ( c*c == val ) {
System.out.printf(" (%d, %d, %d)%n", a, b ,c);
count++;
}
}
}
System.out.printf("%d triangles%n", count);
}
}
private static void generateTriples60(int max) {
int count = 0;
System.out.printf("%nExtra Credit.%nMax side length %d, sides different length, formula: a*a + b*b - a*b = c*c%n", max);
for ( int a = 1 ; a <= max ; a++ ) {
for ( int b = 1 ; b < a ; b++ ) {
int val = a*a + b*b - a*b;
int c = (int) (Math.sqrt(val) + .5d);
if ( c*c == val ) {
count++;
}
}
}
System.out.printf("%d triangles%n", count);
}
}
- Output:
Max side length 13, formula: a*a + b*b = c*c (4, 3, 5) (8, 6, 10) (12, 5, 13) 3 triangles Max side length 13, formula: a*a + b*b - a*b = c*c (1, 1, 1) (2, 2, 2) (3, 3, 3) (4, 4, 4) (5, 5, 5) (6, 6, 6) (7, 7, 7) (8, 3, 7) (8, 5, 7) (8, 8, 8) (9, 9, 9) (10, 10, 10) (11, 11, 11) (12, 12, 12) (13, 13, 13) 15 triangles Max side length 13, formula: a*a + b*b + a*b = c*c (5, 3, 7) (8, 7, 13) 2 triangles Extra Credit. Max side length 10000, sides different length, formula: a*a + b*b - a*b = c*c 18394 triangles
JavaScript
(() => {
'use strict';
// main :: IO ()
const main = () => {
const
f90 = dct => x2 => dct[x2],
f60 = dct => (x2, ab) => dct[x2 - ab],
f120 = dct => (x2, ab) => dct[x2 + ab],
f60unequal = dct => (x2, ab, a, b) =>
(a !== b) ? (
dct[x2 - ab]
) : undefined;
// triangles :: Dict -> (Int -> Int -> Int -> Int -> Maybe Int)
// -> [String]
const triangles = (f, n) => {
const
xs = enumFromTo(1, n),
fr = f(xs.reduce((a, x) => (a[x * x] = x, a), {})),
gc = xs.reduce((a, _) => a, {}),
setSoln = new Set();
return (
xs.forEach(
a => {
const a2 = a * a;
enumFromTo(1, 1 + a).forEach(
b => {
const
suma2b2 = a2 + b * b,
c = fr(suma2b2, a * b, a, b);
if (undefined !== c) {
setSoln.add([a, b, c].sort())
};
}
);
}
),
Array.from(setSoln.keys())
);
};
const
result = 'Triangles of maximum side 13:\n\n' +
unlines(
zipWith(
(s, f) => {
const ks = triangles(f, 13);
return ks.length.toString() + ' solutions for ' + s +
' degrees:\n' + unlines(ks) + '\n';
},
['120', '90', '60'],
[f120, f90, f60]
)
) + '\nUneven triangles of maximum side 10000. Total:\n' +
triangles(f60unequal, 10000).length
return (
//console.log(result),
result
);
};
// GENERIC FUNCTIONS ----------------------------
// concatMap :: (a -> [b]) -> [a] -> [b]
const concatMap = (f, xs) =>
xs.reduce((a, x) => a.concat(f(x)), []);
// enumFromTo :: Int -> Int -> [Int]
const enumFromTo = (m, n) =>
m <= n ? iterateUntil(
x => n <= x,
x => 1 + x,
m
) : [];
// iterateUntil :: (a -> Bool) -> (a -> a) -> a -> [a]
const iterateUntil = (p, f, x) => {
const vs = [x];
let h = x;
while (!p(h))(h = f(h), vs.push(h));
return vs;
};
// Returns Infinity over objects without finite length
// this enables zip and zipWith to choose the shorter
// argument when one non-finite like cycle, repeat etc
// length :: [a] -> Int
const length = xs => xs.length || Infinity;
// take :: Int -> [a] -> [a]
// take :: Int -> String -> String
const take = (n, xs) =>
xs.constructor.constructor.name !== 'GeneratorFunction' ? (
xs.slice(0, n)
) : [].concat.apply([], Array.from({
length: n
}, () => {
const x = xs.next();
return x.done ? [] : [x.value];
}));
// unlines :: [String] -> String
const unlines = xs => xs.join('\n');
// Use of `take` and `length` here allows zipping with non-finite lists
// i.e. generators like cycle, repeat, iterate.
// Use of `take` and `length` here allows zipping with non-finite lists
// i.e. generators like cycle, repeat, iterate.
// zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
const zipWith = (f, xs, ys) => {
const
lng = Math.min(length(xs), length(ys)),
as = take(lng, xs),
bs = take(lng, ys);
return Array.from({
length: lng
}, (_, i) => f(as[i], bs[i], i));
};
// MAIN ---
return main();
})();
- Output:
Triangles of maximum side 13: 2 solutions for 120 degrees: 3,5,7 13,7,8 3 solutions for 90 degrees: 3,4,5 10,6,8 12,13,5 15 solutions for 60 degrees: 1,1,1 2,2,2 3,3,3 4,4,4 5,5,5 6,6,6 7,7,7 3,7,8 5,7,8 8,8,8 9,9,9 10,10,10 11,11,11 12,12,12 13,13,13 Uneven triangles of maximum side 10000. Total: 18394 [Finished in 3.444s]
jq
Adapted from Wren
Works with gojq, the Go implementation of jq
To save space, we define `squares` as a hash rather than as a JSON array.
def squares(n):
reduce range(1; 1+n) as $i ({}; .[$i*$i|tostring] = $i);
# if count, then just count
def solve(angle; maxLen; allowSame; count):
squares(maxLen) as $squares
| def qsqrt($n):
$squares[$n|tostring] as $sqrt
| if $sqrt then $sqrt else null end;
reduce range(1; maxLen+1) as $a ({};
reduce range($a; maxLen+1) as $b (.;
.lhs = $a*$a + $b*$b
| if angle != 90
then if angle == 60
then .lhs += ( - $a*$b)
elif angle == 120
then .lhs += $a*$b
else "Angle must be 60, 90 or 120 degrees" | error
end
else .
end
| qsqrt(.lhs) as $c
| if $c != null
then if allowSame or $a != $b or $b != $c
then .solutions += if count then 1 else [[$a, $b, $c]] end
else .
end
else .
end
)
)
| .solutions ;
def task1($angles):
"For sides in the range [1, 13] where they can all be of the same length:\n",
($angles[]
| . as $angle
| solve($angle; 13; true; false)
| " For an angle of \($angle) degrees, there are \(length) solutions, namely:", .);
def task2(degrees; n):
"For sides in the range [1, \(n)] where they cannot ALL be of the same length:",
(solve(degrees; n; false; true)
| " For an angle of \(degrees) degrees, there are \(.) solutions.") ;
task1([90, 60, 120]), "", task2(60; 10000)
- Output:
For sides in the range [1, 13] where they can all be of the same length: For an angle of 90 degrees, there are 3 solutions, namely: [[3,4,5],[5,12,13],[6,8,10]] For an angle of 60 degrees, there are 15 solutions, namely: [[1,1,1],[2,2,2],[3,3,3],[3,8,7],[4,4,4],[5,5,5],[5,8,7],[6,6,6],[7,7,7],[8,8,8],[9,9,9],[10,10,10],[11,11,11],[12,12,12],[13,13,13]] For an angle of 120 degrees, there are 2 solutions, namely: [[3,5,7],[7,8,13]] For sides in the range [1, 10000] where they cannot ALL be of the same length: For an angle of 60 degrees, there are 18394 solutions.
Julia
sqdict(n) = Dict([(x*x, x) for x in 1:n])
numnotsame(arrarr) = sum(map(x -> !all(y -> y == x[1], x), arrarr))
function filtertriangles(N)
sqd = sqdict(N)
t60 = Vector{Vector{Int}}()
t90 = Vector{Vector{Int}}()
t120 = Vector{Vector{Int}}()
for x in 1:N, y in 1:x
xsq, ysq, xy = (x*x, y*y, x*y)
if haskey(sqd, xsq + ysq - xy)
push!(t60, sort([x, y, sqd[xsq + ysq - xy]]))
elseif haskey(sqd, xsq + ysq)
push!(t90, sort([x, y, sqd[xsq + ysq]]))
elseif haskey(sqd, xsq + ysq + xy)
push!(t120, sort([x, y, sqd[xsq + ysq + xy]]))
end
end
t60, t90, t120
end
tri60, tri90, tri120 = filtertriangles(13)
println("Integer triples for 1 <= side length <= 13:\n")
println("Angle 60:"); for t in tri60 println(t) end
println("Angle 90:"); for t in tri90 println(t) end
println("Angle 120:"); for t in tri120 println(t) end
println("\nFor sizes N through 10000, there are $(numnotsame(filtertriangles(10000)[1])) 60 degree triples with nonequal sides.")
- Output:
Integer triples for 1 <= side length <= 13:
Angle 60: [1, 1, 1] [2, 2, 2] [3, 3, 3] [4, 4, 4] [5, 5, 5] [6, 6, 6] [7, 7, 7] [3, 7, 8] [5, 7, 8] [8, 8, 8] [9, 9, 9] [10, 10, 10] [11, 11, 11] [12, 12, 12] [13, 13, 13] Angle 90: [3, 4, 5] [6, 8, 10] [5, 12, 13] Angle 120: [3, 5, 7] [7, 8, 13]
For sizes N through 10000, there are 18394 60 degree triples with nonequal sides.
Kotlin
// Version 1.2.70
val squares13 = mutableMapOf<Int, Int>()
val squares10000 = mutableMapOf<Int, Int>()
class Trio(val a: Int, val b: Int, val c: Int) {
override fun toString() = "($a $b $c)"
}
fun init() {
for (i in 1..13) squares13.put(i * i, i)
for (i in 1..10000) squares10000.put(i * i, i)
}
fun solve(angle :Int, maxLen: Int, allowSame: Boolean): List<Trio> {
val solutions = mutableListOf<Trio>()
for (a in 1..maxLen) {
inner@ for (b in a..maxLen) {
var lhs = a * a + b * b
if (angle != 90) {
when (angle) {
60 -> lhs -= a * b
120 -> lhs += a * b
else -> throw RuntimeException("Angle must be 60, 90 or 120 degrees")
}
}
when (maxLen) {
13 -> {
val c = squares13[lhs]
if (c != null) {
if (!allowSame && a == b && b == c) continue@inner
solutions.add(Trio(a, b, c))
}
}
10000 -> {
val c = squares10000[lhs]
if (c != null) {
if (!allowSame && a == b && b == c) continue@inner
solutions.add(Trio(a, b, c))
}
}
else -> throw RuntimeException("Maximum length must be either 13 or 10000")
}
}
}
return solutions
}
fun main(args: Array<String>) {
init()
print("For sides in the range [1, 13] ")
println("where they can all be of the same length:-\n")
val angles = intArrayOf(90, 60, 120)
lateinit var solutions: List<Trio>
for (angle in angles) {
solutions = solve(angle, 13, true)
print(" For an angle of ${angle} degrees")
println(" there are ${solutions.size} solutions, namely:")
println(" ${solutions.joinToString(" ", "[", "]")}\n")
}
print("For sides in the range [1, 10000] ")
println("where they cannot ALL be of the same length:-\n")
solutions = solve(60, 10000, false)
print(" For an angle of 60 degrees")
println(" there are ${solutions.size} solutions.")
}
- Output:
For sides in the range [1, 13] where they can all be of the same length:- For an angle of 90 degrees there are 3 solutions, namely: [(3 4 5) (5 12 13) (6 8 10)] For an angle of 60 degrees there are 15 solutions, namely: [(1 1 1) (2 2 2) (3 3 3) (3 8 7) (4 4 4) (5 5 5) (5 8 7) (6 6 6) (7 7 7) (8 8 8) (9 9 9) (10 10 10) (11 11 11) (12 12 12) (13 13 13)] For an angle of 120 degrees there are 2 solutions, namely: [(3 5 7) (7 8 13)] For sides in the range [1, 10000] where they cannot ALL be of the same length:- For an angle of 60 degrees there are 18394 solutions.
Lua
function solve(angle, maxlen, filter)
local squares, roots, solutions = {}, {}, {}
local cos2 = ({[60]=-1,[90]=0,[120]=1})[angle]
for i = 1, maxlen do squares[i], roots[i^2] = i^2, i end
for a = 1, maxlen do
for b = a, maxlen do
local lhs = squares[a] + squares[b] + cos2*a*b
local c = roots[lhs]
if c and (not filter or filter(a,b,c)) then
solutions[#solutions+1] = {a=a,b=b,c=c}
end
end
end
print(angle.."° on 1.."..maxlen.." has "..#solutions.." solutions")
if not filter then
for i,v in ipairs(solutions) do print("",v.a,v.b,v.c) end
end
end
solve(90, 13)
solve(60, 13)
solve(120, 13)
function fexcr(a,b,c) return a~=b or b~=c end
solve(60, 10000, fexcr) -- extra credit
solve(90, 10000, fexcr) -- more extra credit
solve(120, 10000, fexcr) -- even more extra credit
- Output:
90° on 1..13 has 3 solutions 3 4 5 5 12 13 6 8 10 60° on 1..13 has 15 solutions 1 1 1 2 2 2 3 3 3 3 8 7 4 4 4 5 5 5 5 8 7 6 6 6 7 7 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 120° on 1..13 has 2 solutions 3 5 7 7 8 13 60° on 1..10000 has 18394 solutions 90° on 1..10000 has 12471 solutions 120° on 1..10000 has 10374 solutions
Mathematica/Wolfram Language
Solve[{a^2+b^2==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers]
Length[%]
Solve[{a^2+b^2-a b==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers]
Length[%]
Solve[{a^2+b^2+a b==c^2,1<=a<=13,1<=b<=13,1<=c<=13,a<=b},{a,b,c},Integers]
Length[%]
Solve[{a^2+b^2-a b==c^2,1<=a<=10000,1<=b<=10000,1<=c<=10000,a<=b,a!=b,b!=c,a!=c},{a,b,c},Integers]//Length
- Output:
{{a->3,b->4,c->5},{a->5,b->12,c->13},{a->6,b->8,c->10}} 3 {{a->1,b->1,c->1},{a->2,b->2,c->2},{a->3,b->3,c->3},{a->3,b->8,c->7},{a->4,b->4,c->4},{a->5,b->5,c->5},{a->5,b->8,c->7},{a->6,b->6,c->6},{a->7,b->7,c->7},{a->8,b->8,c->8},{a->9,b->9,c->9},{a->10,b->10,c->10},{a->11,b->11,c->11},{a->12,b->12,c->12},{a->13,b->13,c->13}} 15 {{a->3,b->5,c->7},{a->7,b->8,c->13}} 2 18394
Nim
import strformat
import tables
# Generate tables at compile time. This eliminates the initialization at
# the expense of a bigger executable.
const square13 = block:
var tmp = newSeq[tuple[a, b: int]]()
for i in 1..13:
tmp.add((i * i, i))
toTable(tmp)
const square10000 = block:
var tmp = newSeq[tuple[a, b: int]]()
for i in 1..10000:
tmp.add((i * i, i))
toTable(tmp)
proc solve(angle, maxLen: int, allowSame: bool): seq[tuple[a, b, c: int]] =
result = newSeq[tuple[a, b, c: int]]()
for a in 1..maxLen:
for b in a..maxLen:
var lhs = a * a + b * b
if angle != 90:
case angle
of 60:
dec lhs, a * b
of 120:
inc lhs, a * b
else:
raise newException(IOError, "Angle must be 60, 90 or 120 degrees")
case maxLen
of 13:
var c = square13.getOrDefault(lhs)
if (not allowSame and a == b and b == c) or c == 0:
continue
result.add((a, b, c))
of 10000:
var c = square10000.getOrDefault(lhs)
if (not allowSame and a == b and b == c) or c == 0:
continue
result.add((a, b, c))
else:
raise newException(IOError, "Maximum length must be either 13 or 10000")
echo "For sides in the range [1, 13] where they can all be the same length:\n"
let angles = [90, 60, 120]
for angle in angles:
var solutions = solve(angle, 13, true)
echo fmt" For an angle of {angle} degrees there are {len(solutions)} solutions, to wit:"
write(stdout, " ")
for i in 0..<len(solutions):
write(stdout, fmt"{solutions[i]:25}")
if i mod 3 == 2:
write(stdout, "\n ")
write(stdout, "\n")
echo "\nFor sides in the range [1, 10000] where they cannot ALL be of the same length:\n"
var solutions = solve(60, 10000, false)
echo fmt" For an angle of 60 degrees there are {len(solutions)} solutions."
- Output:
For sides in the range [1, 13] where they can all be the same length: For an angle of 90 degrees there are 3 solutions, to wit: (a: 3, b: 4, c: 5) (a: 5, b: 12, c: 13) (a: 6, b: 8, c: 10) For an angle of 60 degrees there are 15 solutions, to wit: (a: 1, b: 1, c: 1) (a: 2, b: 2, c: 2) (a: 3, b: 3, c: 3) (a: 3, b: 8, c: 7) (a: 4, b: 4, c: 4) (a: 5, b: 5, c: 5) (a: 5, b: 8, c: 7) (a: 6, b: 6, c: 6) (a: 7, b: 7, c: 7) (a: 8, b: 8, c: 8) (a: 9, b: 9, c: 9) (a: 10, b: 10, c: 10) (a: 11, b: 11, c: 11) (a: 12, b: 12, c: 12) (a: 13, b: 13, c: 13) For an angle of 120 degrees there are 2 solutions, to wit: (a: 3, b: 5, c: 7) (a: 7, b: 8, c: 13) For sides in the range [1, 10000] where they cannot ALL be of the same length: For an angle of 60 degrees there are 18394 solutions.
Perl
use utf8;
binmode STDOUT, "utf8:";
use Sort::Naturally;
sub triples {
my($n,$angle) = @_;
my(@triples,%sq);
$sq{$_**2}=$_ for 1..$n;
for $a (1..$n-1) {
for $b ($a+1..$n) {
my $ab = $a*$a + $b*$b;
my $cos = $angle == 60 ? $ab - $a * $b :
$angle == 120 ? $ab + $a * $b :
$ab;
if ($angle == 60) {
push @triples, "$a $sq{$cos} $b" if exists $sq{$cos};
} else {
push @triples, "$a $b $sq{$cos}" if exists $sq{$cos};
}
}
}
@triples;
}
$n = 13;
print "Integer triangular triples for sides 1..$n:\n";
for my $angle (120, 90, 60) {
my @itt = triples($n,$angle);
if ($angle == 60) { push @itt, "$_ $_ $_" for 1..$n }
printf "Angle %3d° has %2d solutions: %s\n", $angle, scalar @itt,
join ', ', nsort @itt;
}
printf "Non-equilateral n=10000/60°: %d\n", scalar triples(10000,60);
- Output:
Integer triangular triples for sides 1..13: Angle 120° has 2 solutions: 3 5 7, 7 8 13 Angle 90° has 3 solutions: 3 4 5, 6 8 10, 5 12 13 Angle 60° has 15 solutions: 1 1 1, 2 2 2, 3 3 3, 3 7 8, 4 4 4, 5 5 5, 5 7 8, 6 6 6, 7 7 7, 8 8 8, 9 9 9, 10 10 10, 11 11 11, 12 12 12, 13 13 13 Non-equilateral n=10000/60°: 18394
Phix
Using a simple flat sequence of 100 million elements (well within the desktop language limits, but beyond JavaScript) proved significantly faster than a dictionary (5x or so).
with javascript_semantics --constant lim = iff(platform()=JS?13:10000) constant lim = 10000 --sequence squares = repeat(0,lim*lim) sequence squares = iff(platform()=JS?{}:repeat(0,lim*lim)) for c=1 to lim do squares[c*c] = c end for function solve(integer angle, maxlen, bool samelen=true) sequence res = {} for a=1 to maxlen do integer a2 = a*a for b=a to maxlen do integer c2 = a2+b*b if angle!=90 then if angle=60 then c2 -= a*b elsif angle=120 then c2 += a*b else crash("angle must be 60/90/120") end if end if integer c = iff(c2>length(squares)?0:squares[c2]) if c!=0 and c<=maxlen then if samelen or a!=b or b!=c then res = append(res,{a,b,c}) end if end if end for end for return res end function procedure show(string fmt,sequence res, bool full=true) printf(1,fmt,{length(res),iff(full?sprint(res):"")}) end procedure puts(1,"Integer triangular triples for sides 1..13:\n") show("Angle 60 has %2d solutions: %s\n",solve( 60,13)) show("Angle 90 has %2d solutions: %s\n",solve( 90,13)) show("Angle 120 has %2d solutions: %s\n",solve(120,13)) --if platform()!=JS then show("Non-equilateral angle 60 triangles for sides 1..10000: %d%s\n",solve(60,10000,false),false) --end if
- Output:
Integer triangular triples for sides 1..13: Angle 60 has 15 solutions: {{1,1,1},{2,2,2},{3,3,3},{3,8,7},{4,4,4},{5,5,5},{5,8,7},{6,6,6},{7,7,7},{8,8,8},{9,9,9},{10,10,10},{11,11,11},{12,12,12},{13,13,13}} Angle 90 has 3 solutions: {{3,4,5},{5,12,13},{6,8,10}} Angle 120 has 2 solutions: {{3,5,7},{7,8,13}} Non-equilateral angle 60 triangles for sides 1..10000: 18394
As noted I had to resort to a little trickery to get that last line to run in JavaScript, should a future version impose stricter bounds checking that will stop working.
Prolog
find_solutions(Limit, Solutions):-
find_solutions(Limit, Solutions, Limit, []).
find_solutions(_, S, 0, S):-
!.
find_solutions(Limit, Solutions, A, S):-
find_solutions1(Limit, A, A, S1, S),
A_next is A - 1,
find_solutions(Limit, Solutions, A_next, S1).
find_solutions1(Limit, _, B, Triples, Triples):-
B > Limit,
!.
find_solutions1(Limit, A, B, [Triple|Triples], T):-
is_solution(Limit, A, B, Triple),
!,
B_next is B + 1,
find_solutions1(Limit, A, B_next, Triples, T).
find_solutions1(Limit, A, B, Triples, T):-
B_next is B + 1,
find_solutions1(Limit, A, B_next, Triples, T).
is_solution(Limit, A, B, t(Angle, A, B, C)):-
X is A * A + B * B,
Y is A * B,
(
Angle = 90, C is round(sqrt(X)), X is C * C
;
Angle = 120, C2 is X + Y, C is round(sqrt(C2)), C2 is C * C
;
Angle = 60, C2 is X - Y, C is round(sqrt(C2)), C2 is C * C
),
C =< Limit,
!.
write_triples(Angle, Solutions):-
find_triples(Angle, Solutions, List, 0, Count),
writef('There are %w solutions for gamma = %w:\n', [Count, Angle]),
write_triples1(List),
nl.
find_triples(_, [], [], Count, Count):-
!.
find_triples(Angle, [Triple|Triples], [Triple|Result], C, Count):-
Triple = t(Angle, _, _, _),
!,
C1 is C + 1,
find_triples(Angle, Triples, Result, C1, Count).
find_triples(Angle, [_|Triples], Result, C, Count):-
find_triples(Angle, Triples, Result, C, Count).
write_triples1([]):-!.
write_triples1([t(_, A, B, C)]):-
writef('(%w,%w,%w)\n', [A, B, C]),
!.
write_triples1([t(_, A, B, C)|Triples]):-
writef('(%w,%w,%w) ', [A, B, C]),
write_triples1(Triples).
main:-
find_solutions(13, Solutions),
write_triples(60, Solutions),
write_triples(90, Solutions),
write_triples(120, Solutions).
- Output:
There are 15 solutions for gamma = 60: (1,1,1) (2,2,2) (3,3,3) (3,8,7) (4,4,4) (5,5,5) (5,8,7) (6,6,6) (7,7,7) (8,8,8) (9,9,9) (10,10,10) (11,11,11) (12,12,12) (13,13,13) There are 3 solutions for gamma = 90: (3,4,5) (5,12,13) (6,8,10) There are 2 solutions for gamma = 120: (3,5,7) (7,8,13)
Python
Sets
N = 13
def method1(N=N):
squares = [x**2 for x in range(0, N+1)]
sqrset = set(squares)
tri90, tri60, tri120 = (set() for _ in range(3))
for a in range(1, N+1):
a2 = squares[a]
for b in range(1, a + 1):
b2 = squares[b]
c2 = a2 + b2
if c2 in sqrset:
tri90.add(tuple(sorted((a, b, int(c2**0.5)))))
ab = a * b
c2 -= ab
if c2 in sqrset:
tri60.add(tuple(sorted((a, b, int(c2**0.5)))))
c2 += 2 * ab
if c2 in sqrset:
tri120.add(tuple(sorted((a, b, int(c2**0.5)))))
return sorted(tri90), sorted(tri60), sorted(tri120)
#%%
if __name__ == '__main__':
print(f'Integer triangular triples for sides 1..{N}:')
for angle, triples in zip([90, 60, 120], method1(N)):
print(f' {angle:3}° has {len(triples)} solutions:\n {triples}')
_, t60, _ = method1(10_000)
notsame = sum(1 for a, b, c in t60 if a != b or b != c)
print('Extra credit:', notsame)
- Output:
Integer triangular triples for sides 1..13: 90° has 3 solutions: [(3, 4, 5), (5, 12, 13), (6, 8, 10)] 60° has 15 solutions: [(1, 1, 1), (2, 2, 2), (3, 3, 3), (3, 7, 8), (4, 4, 4), (5, 5, 5), (5, 7, 8), (6, 6, 6), (7, 7, 7), (8, 8, 8), (9, 9, 9), (10, 10, 10), (11, 11, 11), (12, 12, 12), (13, 13, 13)] 120° has 2 solutions: [(3, 5, 7), (7, 8, 13)] Extra credit: 18394
Dictionaries
A variant Python draft based on dictionaries. (Test functions are passed as parameters to the main function.)
from itertools import (starmap)
def f90(dct):
return lambda x2, ab, a, b: dct.get(x2, None)
def f60(dct):
return lambda x2, ab, a, b: dct.get(x2 - ab, None)
def f120(dct):
return lambda x2, ab, a, b: dct.get(x2 + ab, None)
def f60unequal(dct):
return lambda x2, ab, a, b: (
dct.get(x2 - ab, None) if a != b else None
)
# triangles :: Dict -> (Int -> Int -> Int -> Int -> Maybe Int)
# -> [String]
def triangles(f, n):
upto = enumFromTo(1)
xs = upto(n)
dctSquares = dict(zip(xs, [x**2 for x in xs]))
dctRoots = {v: k for k, v in dctSquares.items()}
fr = f(dctRoots)
dct = {}
for a in xs:
a2 = dctSquares[a]
for b in upto(a):
suma2b2 = a2 + dctSquares[b]
c = fr(suma2b2, a * b, a, b)
if (c is not None):
dct[str(sorted([a, b, c]))] = 1
return list(dct.keys())
def main():
print(
'Triangles of maximum side 13\n\n' +
unlines(
zipWith(
lambda f, n: (
lambda ks=triangles(f, 13): (
str(len(ks)) + ' solutions for ' +
str(n) + ' degrees:\n' +
unlines(ks) + '\n'
)
)()
)([f120, f90, f60])
([120, 90, 60])
) + '\n\n' +
'60 degrees - uneven triangles of maximum side 10000. Total:\n' +
str(len(triangles(f60unequal, 10000)))
)
# GENERIC --------------------------------------------------------------
# enumFromTo :: Int -> Int -> [Int]
def enumFromTo(m):
return lambda n: list(range(m, 1 + n))
# unlines :: [String] -> String
def unlines(xs):
return '\n'.join(xs)
# zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
def zipWith(f):
return lambda xs: lambda ys: (
list(starmap(f, zip(xs, ys)))
)
if __name__ == '__main__':
main()
- Output:
Triangles of maximum side 13 2 solutions for 120 degrees: [3, 5, 7] [7, 8, 13] 3 solutions for 90 degrees: [3, 4, 5] [6, 8, 10] [5, 12, 13] 15 solutions for 60 degrees: [1, 1, 1] [2, 2, 2] [3, 3, 3] [4, 4, 4] [5, 5, 5] [6, 6, 6] [7, 7, 7] [3, 7, 8] [5, 7, 8] [8, 8, 8] [9, 9, 9] [10, 10, 10] [11, 11, 11] [12, 12, 12] [13, 13, 13] 60 degrees - uneven triangles of maximum side 10000. Total: 18394
Quackery
[ dup 1
[ 2dup > while
+ 1 >>
2dup / again ]
drop nip ] is sqrt ( n --> n )
[ dup * ] is squared ( n --> n )
[ dup sqrt squared = ] is square ( n --> b )
[ say "90 degrees" cr
0 temp put
13 times
[ i^ 1+ dup times
[ i^ 1+ squared
over squared +
dup square
over sqrt 14 < and
iff
[ i^ 1+ echo sp
over echo sp
sqrt echo cr
1 temp tally ]
else drop ]
drop ]
temp take echo
say " solutions" cr cr ] is 90deg ( --> )
[ say "60 degrees" cr
0 temp put
13 times
[ i^ 1+ dup times
[ i^ 1+
2dup * dip
[ squared
over squared + ]
- dup square
over sqrt 14 < and
iff
[ i^ 1+ echo sp
over echo sp
sqrt echo cr
1 temp tally ]
else drop ]
drop ]
temp take echo
say " solutions" cr cr ] is 60deg ( --> )
[ say "120 degrees" cr
0 temp put
13 times
[ i^ 1+ dup times
[ i^ 1+
2dup * dip
[ squared
over squared + ]
+ dup square
over sqrt 14 < and
iff
[ i^ 1+ echo sp
over echo sp
sqrt echo cr
1 temp tally ]
else drop ]
drop ]
temp take echo
say " solutions" cr cr ] is 120deg ( --> )
90deg 60deg 120deg
- Output:
90 degrees 3 4 5 6 8 10 5 12 13 3 solutions found 60 degrees 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 3 8 7 5 8 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 15 solutions 120 degrees 3 5 7 7 8 13 2 solutions
R
This looks a bit messy, but it really pays off when you see how nicely the output prints.
inputs <- cbind(combn(1:13, 2), rbind(seq_len(13), seq_len(13)))
inputs <- cbind(A = inputs[1, ], B = inputs[2, ])[sort.list(inputs[1, ]),]
Pythagoras <- inputs[, "A"]^2 + inputs[, "B"]^2
AtimesB <- inputs[, "A"] * inputs[, "B"]
CValues <- sqrt(cbind("C (90º)" = Pythagoras,
"C (60º)" = Pythagoras - AtimesB,
"C (120º)" = Pythagoras + AtimesB))
CValues[!t(apply(CValues, MARGIN = 1, function(x) x %in% 1:13))] <- NA
output <- cbind(inputs, CValues)[!apply(CValues, MARGIN = 1, function(x) all(is.na(x))),]
rownames(output) <- paste0("Solution ", seq_len(nrow(output)), ":")
print(output, na.print = "")
cat("There are",
sum(!is.na(output[, 3])), "solutions in the 90º case,",
sum(!is.na(output[, 4])), "solutions in the 60º case, and",
sum(!is.na(output[, 5])), "solutions in the 120º case.")
- Output:
A B C (90º) C (60º) C (120º) Solution 1: 1 1 1 Solution 2: 2 2 2 Solution 3: 3 4 5 Solution 4: 3 5 7 Solution 5: 3 8 7 Solution 6: 3 3 3 Solution 7: 4 4 4 Solution 8: 5 8 7 Solution 9: 5 12 13 Solution 10: 5 5 5 Solution 11: 6 8 10 Solution 12: 6 6 6 Solution 13: 7 8 13 Solution 14: 7 7 7 Solution 15: 8 8 8 Solution 16: 9 9 9 Solution 17: 10 10 10 Solution 18: 11 11 11 Solution 19: 12 12 12 Solution 20: 13 13 13 There are 3 solutions in the 90º case, 15 solutions in the 60º case, and 2 solutions in the 120º case.
Raku
(formerly Perl 6)
In each routine, race is used to allow concurrent operations, requiring the use of the atomic increment operator, ⚛++, to safely update @triples, which must be declared fixed-sized, as an auto-resizing array is not thread-safe. At exit, default values in @triples are filtered out with the test !eqv Any
.
multi triples (60, $n) {
my %sq = (1..$n).map: { .² => $_ };
my atomicint $i = 0;
my @triples[2*$n];
(1..^$n).race(:8degree).map: -> $a {
for $a^..$n -> $b {
my $cos = $a * $a + $b * $b - $a * $b;
@triples[$i⚛++] = $a, %sq{$cos}, $b if %sq{$cos}:exists;
}
}
@triples.grep: so *;
}
multi triples (90, $n) {
my %sq = (1..$n).map: { .² => $_ };
my atomicint $i = 0;
my @triples[2*$n];
(1..^$n).race(:8degree).map: -> $a {
for $a^..$n -> $b {
my $cos = $a * $a + $b * $b;
@triples[$i⚛++] = $a, $b, %sq{$cos} and last if %sq{$cos}:exists;
}
}
@triples.grep: so *;
}
multi triples (120, $n) {
my %sq = (1..$n).map: { .² => $_ };
my atomicint $i = 0;
my @triples[2*$n];
(1..^$n).race(:8degree).map: -> $a {
for $a^..$n -> $b {
my $cos = $a * $a + $b * $b + $a * $b;
@triples[$i⚛++] = $a, $b, %sq{$cos} and last if %sq{$cos}:exists;
}
}
@triples.grep: so *;
}
use Sort::Naturally;
my $n = 13;
say "Integer triangular triples for sides 1..$n:";
for 120, 90, 60 -> $angle {
my @itt = triples($angle, $n);
if $angle == 60 { push @itt, "$_ $_ $_" for 1..$n }
printf "Angle %3d° has %2d solutions: %s\n", $angle, +@itt, @itt.sort(&naturally).join(', ');
}
my ($angle, $count) = 60, 10_000;
say "\nExtra credit:";
say "$angle° integer triples in the range 1..$count where the sides are not all the same length: ", +triples($angle, $count);
- Output:
Integer triangular triples for sides 1..13: Angle 120° has 2 solutions: 3 5 7, 7 8 13 Angle 90° has 3 solutions: 3 4 5, 5 12 13, 6 8 10 Angle 60° has 15 solutions: 1 1 1, 2 2 2, 3 3 3, 3 7 8, 4 4 4, 5 5 5, 5 7 8, 6 6 6, 7 7 7, 8 8 8, 9 9 9, 10 10 10, 11 11 11, 12 12 12, 13 13 13 Extra credit: 60° integer triples in the range 1..10000 where the sides are not all the same length: 18394
REXX
(Using some optimization and memoization.)
Instead of coding a general purpose subroutine (or function) to solve all of the
task's requirements, it was decided to
write three very similar do loops (triple nested) to provide the
answers for the three requirements.
Three arguments (from the command line) can be specified which indicates the
maximum length of the triangle sides
(the default is 13, as per the task's requirement) for each of the
three types of angles (60º, 90º, and 120º) for
the triangles. If the maximum length of the triangle's number of
sides is positive, it indicates that the triangle sides are
displayed, as well as a total number of triangles found.
If the maximum length of the triangle sides is negative, only
the number of triangles are
displayed (using the
absolute value of the negative number).
/*REXX pgm finds integer sided triangles that satisfy Law of cosines for 60º, 90º, 120º.*/
parse arg os1 os2 os3 os4 . /*obtain optional arguments from the CL*/
if os1=='' | os1=="," then os1= 13; s1=abs(os1) /*Not specified? Then use the default.*/
if os2=='' | os2=="," then os2= 13; s2=abs(os2) /* " " " " " " */
if os3=='' | os3=="," then os3= 13; s3=abs(os3) /* " " " " " " */
if os4=='' | os4=="," then os4= -0; s4=abs(os4) /* " " " " " " */
@.= /*@: array holds squares, max of sides*/
do j=1 for max(s1, s2, s3, s4); @.j= j * j /*use memoization.*/
end /*j*/
if s1>0 then call s1 /*handle the triangle case for 120º. */
if s2>0 then call s2 /*handle the triangle case for 90º. */
if s3>0 then call s3 /*handle the triangle case for 60º. */
if s4>0 then call s4 /*handle the case for unique sides. */
exit 0 /*stick a fork in it, we're all done. */
/*──────────────────────────────────────────────────────────────────────────────────────*/
commas: parse arg ?; do jc=length(?)-3 to 1 by -3; ?=insert(',', ?, jc); end; return ?
dAng: w= length(s); ang= ' 'd"º " uq' '; ss= s * s; @sol= " solutions found for"; return
foot: say right(commas(#) @sol ang "(sides up to" commas(arg(1) +0)')', 65); say; return
head: #= 0; parse arg d,uq,s; @= ','; call dAng; say center(ang, 65, '═'); return
show: #=#+1; arg p; if p>0 then say ' ('right(a,w)@ right(b,w)@ right(c,w)")"; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
s1: call head 120,,s1 /*────────── 120º: a² + b² + ab ≡ c² */
do a=1 for s1; ap1= a + 1
do b=ap1 for s1-ap1+1; x= @.a + @.b + a*b; if x>ss then iterate a
do c=b+1 for s1-b+1 until @.c>x
if x==@.c then do; call show os1; iterate b; end
end /*c*/
end /*b*/
end /*a*/
call foot s1; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
s2: call head 90,,s2 /*────────── 90º: a² + b² ≡ c² */
do a=1 for s2; ap1= a + 1
do b=ap1 for s2-ap1+1; x= @.a + @.b; if x>ss then iterate a
do c=b+1 for s2-b+2 until @.c>x
if x==@.c then do; call show os2; iterate b; end
end /*c*/
end /*b*/
end /*a*/
call foot s2; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
s3: call head 60,,s3 /*────────── 60º: a² + b² ─ ab ≡ c² */
do a=1 for s3; s3ma= s3 - a + 1
do b=a for s3ma; x= @.a + @.b - a*b; if x>ss then iterate a
do c=a for s3ma until @.c>x
if x==@.c then do; call show os3; iterate b; end
end /*c*/
end /*b*/
end /*a*/
call foot s2; return
/*──────────────────────────────────────────────────────────────────────────────────────*/
s4: call head 60, 'unique', os4 /*────────── 60º: a² + b² ─ ab ≡ c² */
do a=1 for s4; ap1= a + 1; s4map1= s4 - ap1 + 1
do b=ap1 for s4map1; x= @.a + @.b - a*b; if x>ss then iterate a
do c=ap1 for s4map1 until @.c>x
if x==@.c then do; call show os4; iterate b; end
end /*c*/
end /*b*/
end /*a*/
call foot s4; return
- output when using the default number of sides for the input: 13
═════════════════════════════ 120º ══════════════════════════════ ( 3, 5, 7) ( 7, 8, 13) 2 solutions found for 120º (sides up to 13) ══════════════════════════════ 90º ══════════════════════════════ ( 3, 4, 5) ( 5, 12, 13) ( 6, 8, 10) 3 solutions found for 90º (sides up to 13) ══════════════════════════════ 60º ══════════════════════════════ ( 1, 1, 1) ( 2, 2, 2) ( 3, 3, 3) ( 3, 8, 7) ( 4, 4, 4) ( 5, 5, 5) ( 5, 8, 7) ( 6, 6, 6) ( 7, 7, 7) ( 8, 8, 8) ( 9, 9, 9) (10, 10, 10) (11, 11, 11) (12, 12, 12) (13, 13, 13) 15 solutions found for 60º (sides up to 13)
- output when using the inputs of: 0 0 0 -10000
Note that the first three computations are bypassed because of the three zero (0) numbers, the negative ten thousand indicates to find all the triangles with sides up to 10,000, but not list the triangles, it just reports the number of solutions found.
══════════════════════════ 60º unique ══════════════════════════ 18,394 solutions found for 60º unique (sides up to 10,000)
RPL
≪ → formula max
≪ { }
1 max FOR a a max FOR b
formula EVAL √ RND
IF DUP max ≤ OVER FP NOT AND
THEN a b ROT 3 →LIST 1 →LIST +
ELSE DROP END
NEXT NEXT
≫ ≫ ‘TASK’ STO
'a^2+b^2' 13 TASK 'a^2+b^2-a*b' 13 TASK 'a^2+b^2+a*b' 13 TASK
- Output:
3: { { 4 3 5 } { 8 6 10 } { 12 5 13 } } 2: { { 1 1 1 } { 2 2 2 } { 3 3 3 } { 4 4 4 } { 5 5 5 } { 6 6 6 } { 7 7 7 } { 8 3 7 } { 8 5 7 } { 8 8 8 } { 9 9 9 } { 10 10 10 } { 11 11 11 } { 12 12 12 } { 13 13 13 } } 1: { { 5 3 7 } { 8 7 13 } }
Ruby
grouped = (1..13).to_a.repeated_permutation(3).group_by do |a,b,c|
sumaabb, ab = a*a + b*b, a*b
case c*c
when sumaabb then 90
when sumaabb - ab then 60
when sumaabb + ab then 120
end
end
grouped.delete(nil)
res = grouped.transform_values{|v| v.map(&:sort).uniq }
res.each do |k,v|
puts "For an angle of #{k} there are #{v.size} solutions:"
puts v.inspect, "\n"
end
- Output:
For an angle of 60 there are 15 solutions: [[1, 1, 1], [2, 2, 2], [3, 3, 3], [3, 7, 8], [4, 4, 4], [5, 5, 5], [5, 7, 8], [6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9], [10, 10, 10], [11, 11, 11], [12, 12, 12], [13, 13, 13]] For an angle of 90 there are 3 solutions: [[3, 4, 5], [5, 12, 13], [6, 8, 10]] For an angle of 120 there are 2 solutions: [[3, 5, 7], [7, 8, 13]]
Extra credit:
n = 10_000
ar = (1..n).to_a
squares = {}
ar.each{|i| squares[i*i] = true }
count = ar.combination(2).count{|a,b| squares.key?(a*a + b*b - a*b)}
puts "There are #{count} 60° triangles with unequal sides of max size #{n}."
- Output:
There are 18394 60° triangles with unequal sides of max size 10000.
Wren
var squares13 = {}
var squares10000 = {}
var initMaps = Fn.new {
for (i in 1..13) squares13[i*i] = i
for (i in 1..10000) squares10000[i*i] = i
}
var solve = Fn.new { |angle, maxLen, allowSame|
var solutions = []
for (a in 1..maxLen) {
for (b in a..maxLen) {
var lhs = a*a + b*b
if (angle != 90) {
if (angle == 60) {
lhs = lhs - a*b
} else if (angle == 120) {
lhs = lhs + a*b
} else {
Fiber.abort("Angle must be 60, 90 or 120 degrees")
}
}
if (maxLen == 13) {
var c = squares13[lhs]
if (c != null) {
if (allowSame || a != b || b != c) {
solutions.add([a, b, c])
}
}
} else if (maxLen == 10000) {
var c = squares10000[lhs]
if (c != null) {
if (allowSame || a != b || b != c) {
solutions.add([a, b, c])
}
}
} else {
Fiber.abort("Maximum length must be either 13 or 10000")
}
}
}
return solutions
}
initMaps.call()
System.write("For sides in the range [1, 13] ")
System.print("where they can all be of the same length:-\n")
var angles = [90, 60, 120]
var solutions = []
for (angle in angles) {
solutions = solve.call(angle, 13, true)
System.write(" For an angle of %(angle) degrees")
System.print(" there are %(solutions.count) solutions, namely:")
System.print(" %(solutions)")
System.print()
}
System.write("For sides in the range [1, 10000] ")
System.print("where they cannot ALL be of the same length:-\n")
solutions = solve.call(60, 10000, false)
System.write(" For an angle of 60 degrees")
System.print(" there are %(solutions.count) solutions.")
- Output:
For sides in the range [1, 13] where they can all be of the same length:- For an angle of 90 degrees there are 3 solutions, namely: [[3, 4, 5], [5, 12, 13], [6, 8, 10]] For an angle of 60 degrees there are 15 solutions, namely: [[1, 1, 1], [2, 2, 2], [3, 3, 3], [3, 8, 7], [4, 4, 4], [5, 5, 5], [5, 8, 7], [6, 6, 6], [7, 7, 7], [8, 8, 8], [9, 9, 9], [10, 10, 10], [11, 11, 11], [12, 12, 12], [13, 13, 13]] For an angle of 120 degrees there are 2 solutions, namely: [[3, 5, 7], [7, 8, 13]] For sides in the range [1, 10000] where they cannot ALL be of the same length:- For an angle of 60 degrees there are 18394 solutions.
XPL0
proc LawCos(Eqn);
int Eqn;
int Cnt, A, B, C;
proc Show;
[Cnt:= Cnt+1;
IntOut(0, A); ChOut(0, ^ );
IntOut(0, B); ChOut(0, ^ );
IntOut(0, C); CrLf(0);
];
[Cnt:= 0;
for A:= 1 to 13 do
for B:= 1 to A do
for C:= 1 to 13 do
case Eqn of
1: if A*A + B*B = C*C then Show;
2: if A*A + B*B - A*B = C*C then Show;
3: if A*A + B*B + A*B = C*C then Show
other [];
IntOut(0, Cnt); Text(0, " results^m^j");
];
proc ExtraCredit;
int Cnt, A, B, C, C2;
[Cnt:= 0;
for A:= 1 to 10_000 do
for B:= 1 to A-1 do
[C2:= A*A + B*B - A*B;
C:= sqrt(C2);
if C*C = C2 then Cnt:= Cnt+1;
];
Text(0, "Extra credit: ");
IntOut(0, Cnt);
CrLf(0);
];
int Case;
[for Case:= 1 to 3 do LawCos(Case);
ExtraCredit;
]
- Output:
4 3 5 8 6 10 12 5 13 3 results 1 1 1 2 2 2 3 3 3 4 4 4 5 5 5 6 6 6 7 7 7 8 3 7 8 5 7 8 8 8 9 9 9 10 10 10 11 11 11 12 12 12 13 13 13 15 results 5 3 7 8 7 13 2 results Extra credit: 18394
zkl
fcn tritri(N=13){
sqrset:=[0..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n });
tri90, tri60, tri120 := List(),List(),List();
foreach a,b in ([1..N],[1..a]){
aa,bb := a*a,b*b;
ab,c := a*b, aa + bb - ab; // 60*
if(sqrset.holds(c)){ tri60.append(abc(a,b,c)); continue; }
c=aa + bb; // 90*
if(sqrset.holds(c)){ tri90.append(abc(a,b,c)); continue; }
c=aa + bb + ab; // 120*
if(sqrset.holds(c)) tri120.append(abc(a,b,c));
}
List(tri60,tri90,tri120)
}
fcn abc(a,b,c){ List(a,b).sort().append(c.toFloat().sqrt().toInt()) }
fcn triToStr(tri){ // ((c,d,e),(a,b,c))-->"(a,b,c),(c,d,e)"
tri.sort(fcn(t1,t2){ t1[0]<t2[0] })
.apply("concat",",").apply("(%s)".fmt).concat(",")
}
N:=13;
println("Integer triangular triples for sides 1..%d:".fmt(N));
foreach angle, triples in (T(60,90,120).zip(tritri(N))){
println(" %3d\U00B0; has %d solutions:\n %s"
.fmt(angle,triples.len(),triToStr(triples)));
}
- Output:
Integer triangular triples for sides 1..13: 60° has 15 solutions: (1,1,1),(2,2,2),(3,8,7),(3,3,3),(4,4,4),(5,8,7),(5,5,5),(6,6,6),(7,7,7),(8,8,8),(9,9,9),(10,10,10),(11,11,11),(12,12,12),(13,13,13) 90° has 3 solutions: (3,4,5),(5,12,13),(6,8,10) 120° has 2 solutions: (3,5,7),(7,8,13)
Extra credit:
fcn tri60(N){ // special case 60*
sqrset:=[1..N].pump(Dictionary().add.fp1(True),fcn(n){ n*n });
n60:=0;
foreach a,b in ([1..N],[1..a]){
c:=a*a + b*b - a*b;
if(sqrset.holds(c) and a!=b!=c) n60+=1;
}
n60
}
N:=10_000;
println(("60\U00b0; triangle where side lengths are unique,\n"
" side lengths 1..%,d, there are %,d solutions.").fmt(N,tri60(N)));
- Output:
60° triangle where side lengths are unique, side lengths 1..10,000, there are 18,394 solutions.