@@ -32,48 +32,37 @@ def solve(job: Job) -> Result:
32
32
33
33
# slowest, but perfect solver; originally O(n!), now much faster (see Job.n_combinations())
34
34
def _solve_bruteforce (job : Job ) -> tuple [ResultEntry , ...]:
35
- mutable_job = job .model_copy (deep = True )
35
+ minimal_trimmings = float ('inf' )
36
+ best_results = []
36
37
37
- # find every possible ordering (`factorial(sum(sizes))` elements) and reduce to unique
38
- # equal to set(permutations(...)), but much more efficient
39
- all_orderings = distinct_permutations (mutable_job .iterate_required ())
40
- all_stocks = distinct_permutations (mutable_job .iterate_stocks ())
41
-
42
- # would be int max if we had an upper boundary
43
- minimal_trimmings : int | None = None
44
- best_results : list [tuple [ResultEntry , ...]] = []
45
-
46
- # could distribute to multiprocessing, but web worker is parallel anyway
47
- for stock_ordering in all_stocks :
48
- for required_ordering in all_orderings :
49
- result = _group_into_lengths (stock_ordering , required_ordering , mutable_job .cut_width )
38
+ required_orderings = distinct_permutations (job .iterate_required ())
39
+ for stock_ordering in distinct_permutations (job .iterate_stocks ()):
40
+ for required_ordering in required_orderings :
41
+ result = _group_into_lengths (stock_ordering , required_ordering , job .cut_width )
50
42
if result is None :
51
- # seems like we were able to short-circuit
43
+ # Short-circuit if bad solution
52
44
continue
53
45
trimmings = sum (lt .trimming for lt in result )
54
- if minimal_trimmings is None or trimmings < minimal_trimmings :
46
+ if trimmings < minimal_trimmings :
55
47
minimal_trimmings = trimmings
56
- best_results .clear ()
57
- best_results .append (result )
48
+ best_results = [result ]
58
49
elif trimmings == minimal_trimmings :
59
50
best_results .append (result )
60
51
61
- assert len (best_results ) > 0
62
- ideal_result = find_best_solution (best_results )
63
- return sort_entries ([r for r in ideal_result ])
52
+ assert best_results , "No valid solution found"
53
+ return sort_entries (find_best_solution (best_results ))
64
54
65
55
66
56
def _group_into_lengths (stocks : tuple [NS , ...], sizes : tuple [NS , ...], cut_width : int ) \
67
57
-> tuple [ResultEntry , ...] | None :
68
58
"""
69
59
Collects sizes until length is reached, then starts another stock
70
- Returns none for orderings that exceed ideal conditions
60
+ Returns None for orderings that exceed ideal conditions
71
61
"""
72
62
available = list (reversed (stocks ))
73
63
required = list (reversed (sizes ))
74
64
75
65
result : list [ResultEntry ] = []
76
-
77
66
current_cuts : list [NS ] = []
78
67
cut_sum = 0 # could be calculated, but I think this is faster
79
68
@@ -150,7 +139,7 @@ def _solve_FFD(job: Job) -> tuple[ResultEntry, ...]:
150
139
return sort_entries ([create_result_entry (job .stocks [0 ].as_base (), r , job .cut_width ) for r in layout ])
151
140
152
141
153
- # even faster than FFD, seems like equal results; selfmade and less proven!
142
+ # even faster than FFD, seems like equal results; self-made and less proven!
154
143
def _solve_gapfill (job : Job ) -> tuple [ResultEntry , ...]:
155
144
"""
156
145
1. Sort by magnitude (largest first)
0 commit comments