A not-so-fancy way would be to build the grid of lines, and then collect the 4-tuples of lines from the grid.
First, the preamble:
#! /usr/bin/env python3
import numpy as np
start = 17
sep = 23
rep = np.array([4,3,3,1])
To build the grid, note that there the length of rep is how long the grid is in the x axis, and the maximum value in rep is how long it is in the y axis. However, we are building a grid of lines, not points, so it's 1 less in each direction. Note that I use N_y and N_x in that order so that a simple print shows the grid in format that's closer to the diagram in the picture.
N_x = len(rep) - 1
N_y = max(rep) - 1
grid = np.zeros((N_y, N_x, 2))
How many horizontal lines are there in each column? For the column formed by points (x_i, y) and (x_i+1, y), that's min(rep[i], rep[i+1]) - the number of points in the smaller column. For column 0, it's 0 - there's nothing to the left of the grid. That also gets us the total number of horizontal lines.
The number of vertical lines in each column is easy - it's the number of points in the y axis - 1. (And hence the total number of vertical lines is the sum of rep - length of rep.)
kxs = [0] + [min(rep[i], rep[i+1]) for i in range(len(rep)-1)]
T_x = sum(kxs)
T_y = sum(rep) - len(rep)
T_total = T_x + T_y
lines = np.arange(start, start+T_total)
Let's start filling the grid. The indexing here is awkward since we're filling it from bottom up, and since the axes are swapped as mentioned earlier. We go from left to right and bottom to top.
# `lines_before` is essentially, an index into the `lines` array,
# keeping track of how many lines have already been used
lines_before = 0
for i in range(N_x):
for j in range(N_y-1, -1, -1):
if j >= kxs[i+1]:
# We haven't reached the minimum depth (or height, however you think of it)
continue
grid[j,i,0] = lines[lines_before]
lines_before += 1
print(grid[:,:,0])
for i in range(N_x):
for j in range(N_y-1, -1, -1):
if j < rep[i] - 1:
grid[j,i,1] = lines[lines_before]
lines_before += 1
print(grid[:,:,1])
At this point the output is, for the numbers in the question:
[[19. 22. 23.]
[18. 21. 0.]
[17. 20. 0.]]
[[26. 28. 30.]
[25. 27. 29.]
[24. 0. 0.]]
The line numbers do match with the image, and the zeros now indicate where lines aren't present.
Now we can collect the sets of lines. Starting from the top, and going left to right and top to bottom, at each position we collect the horizontal line there and the one below it, and the vertical line there and the one to the right of it. (From the image, then, that would be taking (19, 18, 26, 28), (18, 17, 25, 27), etc.) For each collected set, if any number is zero, that means it's missing a line, and we can eliminate it. So:
for i in range(N_x - 1):
for j in range(N_y - 1):
square = np.append(grid[j:j+2, i, 0], grid[j, i:i+2, 1])
if all(square):
print(square)
Giving:
[19. 18. 26. 28.]
[18. 17. 25. 27.]
[22. 21. 28. 30.]
[21. 20. 27. 29.]
It's not in the order you want, and we're building the grid, which might be too expensive, but I leave the ordering and optimization as an exercise, with the following notes:
- From
kxs and rep, the number of the line at each position in the grid can be determined, so we don't need the grid at all.
- The ordering is just a question of which direction you traverse the grid.