Skip to content

Commit

Permalink
Add pythagorean-triplet exercise (#419)
Browse files Browse the repository at this point in the history
  • Loading branch information
keiravillekode authored Jan 18, 2025
1 parent 14b3dcc commit ff72175
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 0 deletions.
8 changes: 8 additions & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,14 @@
],
"difficulty": 1
},
{
"slug": "pythagorean-triplet",
"name": "Pythagorean Triplet",
"uuid": "393d316c-f20a-4d86-859b-8bf0e920fb55",
"practices": [],
"prerequisites": [],
"difficulty": 5
},
{
"slug": "grains",
"name": "Grains",
Expand Down
23 changes: 23 additions & 0 deletions exercises/practice/pythagorean-triplet/.docs/instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Instructions

A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for which,

```text
a² + b² = c²
```

and such that,

```text
a < b < c
```

For example,

```text
3² + 4² = 5².
```

Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`.

For example, with N = 1000, there is exactly one Pythagorean triplet for which `a + b + c = 1000`: `{200, 375, 425}`.
19 changes: 19 additions & 0 deletions exercises/practice/pythagorean-triplet/.meta/config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"authors": [
"keiravillekode"
],
"files": {
"solution": [
"pythagorean_triplet.zig"
],
"test": [
"test_pythagorean_triplet.zig"
],
"example": [
".meta/example.zig"
]
},
"blurb": "There exists exactly one Pythagorean triplet for which a + b + c = 1000. Find the triplet.",
"source": "Problem 9 at Project Euler",
"source_url": "https://projecteuler.net/problem=9"
}
51 changes: 51 additions & 0 deletions exercises/practice/pythagorean-triplet/.meta/example.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
const std = @import("std");
const mem = std.mem;

pub const Triplet = struct {
a: usize,
b: usize,
c: usize,

pub fn init(a: usize, b: usize, c: usize) Triplet {
return .{ .a = a, .b = b, .c = c };
}
};

// For every Pythagorean triplet with total a + b + c = n,
// a² + b² = c²
// <=> a² + b² = (n - a - b)², substituting c
// <=> 0 = n² - 2*n*a - 2*n*b + 2*a*b
// <=> (2*n - 2*a) b = (n² - 2*n*a)
// <=> b = (n² - 2*n*a) / (2*n - 2*a)
//
// The denominator is never 0, as perimeter exceeds a side length.
//
pub fn tripletsWithSum(allocator: mem.Allocator, n: usize) mem.Allocator.Error![]Triplet {
var buffer = try allocator.alloc(Triplet, n / 3);
if (buffer.len == 0) {
return buffer;
}

defer allocator.free(buffer);

var a: usize = 1;
var numTriples: usize = 0;
while (true) {
const numerator = n * (n - 2 * a);
const denominator = 2 * (n - a);
const b = numerator / denominator;
if (b <= a) {
break;
}
if (numerator % denominator == 0) {
buffer[numTriples] = Triplet.init(a, b, n - a - b);
numTriples += 1;
}

a += 1;
}

const result = try allocator.alloc(Triplet, numTriples);
@memcpy(result, buffer[0..numTriples]);
return result;
}
31 changes: 31 additions & 0 deletions exercises/practice/pythagorean-triplet/.meta/tests.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# This is an auto-generated file.
#
# Regenerating this file via `configlet sync` will:
# - Recreate every `description` key/value pair
# - Recreate every `reimplements` key/value pair, where they exist in problem-specifications
# - Remove any `include = true` key/value pair (an omitted `include` key implies inclusion)
# - Preserve any other key/value pair
#
# As user-added comments (using the # character) will be removed when this file
# is regenerated, comments can be added via a `comment` key.

[a19de65d-35b8-4480-b1af-371d9541e706]
description = "triplets whose sum is 12"

[48b21332-0a3d-43b2-9a52-90b2a6e5c9f5]
description = "triplets whose sum is 108"

[dffc1266-418e-4daa-81af-54c3e95c3bb5]
description = "triplets whose sum is 1000"

[5f86a2d4-6383-4cce-93a5-e4489e79b186]
description = "no matching triplets for 1001"

[bf17ba80-1596-409a-bb13-343bdb3b2904]
description = "returns all matching triplets"

[9d8fb5d5-6c6f-42df-9f95-d3165963ac57]
description = "several matching triplets"

[f5be5734-8aa0-4bd1-99a2-02adcc4402b4]
description = "triplets for large number"
19 changes: 19 additions & 0 deletions exercises/practice/pythagorean-triplet/pythagorean_triplet.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
const std = @import("std");
const mem = std.mem;

pub const Triplet = struct {
// This struct, as well as its fields and init method, needs to be implemented.

pub fn init(a: usize, b: usize, c: usize) Triplet {
_ = a;
_ = b;
_ = c;
@compileError("please implement the init method");
}
};

pub fn tripletsWithSum(allocator: mem.Allocator, n: usize) mem.Allocator.Error![]Triplet {
_ = allocator;
_ = n;
@compileError("please implement the tripletsWithSum function");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
const std = @import("std");
const testing = std.testing;

const pythagorean_triplet = @import("pythagorean_triplet.zig");
const Triplet = pythagorean_triplet.Triplet;
const tripletsWithSum = pythagorean_triplet.tripletsWithSum;

test "triplets whose sum is 12" {
const expected: [1]Triplet = .{
Triplet.init(3, 4, 5),
};
const actual = try tripletsWithSum(testing.allocator, 12);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "triplets whose sum is 108" {
const expected: [1]Triplet = .{
Triplet.init(27, 36, 45),
};
const actual = try tripletsWithSum(testing.allocator, 108);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "triplets whose sum is 1000" {
const expected: [1]Triplet = .{
Triplet.init(200, 375, 425),
};
const actual = try tripletsWithSum(testing.allocator, 1000);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "no matching triplets for 1001" {
const expected: [0]Triplet = .{};
const actual = try tripletsWithSum(testing.allocator, 1001);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "returns all matching triplets" {
const expected: [2]Triplet = .{
Triplet.init(9, 40, 41),
Triplet.init(15, 36, 39),
};
const actual = try tripletsWithSum(testing.allocator, 90);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "several matching triplets" {
const expected: [8]Triplet = .{
Triplet.init(40, 399, 401),
Triplet.init(56, 390, 394),
Triplet.init(105, 360, 375),
Triplet.init(120, 350, 370),
Triplet.init(140, 336, 364),
Triplet.init(168, 315, 357),
Triplet.init(210, 280, 350),
Triplet.init(240, 252, 348),
};
const actual = try tripletsWithSum(testing.allocator, 840);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

test "triplets for large number" {
const expected: [5]Triplet = .{
Triplet.init(1200, 14375, 14425),
Triplet.init(1875, 14000, 14125),
Triplet.init(5000, 12000, 13000),
Triplet.init(6000, 11250, 12750),
Triplet.init(7500, 10000, 12500),
};
const actual = try tripletsWithSum(testing.allocator, 30000);
defer testing.allocator.free(actual);
try testing.expectEqualSlices(Triplet, &expected, actual);
}

0 comments on commit ff72175

Please sign in to comment.