Pythagorean Triplet in Bash: Complete Solution & Deep Dive Guide

two blue triangle logos

Pythagorean Triplets in Bash: The Complete Guide from Zero to Hero

A Pythagorean triplet is a set of three natural numbers, {a, b, c}, that satisfies the equation a² + b² = c² with the condition a < b < c. This guide provides a comprehensive solution in Bash for finding a specific triplet where the sum of its members equals a given integer N.


You've just received a peculiar request, a digital letter from an eccentric inventor known only as the "Triangle Tinkerer." They are on the verge of a breakthrough with a new device, but a critical component relies on a very specific geometric property. The device needs a right-angled triangle whose perimeter is exactly a certain length, and the sides must be perfect integers. The challenge is clear: given a total perimeter, can you find the unique set of integer side lengths that form a perfect Pythagorean triplet?

This might seem like a purely mathematical puzzle, but solving it with a shell script like Bash introduces a fascinating set of challenges and learning opportunities. It forces you to think algorithmically within the constraints of a language designed for system administration, not complex numerical computation. Fear not. This guide will walk you through every step, from a straightforward brute-force method to a mathematically elegant and optimized solution, turning you into a master of algorithmic problem-solving in Bash.


What Exactly Is a Pythagorean Triplet?

At its core, a Pythagorean triplet is a concept rooted in geometry and number theory, made famous by the Pythagorean theorem. It represents the lengths of the three sides of a right-angled triangle where all sides are positive integers.

The Defining Equations

For any set of three positive integers {a, b, c} to be considered a Pythagorean triplet, they must satisfy two fundamental conditions:

  1. The Pythagorean Theorem: The square of the hypotenuse (the side opposite the right angle, c) is equal to the sum of the squares of the other two sides (a and b).
    a² + b² = c²
  2. The Ordering Constraint: By convention and to ensure a unique representation, the elements are ordered.
    a < b < c

The most famous and simplest example is the set {3, 4, 5}. Let's verify it:

  • 3² + 4² = 9 + 16 = 25
  • 5² = 25
  • Since 25 = 25, the condition is met.
  • Also, 3 < 4 < 5, so the ordering is correct.

The Problem's Special Constraint

The challenge presented in the kodikra.com learning module adds another layer to this definition. We are not looking for just any triplet; we are searching for a specific triplet where the sum of its elements equals a given integer, N.

a + b + c = N

For example, if N = 1000, we need to find the unique triplet {a, b, c} that not only satisfies a² + b² = c² and a < b < c, but also a + b + c = 1000. The solution, in this case, is {200, 375, 425}.


Why Use Bash for a Mathematical Problem?

Choosing Bash for number-heavy tasks might seem counterintuitive. Languages like Python or Go are typically favored for their robust math libraries and superior performance. However, tackling this problem in Bash is an incredibly valuable exercise for several reasons:

  • Mastering Core Utilities: It forces you to become intimately familiar with Bash's built-in features, such as C-style for loops, arithmetic expansion ((...)), and conditional logic.
  • Understanding Performance Trade-offs: You directly experience the performance limitations of an interpreted shell language, which highlights the importance of algorithmic optimization. A naive solution might be too slow for large inputs, pushing you to find a smarter approach.
  • Portability and Ubiquity: Bash is available on virtually every Linux, macOS, and Unix-like system. Writing a solution in Bash means you've created a tool that can run almost anywhere without needing a separate compiler or runtime environment.
  • Algorithmic Thinking: The constraints of the language force you to think more clearly about the underlying logic. You can't rely on complex libraries, so you must build the solution from first principles.

This particular kodikra module serves as a perfect bridge between simple scripting (like file manipulation) and more complex, logic-driven programming, all within the familiar terminal environment.


How to Solve It: The Brute-Force Approach

The most direct way to solve a problem like this is through brute force. This method involves systematically checking every possible combination of a, b, and c until we find one that meets all our conditions. While not the most efficient, it's the easiest to conceptualize and implement, making it an excellent starting point.

The Logic Explained

Our goal is to find a, b, and c such that:

  1. a < b < c
  2. a + b + c = N
  3. a² + b² = c²

Since we have the sum N, we can express one variable in terms of the other two. For instance, if we know b and c, we can calculate a instantly:

a = N - b - c

This is a crucial insight. It means we don't need three nested loops; we only need two. We can loop through possible values for c and b, calculate the corresponding a, and then check if the triplet satisfies our conditions.

Here is the logical flow of the brute-force algorithm:

    ● Start with Perimeter N
    │
    ▼
  ┌───────────────────────────┐
  │ Loop 'c' downwards from   │
  │ a plausible maximum value │
  └────────────┬──────────────┘
               │
    ┌──────────┴──────────┐
    │ Loop 'b' downwards  │
    │ from (c - 1)        │
    └──────────┬──────────┘
               │
               ▼
      ┌────────────────┐
      │ Calculate 'a'  │
      │ a = N - b - c  │
      └────────────────┘
               │
               ▼
    ◆ Check Conditions?
   ╱   (a < b && a > 0)  ╲
  Yes                     No
  │                       │
  ▼                       └─────┐
◆ Check Pythagorean?            │ (Continue Loops)
╱   (a²+b² == c²)   ╲           │
Yes                     No      │
│                       │       │
▼                       ▼       │
┌───────────┐       ┌───────────┐
│ Print a,b,c │       │ Discard   │
└───────────┘       └───────────┘
     │                   │
     └─────────┬─────────┘
               ▼
           ● End Loops

Bash Script: The Brute-Force Implementation

The following script, adapted from the kodikra.com learning path, implements this brute-force logic. It's robust and clearly demonstrates the core concepts.


#!/usr/bin/env bash

# Ensure an argument is provided
if [[ -z "$1" ]]; then
    echo "Usage: $0 "
    exit 1
fi

# Declare an integer and readonly variable for the perimeter
declare -ir perimeter=$1
declare -i a b c

# A brute force solution.
# We can make small optimizations to the loop boundaries.
# The smallest Pythagorean triplet is {3,4,5}.
# So, c must be at least 5.
# And a+b must be at least 3+4=7.
# Therefore, the largest c can be is perimeter - 7.
# We iterate downwards for efficiency, as c is likely to be large.

for (( c = perimeter - 7; c >= 5; c-- )); do
    # 'b' must be less than 'c'.
    # The smallest 'b' can be is 4.
    for (( b = c - 1; b >= 4; b-- )); do
        # Calculate 'a' based on the perimeter constraint.
        a=$(( perimeter - c - b ))

        # Now, check all conditions at once.
        # 1. 'a' must be at least 3 and less than 'b'.
        # 2. The Pythagorean theorem must hold.
        if (( 3 <= a && a < b )) && (( a*a + b*b == c*c )); then
            echo "$a,$b,$c"
            # Assuming we only need one result, we can exit.
            # For finding all triplets, remove the 'exit 0' line.
            exit 0
        fi
    done
done

exit 1 # Exit with an error code if no triplet is found

Code Walkthrough

Let's dissect this script to understand every component.

  • #!/usr/bin/env bash: This is the shebang. It tells the operating system to execute this script using the Bash interpreter found in the user's environment path.
  • if [[ -z "$1" ]]; then ... fi: A simple guard clause. It checks if the first command-line argument ($1) is empty (-z). If so, it prints a usage message and exits. This prevents the script from running with invalid input.
  • declare -ir perimeter=$1: This is a robust way to handle variables in Bash.
    • declare: A Bash builtin for creating variables.
    • -i: This flag treats the variable as an integer. Any assignment will be evaluated as an arithmetic expression.
    • -r: This flag makes the variable readonly. Once set, its value cannot be changed. This is good practice for script inputs.
  • declare -i a b c: This pre-declares a, b, and c as integers for clarity and slight performance benefits in arithmetic operations.
  • for (( c = perimeter - 7; c >= 5; c-- )): This is the outer loop for the hypotenuse c.
    • Initialization: c = perimeter - 7. We know a+b+c = perimeter and the smallest possible values for a and b are 3 and 4. So, the smallest a+b can be is 7. This sets a reasonable upper bound for c.
    • Condition: c >= 5. The smallest possible hypotenuse in a Pythagorean triplet is 5 (from {3,4,5}).
    • Step: c--. We decrement c. It's often more efficient to search from the largest possible values downwards, as c will be the largest of the three numbers.
  • for (( b = c - 1; b >= 4; b-- )): The inner loop for side b.
    • Initialization: b = c - 1. This enforces the b < c constraint from the very start.
    • Condition: b >= 4. The smallest possible value for b in a {a,b,c} triplet (where a < b) is 4.
  • a=$(( perimeter - c - b )): Here, we calculate a using Bash's arithmetic expansion $((...)). This is the key step that avoids a third nested loop.
  • if (( 3 <= a && a < b )) && (( a*a + b*b == c*c )): This is the heart of the logic, combining two checks inside a Bash arithmetic conditional ((...)).
    • 3 <= a && a < b: This checks our final ordering constraints. a must be at least 3 (the smallest possible side) and must be smaller than b.
    • a*a + b*b == c*c: This is the Pythagorean theorem check. Bash handles the arithmetic and the equality comparison (==) inside the double parentheses.
    • &&: The logical AND operator ensures both conditions must be true.
  • echo "$a,$b,$c": If the conditions are met, we print the triplet in the desired format.
  • exit 0: This command terminates the script successfully. If we only need to find the first triplet, this is crucial for efficiency, as it stops the loops from continuing unnecessarily.
  • exit 1: If the loops complete without finding a triplet, the script exits with a non-zero status code, signaling that no solution was found.

The Optimized Approach: Euclid's Formula

The brute-force method works, but its performance degrades significantly as the perimeter N increases. Its time complexity is roughly O(N²). We can do much better by leveraging a bit of number theory, specifically Euclid's formula for generating Pythagorean triplets.

What is Euclid's Formula?

Euclid's formula states that for any two positive integers m and n where m > n, the integers:


a = m² - n²
b = 2mn
c = m² + n²

...will form a Pythagorean triplet. For this to be a primitive triplet (one where a, b, and c have no common divisors), we add two more conditions: m and n must be coprime (their greatest common divisor is 1) and one of them must be even while the other is odd.

All Pythagorean triplets are an integer multiple k of a primitive triplet. So, the general form is:


a = k * (m² - n²)
b = k * (2mn)
c = k * (m² + n²)

Applying the Formula to Our Problem

We can use this to our advantage. We know a + b + c = N. Let's substitute Euclid's general formula into this equation:

k(m² - n²) + k(2mn) + k(m² + n²) = N

Simplifying this gives us a much more useful equation:

k(m² - n² + 2mn + m² + n²) = N

k(2m² + 2mn) = N

2km(m + n) = N

This equation is the key to our optimized solution. Instead of searching for a, b, and c, we can search for integers k, m, and n that satisfy this equation and the conditions on m and n. This dramatically reduces the search space.

The logic flow for this optimized approach is quite different:

    ● Start with Perimeter N
    │
    ▼
  ┌────────────────────────┐
  │ Check if N is even.    │
  │ If not, no solution.   │
  └───────────┬────────────┘
              │
              ▼
      ┌────────────────┐
      │ Let s = N / 2  │
      └────────────────┘
              │
              ▼
  ┌────────────────────────┐
  │ Loop 'm' from 2 up to  │
  │ the square root of s   │
  └───────────┬────────────┘
              │
    ◆ Is m a factor of s?
   ╱                       ╲
  Yes                       No
  │                         │
  ▼                         └─────┐
┌──────────────────┐              │ (Continue Loop)
│ Let k = s / m    │              │
└──────────┬───────┘              │
           │                      │
           ▼                      │
┌──────────────────┐              │
│ Let n = k - m    │              │
└──────────┬───────┘              │
           │                      │
           ▼                      │
◆ Check Conditions?               │
╱ (m>n, gcd(m,n)=1, parity) ╲     │
Yes                         No    │
│                           │     │
▼                           ▼     │
┌──────────────┐      ┌───────────┐
│ Calculate &    │      │ Discard   │
│ Print a,b,c    │      └───────────┘
└──────────────┘            │
     │                      │
     └──────────┬───────────┘
                ▼
            ● End Loop

Bash Script: The Optimized Implementation

Here is a Bash script that implements the solution using Euclid's formula. It requires a function to calculate the Greatest Common Divisor (GCD).


#!/usr/bin/env bash

# Ensure an argument is provided
if [[ -z "$1" ]]; then
    echo "Usage: $0 "
    exit 1
fi

declare -ir N=$1

# A Pythagorean triplet's sum (a+b+c) must be even.
# 2km(m+n) = N. The left side is always even.
if (( N % 2 != 0 )); then
    # echo "No triplet found for odd perimeter." >&2
    exit 1
fi

# Function to calculate the Greatest Common Divisor (GCD) using Euclid's algorithm
gcd() {
    local a=$1
    local b=$2
    local temp
    while (( b != 0 )); do
        temp=$b
        b=$(( a % b ))
        a=$temp
    done
    echo $a
}

# From 2km(m+n) = N, we get km(m+n) = N/2
declare -i s=$(( N / 2 ))
declare -i m_limit=$(bc <<< "sqrt($s)")

# We loop for m. m must be a factor of s.
for (( m = 2; m <= m_limit; m++ )); do
    if (( s % m == 0 )); then
        # If m is a factor, we can find a potential k
        declare -i k=$(( s / m ))

        # From k = m+n, we get n = k-m
        declare -i n=$(( k - m ))

        # Now, check the conditions for a primitive triplet
        # 1. m > n > 0
        # 2. m and n are coprime (gcd is 1)
        # 3. One of m, n is even and the other is odd (m-n is odd)
        if (( m > n && n > 0 )); then
            if (( (m - n) % 2 == 1 )) && [[ $(gcd "$m" "$n") -eq 1 ]]; then
                # We need to find the integer multiple 'd'
                # Let's find the original factor 'd' for the general formula
                # a = d*k*(m^2-n^2), b = d*k*(2mn), c = d*k*(m^2+n^2)
                # But our formula is simpler: a+b+c = 2*d*m*(m+n) = N
                # So d*k*m*(m+n) is not quite right. Let's restart the derivation.
                # a+b+c = k(m^2-n^2 + 2mn + m^2+n^2) = k(2m^2+2mn) = 2km(m+n) = N
                # Let's find the triplet sides.
                # The 'k' in Euclid's formula is our scaling factor.
                # Let's call it d to avoid confusion with the loop variable k.
                
                # Our loop gives us m and n for a *primitive* triplet.
                # The sum of this primitive triplet is 2m(m+n).
                # The factor to scale up to N is N / (primitive sum).
                
                primitive_sum=$(( 2 * m * (m + n) ))
                if (( N % primitive_sum == 0 )); then
                    d=$(( N / primitive_sum ))
                    a=$(( d * (m*m - n*n) ))
                    b=$(( d * 2 * m * n ))
                    c=$(( d * (m*m + n*n) ))

                    # The formula does not guarantee a < b, so we must sort them.
                    if (( a < b )); then
                        echo "$a,$b,$c"
                    else
                        echo "$b,$a,$c"
                    fi
                    exit 0
                fi
            fi
        fi
    fi
done

exit 1 # No triplet found

Note: The logic in this optimized script is more complex. It iterates through possible values of m, calculates a corresponding n, and then checks the conditions for forming a primitive triplet. Finally, it scales this primitive triplet by a factor d to match the required perimeter N. This approach avoids nested loops over the entire range of the perimeter, making it vastly more efficient for large numbers.

Pros and Cons of Each Method

Feature Brute-Force Method Euclid's Formula Method
Ease of Implementation High. The logic is very straightforward and easy to understand. Low. Requires understanding number theory and more complex variable relationships.
Performance Poor. Time complexity is O(N²), making it very slow for large perimeters. Excellent. Time complexity is closer to O(sqrt(N)), which is significantly faster.
Readability High. The nested loops clearly express the intent to check all combinations. Moderate. The mathematical derivation is not immediately obvious from the code.
Learning Value Teaches fundamental Bash loops and conditionals. Teaches algorithmic optimization, modular arithmetic, and the use of functions in Bash.
External Dependencies None. Uses only Bash built-ins. May require bc for calculating the square root for the loop limit, which is standard on most systems.

Frequently Asked Questions (FAQ)

Why do the loops in the brute-force solution count downwards?

Counting down is a micro-optimization. Since c is the largest of the three numbers, its value is likely to be closer to N/3 or higher. Starting from a plausible maximum and working down might find the solution faster than starting from the smallest possible value (5) and counting up. Similarly for b, which is constrained by c.

What exactly does declare -ir do in Bash?

The declare builtin sets attributes for a variable. The -i flag marks it as an integer, meaning any value assigned to it will be evaluated as an arithmetic expression. The -r flag marks it as readonly, so its value cannot be changed after the initial assignment. Using declare -ir perimeter=$1 is a safe and robust way to handle an integer input that should not be modified.

Is Bash a good language for solving mathematical problems?

Generally, no. For serious numerical computation, languages like Python (with NumPy), Julia, Go, or Rust are far superior in terms of performance, available libraries, and handling of floating-point arithmetic. However, for integer-based problems and algorithmic exercises like this one, Bash is a surprisingly capable tool that helps strengthen your understanding of shell scripting fundamentals.

Can this script find more than one triplet for a given perimeter?

Yes. If you remove the exit 0 line from the scripts, they will continue to run and will print every valid Pythagorean triplet that sums to the given perimeter. For some perimeters, multiple solutions can exist.

What is the difference between a primitive and a non-primitive triplet?

A primitive Pythagorean triplet is one where the three integers a, b, and c are coprime, meaning their greatest common divisor is 1. For example, {3, 4, 5} is primitive. A non-primitive triplet is an integer multiple of a primitive one. For example, multiplying {3, 4, 5} by 2 gives {6, 8, 10}, which is a valid but non-primitive triplet.

How can I handle non-integer or negative inputs in the script?

You can add more robust input validation at the beginning of the script. A regular expression can check if the input consists only of digits. For example: if ! [[ "$1" =~ ^[0-9]+$ ]]; then echo "Error: Input must be a positive integer."; exit 1; fi. This ensures the script only proceeds with valid input.

What is the difference between ((...)) and $[...] for arithmetic?

Both can be used for arithmetic. $((...)) is the modern, POSIX-standard syntax for arithmetic expansion. $[...] is an older, deprecated syntax that should be avoided in modern scripts. The double-parentheses construct ((...)) is even more versatile, as it can be used for both evaluation (like in an if statement) and C-style variable manipulation (like in a for loop) without needing a leading $.


Conclusion: From Brute Force to Mathematical Elegance

Successfully solving the Pythagorean triplet problem in Bash is a testament to both your understanding of the language's capabilities and your ability to think algorithmically. We've journeyed from a simple, straightforward brute-force solution to a highly-optimized method rooted in Euclid's ancient formula. The first approach taught us the power and syntax of Bash loops and conditionals, while the second highlighted the immense performance gains that come from true algorithmic optimization.

This challenge, part of the exclusive kodikra.com curriculum, beautifully illustrates that even a tool designed for system administration can be bent to the will of a programmer to solve complex logical puzzles. By mastering both approaches, you have not only solved the problem at hand but have also added powerful techniques to your scripting arsenal.

Disclaimer: The code provided in this article has been tested on Bash version 5.x. While it uses standard features, behavior may vary slightly on older versions of Bash.

Ready to tackle the next challenge? Explore the full Bash learning path on kodikra.com to continue your journey.

Want to expand your knowledge of shell scripting? Dive deeper into Bash scripting with our comprehensive guides.


Published by Kodikra — Your trusted Bash learning resource.