Compare commits

...

2 Commits

2 changed files with 31 additions and 44 deletions

57
main.cc
View File

@@ -1,6 +1,6 @@
/** \file main.cc /** \file main.cc
* \author Matthew Gretton-Dann * \author Matthew Gretton-Dann
* \brief Solves the partirige problem for user specified size. * \brief Solves the Partridge problem for user specified size.
* *
* Copyright 2025, Matthew-Gretton-Dann * Copyright 2025, Matthew-Gretton-Dann
* SPDX: Apache-2.0 * SPDX: Apache-2.0
@@ -11,13 +11,12 @@
#include <string_view> #include <string_view>
#include <vector> #include <vector>
#include <iostream> #include <iostream>
#include <set>
using size_t = std::size_t;
namespace { namespace {
using size_t = std::uint64_t;
/** (x, y) pair storing a position. */ /** (x, y) pair storing a position. */
using Pos = std::size_t; using Pos = size_t;
/** A square - consisting of position of closest corner to origin, and side-length. /** A square - consisting of position of closest corner to origin, and side-length.
*/ */
@@ -40,10 +39,10 @@ namespace {
~Square() noexcept = default; ~Square() noexcept = default;
/** Get x co-ordinate of closest corner to origin. */ /** Get x co-ordinate of closest corner to origin. */
auto pos() const noexcept -> Pos { return pos_; } [[nodiscard]] auto pos() const noexcept -> Pos { return pos_; }
/** Get side length. */ /** Get side length. */
auto length() const noexcept -> size_t { return length_; } [[nodiscard]] auto length() const noexcept -> size_t { return length_; }
private: private:
Pos pos_; ///< Position of corner closest to origin Pos pos_; ///< Position of corner closest to origin
@@ -66,7 +65,7 @@ namespace {
~Results() noexcept = default; ~Results() noexcept = default;
auto length() const noexcept -> size_t { return length_; } [[nodiscard]] auto length() const noexcept -> size_t { return length_; }
/** Output the grid. */ /** Output the grid. */
auto output() const -> void { auto output() const -> void {
@@ -87,8 +86,8 @@ namespace {
s[x + y * length_] = c; s[x + y * length_] = c;
} }
auto sq_x(Square const &sq) const noexcept -> size_t { return sq.pos() % length_; } [[nodiscard]] auto sq_x(Square const &sq) const noexcept -> size_t { return sq.pos() % length_; }
auto sq_y(Square const &sq) const noexcept -> size_t { return sq.pos() / length_; } [[nodiscard]] auto sq_y(Square const &sq) const noexcept -> size_t { return sq.pos() / length_; }
auto prettify_sq(std::string &s, Square const &sq) const noexcept -> void { auto prettify_sq(std::string &s, Square const &sq) const noexcept -> void {
switch (sq.length()) { switch (sq.length()) {
@@ -117,7 +116,7 @@ namespace {
size_t i = sq_x(sq) + n - 1; size_t i = sq_x(sq) + n - 1;
while (n != 0) { while (n != 0) {
set(s, --i, sq_y(sq) + 1, '0' + (n % 10)); set(s, --i, sq_y(sq) + 1, static_cast<char>('0' + static_cast<char>(n % 10)));
n /= 10; n /= 10;
} }
} }
@@ -134,7 +133,7 @@ namespace {
using T = std::int_fast64_t; using T = std::int_fast64_t;
/** Construct a grid of given side-length. */ /** Construct a grid of given side-length. */
Grid(size_t length) : grid_(length * length, empty), length_(length) { explicit Grid(size_t length) : grid_(length * length, empty), length_(length) {
} }
Grid(Grid const &other) = delete; Grid(Grid const &other) = delete;
@@ -148,12 +147,12 @@ namespace {
~Grid() noexcept = default; ~Grid() noexcept = default;
/** Get grid length */ /** Get grid length */
auto end() const noexcept -> size_t { return grid_.size(); } [[nodiscard]] auto end() const noexcept -> size_t { return static_cast<size_t>(grid_.size()); }
/** Add a square to the grid. */ /** Add a square to the grid. */
auto add(Square const &sq) noexcept -> void { auto add(Square const &sq) noexcept -> void {
/* One would expect the fastest way to do this would be to have x be the /* One would expect the fastest way to do this would be to have x be the
* fastest increasing index so we stores [pos, pos + 1,..., pos+length, ...] * fastest increasing index so we store [pos, pos + 1,..., pos+length, ...]
* But experimentation tells us this isn't so, and storing * But experimentation tells us this isn't so, and storing
* [pos, pos + length, ..., pos + 1, ...] is faster! * [pos, pos + length, ..., pos + 1, ...] is faster!
*/ */
@@ -175,7 +174,7 @@ namespace {
/** \brief Get length of the largest square that fits at \a pos in the grid. /** \brief Get length of the largest square that fits at \a pos in the grid.
*/ */
auto largest_square(Pos pos, size_t n) const noexcept -> size_t { [[nodiscard]] auto largest_square(Pos pos, size_t n) const noexcept -> size_t {
assert(pos < end()); assert(pos < end());
/* Because of how we walk through the grid (starting at 0,0 then increasing /* Because of how we walk through the grid (starting at 0,0 then increasing
@@ -203,27 +202,15 @@ namespace {
/** Get the next position to check starting at pos. /** Get the next position to check starting at pos.
* *
* Returns grid_.length() if no more positions avaialble. * Returns grid_.length() if no more positions available.
*/ */
auto next_pos(Pos pos) const noexcept -> Pos { [[nodiscard]] auto next_pos(Pos pos) const noexcept -> Pos {
auto const b = grid_.begin() + pos; auto const b = grid_.begin() + static_cast<std::ptrdiff_t>(pos);
auto const p = std::find(b, grid_.end(), empty); auto const p = std::find(b, grid_.end(), empty);
return p - grid_.begin(); return p - grid_.begin();
} }
private: private:
/** Set the grid position (x, y) to the character c.
*
* It is an error if (x, y) is already set to c - as that means we have overlapping
* squares.
*/
auto set(size_t x, size_t y, T c) noexcept -> void {
assert(x < length_);
assert(y < length_);
assert(grid_[x + y * length_] != c);
grid_[x + y * length_] = c;
}
std::vector<T> grid_; ///< The grid std::vector<T> grid_; ///< The grid
size_t length_; ///< Side length size_t length_; ///< Side length
@@ -231,7 +218,7 @@ namespace {
static constexpr char filled = 1; ///< Character used for a filled cell, static constexpr char filled = 1; ///< Character used for a filled cell,
}; };
/** Get the n'th triangular number. */ /** Get the n-th triangular number. */
auto triangle_num(size_t n) noexcept -> size_t { return (n * (n + 1)) / 2; } auto triangle_num(size_t n) noexcept -> size_t { return (n * (n + 1)) / 2; }
/** Vector used to identify the available squares. */ /** Vector used to identify the available squares. */
@@ -253,7 +240,7 @@ namespace {
* available squares until we find one that fits. * available squares until we find one that fits.
*/ */
// grid is our inprogress grid of square positions. // grid is our in-progress grid of square positions.
auto const length = triangle_num(n); auto const length = triangle_num(n);
Grid grid(length); Grid grid(length);
@@ -276,7 +263,7 @@ namespace {
while (true) { while (true) {
/* If the idx is 0 we've looked at all possible square lengths for this /* If the idx is 0 we've looked at all possible square lengths for this
* position, and they've failed. Pop the last square of the stack, remove * position, and they've failed. Pop the last square of the stack, remove
* it from the grid and try the next smaller one in the same position. * it from the grid and try the next smaller size in the same position.
*/ */
if (idx == 0) { if (idx == 0) {
// No squares on the stack -> failed to find a solution. // No squares on the stack -> failed to find a solution.
@@ -312,13 +299,13 @@ namespace {
if (pos == grid.end()) { break; } if (pos == grid.end()) { break; }
} }
return Results(length, sqs); return {length, sqs};
} }
} // anon namespace } // anon namespace
int main(int argc, char **argv) { int main(int argc, char **argv) {
auto n = (argc == 1) ? 8 : std::atol(argv[1]); auto n = (argc == 1) ? 8 : std::atol(argv[1]);
auto grid = find_solution(n); auto const grid = find_solution(n);
std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n'; std::cout << "Partridge problem " << n << " side length " << grid.length() << '\n';
grid.output(); grid.output();
return 0; return 0;

View File

@@ -5,9 +5,9 @@ The following show some of the results produced by running `partridge_cpp`.
# Partridge 8 # Partridge 8
Timings on Macbook Air M1: Timings on Macbook Air M1:
* 2.05s user * 1.76s user
* 0.01s system * 0.00s system
* 2.063 total * 1.765 total
```text ```text
+------++------++------++------++--+ +------++------++------++------++--+
@@ -50,9 +50,9 @@ Timings on Macbook Air M1:
# Partridge 9 # Partridge 9
Timings on Macbook Air M1: Timings on Macbook Air M1:
* 176.48s user * 156.99s user
* 0.25s system * 0.18s system
* 2:59.03 total * 2:38.69 total
```text ```text
+-------++-------++-------++-------++-------+ +-------++-------++-------++-------++-------+
@@ -105,9 +105,9 @@ Timings on Macbook Air M1:
# Partridge 10 # Partridge 10
Timings on Macbook Air M1: Timings on Macbook Air M1:
* 30578.68s user * 23828.35s user
* 88.19s system * 5.62s system
* 8:40:24.11 total * 6:38:02.83 total
```text ```text
+--------++--------++--------++--------++--------++---+ +--------++--------++--------++--------++--------++---+