#!/usr/bin/env python3 # This script calculates the optimal cut distribution to reduce waste while matching the desired 2:2:4 segment count # It applies brute force since the problem space is very small. import numpy as np import itertools l_tot = 500 a, b, c = 94, 124, 80 n, m, l = 2, 2, 4 arr = np.array([a, b, c], dtype=int) arr_count = np.array([n, m, l], dtype=int) # Find all possible splits of a [l_tot] m led tape into segments of lengths [a], [b] and [c] that leave a remainder # that's smaller than any of [a], [b] and [c]. candidates = [] for i in range(l_tot//a + 1): l_rem_i = l_tot - i*a if l_rem_i < 0: continue for j in range(l_rem_i//b + 1): l_rem_j = l_rem_i - j*b if l_rem_j < 0: continue k = l_rem_j // c l_rem_k = l_rem_j - k*c print(f'Candidate: ({i} {j} {k}) {i=}*{a} {j=}*{b=} {k=}*{c=} => remainder {l_rem_k}') candidates.append((i, j, k)) candidates = np.array(candidates, dtype=int) print() # Find all ways to combine the cuts found above to cut [num_rolls] into segments, where the amount of segments of length # [a], [b], and [c] that we get in total best matches the proportions we need ([n] times [a], [m] times [b], [l] times # [c], so 2:2:4 times for 94:124:80 cm) num_rolls = 3 indices_seen = set() out = [] for indices in itertools.product(candidates, repeat=num_rolls): indices = np.array(indices) index_tup = tuple(sorted(map(tuple, indices))) if index_tup in indices_seen: continue indices_seen.add(index_tup) rem = l_tot - (indices * arr).sum(axis=1) rem_total = rem.sum() count_total = indices.sum(axis=0).astype(float) count_total /= arr_count spread = count_total.max() - count_total.min() if spread > 2 or (rem < 2).any(): continue print(indices.tolist(), f'{rem_total=} {spread=}') out.append((spread, rem_total, indices.tolist(), rem.tolist(), indices.sum(axis=0).tolist())) print() # Print out the n best matches found. Sort first by how close we match our target 2:2:4 ratio, then by how much waste # we leave. print('Best matches:') for spread, rem_total, indices, rem, index_sum in sorted(out, key=lambda x: (x[0], x[1]))[:25]: print(indices, f'{spread=} {rem_total=} {rem=} {index_sum=}') # Here's the output for future reference. There are a number of combinations that produce 68 cm of waste split across # three 5m rolls of tape. We selected # 1 since it leaves leftovers of useful lengths. # # Best matches: # [[0, 0, 6], [0, 4, 0], [4, 0, 1]] spread=0.25 rem_total=68 rem=[20, 4, 44] index_sum=[4, 4, 7] # [[0, 0, 6], [1, 3, 0], [3, 1, 1]] spread=0.25 rem_total=68 rem=[20, 34, 14] index_sum=[4, 4, 7] # [[0, 2, 3], [0, 2, 3], [4, 0, 1]] spread=0.25 rem_total=68 rem=[12, 12, 44] index_sum=[4, 4, 7] # [[0, 2, 3], [1, 1, 3], [3, 1, 1]] spread=0.25 rem_total=68 rem=[12, 42, 14] index_sum=[4, 4, 7] # [[0, 2, 3], [2, 1, 2], [2, 1, 2]] spread=0.25 rem_total=68 rem=[12, 28, 28] index_sum=[4, 4, 7] # [[0, 3, 1], [1, 0, 5], [3, 1, 1]] spread=0.25 rem_total=68 rem=[48, 6, 14] index_sum=[4, 4, 7] # [[0, 4, 0], [1, 0, 5], [3, 0, 2]] spread=0.25 rem_total=68 rem=[4, 6, 58] index_sum=[4, 4, 7] # [[1, 0, 5], [1, 3, 0], [2, 1, 2]] spread=0.25 rem_total=68 rem=[6, 34, 28] index_sum=[4, 4, 7] # [[0, 0, 6], [0, 3, 1], [3, 1, 1]] spread=0.5 rem_total=82 rem=[20, 48, 14] index_sum=[3, 4, 8] # [[0, 0, 6], [0, 4, 0], [3, 0, 2]] spread=0.5 rem_total=82 rem=[20, 4, 58] index_sum=[3, 4, 8] # [[0, 0, 6], [1, 3, 0], [2, 1, 2]] spread=0.5 rem_total=82 rem=[20, 34, 28] index_sum=[3, 4, 8] # [[0, 1, 4], [0, 2, 3], [3, 1, 1]] spread=0.5 rem_total=82 rem=[56, 12, 14] index_sum=[3, 4, 8] # [[0, 2, 3], [0, 2, 3], [3, 0, 2]] spread=0.5 rem_total=82 rem=[12, 12, 58] index_sum=[3, 4, 8] # [[0, 2, 3], [1, 0, 5], [2, 2, 0]] spread=0.5 rem_total=82 rem=[12, 6, 64] index_sum=[3, 4, 8] # [[0, 2, 3], [1, 1, 3], [2, 1, 2]] spread=0.5 rem_total=82 rem=[12, 42, 28] index_sum=[3, 4, 8] # [[0, 3, 1], [1, 0, 5], [2, 1, 2]] spread=0.5 rem_total=82 rem=[48, 6, 28] index_sum=[3, 4, 8] # [[0, 4, 0], [1, 0, 5], [2, 0, 3]] spread=0.5 rem_total=82 rem=[4, 6, 72] index_sum=[3, 4, 8] # [[1, 0, 5], [1, 1, 3], [1, 3, 0]] spread=0.5 rem_total=82 rem=[6, 42, 34] index_sum=[3, 4, 8] # [[0, 0, 6], [0, 3, 1], [4, 0, 1]] spread=0.5 rem_total=112 rem=[20, 48, 44] index_sum=[4, 3, 8] # [[0, 0, 6], [1, 2, 1], [3, 1, 1]] spread=0.5 rem_total=112 rem=[20, 78, 14] index_sum=[4, 3, 8] # [[0, 0, 6], [1, 3, 0], [3, 0, 2]] spread=0.5 rem_total=112 rem=[20, 34, 58] index_sum=[4, 3, 8] # [[0, 0, 6], [2, 1, 2], [2, 2, 0]] spread=0.5 rem_total=112 rem=[20, 28, 64] index_sum=[4, 3, 8] # [[0, 1, 4], [0, 2, 3], [4, 0, 1]] spread=0.5 rem_total=112 rem=[56, 12, 44] index_sum=[4, 3, 8] # [[0, 1, 4], [1, 1, 3], [3, 1, 1]] spread=0.5 rem_total=112 rem=[56, 42, 14] index_sum=[4, 3, 8] # [[0, 1, 4], [2, 1, 2], [2, 1, 2]] spread=0.5 rem_total=112 rem=[56, 28, 28] index_sum=[4, 3, 8]