A Pythagorean triplet is a set of three natural numbers, , for which,
For example, .
There exists exactly one Pythagorean triplet for which .
Find the product .
Mi first approach was to find and such that the square of both numbers summed up to the squared. I achieved it by providing the desired value of to find the triplet corresponding to that value.
Because , it is possible to allow a to start at , as a consequence , thus making the minimum value c can take equal to .
;; a: adjacent side
;; b: opposite side
;; c: hypotenuse
(defun find-triplet-aux (a b c &optional (a2 (expt a 2)) (b2 (expt b 2)) (c2 (expt c 2)))
;(format t "~a~%" (list a b))
(cond ((equalp c2 (+ a2 b2)) (list a b c))
((< b c) (find-triplet-aux a (1+ b) c a2 (expt (1+ b) 2)))
((< a (- c 2)) (find-triplet-aux (1+ a) (+ 2 a) c (expt (1+ a) 2) (expt (+ 2 a) 2)))
(t nil)))
(defun find-triplet (c)
(find-triplet-aux 1 2 c))
Some triplets can be consulted in the Pythagorean triplets entry at Wikipedia, such as:
find-triplet
is correct but not complete. I mention this because if find-triplet
is run with we get (13 84 85)
which does appear in the previous shown table of triplets, but (36 77 85)
can't be obtained with the current implementation of find-triplet
. > (find-triplet 85)
(13 84 85)
Another I figured out to define find-triplet-aux
is by iterating first through all available number of instead of , where will be allowed to take values within the range for each value of .
(defun find-triplet-aux (a b c &optional (a2 (expt a 2)) (b2 (expt b 2)) (c2 (expt c 2)))
;(format t "~a~%" (list a b))
(cond ((equalp c2 (+ a2 b2)) (list a b c))
((< a b) (find-triplet-aux (1+ a) b c (expt (1+ a) 2)))
((< b c) (find-triplet-aux 1 (1+ b) c 1 (expt (1+ b) 2)))
(t nil)))
The values of and can be seen on both implementations of find-triplet-aux
by uncommenting the line with the format
function. Nevertheless, this new implementation of find-triplet-aux
doesn't solve the problem either because it only returns one single triplet. Although. if called with , it doesn't returns (36, 77, 85)
but instead (51 68 85)
.
> (find-triplet 85)
(51 68 85)
Meaning there is not a unique set of values that fulfill equation (1).
Before reaching this realization, I already implemented a function that started with until an upper limit in order to find such that they sum up to said upper limit, the problem states that .
The idea is simple, call find-triplet
with , if the resulting triplet sums up to then return the triplet, else increment . Another case can occur, the one where no triplet is found for the current value.
;; sum: equals to the expected sum of a + b + c
(defun triplet-of-sum (sum &optional (c 3))
(let ((triplet (find-triplet c)))
;(format t "~a ~a ~%" c triplet)
(cond ((equalp nil triplet)
(tmp sum (1+ c)))
((equalp sum (sum/lst triplet))
triplet)
((< c sum)
(tmp sum (1+ c)))
(t nil))))
I did not find the correct result of this problem following this approach, triplet-of-sum
is not able to even find the triplet corresponding to . Although, it does find the correct triplet for other values of that I tested. I even tried using both implementations of find-triplet-aux
but with no avail.
The function sum/lst
is an auxiliary function to sum the elements of a list because it is not implemented in SBCL.
(defun sum/lst (lst)
(reduce #'(lambda (acc x) (+ acc x)) lst :initial-value 0))
This is where I decided to start again.
I searched for some ideas on the internet, and came up with a simple solution.
(defun triplet-of-n (n &optional (a 1) (b 2) (blim (1- (floor n 2))))
(let ((c (- n a b)))
;(format t "~a~%" (list a b c))
(cond ((is-triplet? a b c) (list a b c))
((< a (1- b)) (triplet-of-n n (1+ a) b))
((< b blim) (triplet-of-n n 1 (1+ b))))))
It is an iterative solution where the value of is computed as . Instead of trying to find the triplet that sums up to , it is used to compute the value that should have. Then, it is asked if the current is indeed a Pythagorean triplet with is-triplet?
.
(defun is-triplet? (a b c)
(when (equalp (+ (expt a 2) (expt b 2)) (expt c 2)) t))
If is a Pythagorean triplet, the product is easily obtained with triplet-prod
.
(defun triplet-prod (triplet)
(when triplet (prod/lst triplet)))
These functions use when
, meaning that only in the case that the'll only evaluate if the condition is not nil
. Lastly, the function prod/lst
is based on reduce
just as sum/lst
.
(defun prod/lst (lst)
(reduce #'(lambda (acc x) (* acc x)) lst :initial-value 1))
Some examples of the triplet related functions are shown below.
> (is-triplet? 1 1 2)
NIL
> (is-triplet? 3 4 5)
T
> (triplet-of-n 12)
(3 4 5)
> (triplet-of-n 1000)
(200 375 425)
> (triplet-prod (triplet-of-n 12))
60
> (triplet-prod (triplet-of-n 1000))
31875000
Lastly, I didn't explained why function triplet-of-n
has an optional argument that sets the upper limit of set to , so allow me to tackle this point. First, we have to keep in mind that , it is easy to deduce that the minimum value that can take is . Because of this, the smallest value is . Similarly, the smallest value can take is .
What about the maximum values? I only assumed the scenario where to find an answer to this question. Assuming this is the case, then
The constraint doesn't apply here, in fact, it is allowed that . If both are equal, , that means that . Therefore,
Now we have the limits of interest, the lower limit of and the lower and upper limits of . I didn't tried to find an upper limit for because I didn't needed it for my implementation. Likewise, I didn't use the upper limit of in the implementation of triplet-of-n
, only to find the upper limit of .
triplet-of-n
starts at , it doesn't adds 1 unit to because that violates the condition . So, it adds 1 to and sets , making the next pair of values are , and the second pair of values equal to . This process continues until a triplet is found or .
I didn't mention this before but there can exists a set of values for any in the natural numbers that is also a Pythagorean triplet. This can be another way of finding the result, but I didn't explore it further.
~ eof