import time class LengthError(ValueError): pass class Cut(object): def __init__(self, l): self.Length = l class CutableRod(object): def __init__(self): self.Length = 6000 self.CutThickness = 0 self.Remainder = self.Length self.Cuts = [] def addCut(self, cut): if not self.canAddCut(cut): raise LengthError("Cut.Length=%i, Remainder=%i" % (cut.Length, self.Remainder)) self.Cuts.append(cut) self.Remainder -= cut.Length self.Remainder -= self.CutThickness def copy(self): new_rod = CutableRod() new_rod.Length = self.Length new_rod.CutThickness = self.CutThickness new_rod.Remainder = self.Remainder for c in self.Cuts: new_rod.Cuts.append(c) return new_rod def canAddCut(self, cut): return self.Remainder >= cut.Length def variety(self): """ Amount of different cut lengths """ lengths = [] for cut in self.Cuts: if cut.Length not in lengths: lengths.append(cut.Length) return len(lengths) class CutSolution(object): def __init__(self): self.Rods = [] def isBetterThan(self, other): if self.rods() != other.rods(): return self.rods() < other.rods() elif self.remainder() != other.remainder(): return self.remainder() < other.remainder() elif self.variety() != other.variety(): return self.variety() < other.variety() else: return False def copy(self): new_solution = CutSolution() for r in self.Rods: new_solution.Rods.append(r.copy()) return new_solution def variety(self): variety = 0 for r in self.Rods: v = r.variety() if v > 1: variety += v return variety def rods(self): return len(self.Rods) def remainder(self): remainder = 0 for r in self.Rods: remainder += r.Remainder return remainder def print(self, short=False): if not short: print("\nSolution") for idx_r in range(len(self.Rods)): r = self.Rods[idx_r] print("Rod ", idx_r, ": ", end="", sep="") for idx_c in range(len(r.Cuts)): c = r.Cuts[idx_c] print(c.Length, " ", end="", sep="") print("(-", r.Remainder, ")", sep="") print("Rods=", self.rods(), ", Remainder=", self.remainder(), ", Variety=", self.variety(), sep="") class CutRequest(object): def __init__(self): self.Cuts = [] self.CreatedSolutions = [] def add(self, amount, length): for i in range(amount): c = Cut(length) self.Cuts.append(c) def _iterate(self, current_solution, remaining_cuts): if len(remaining_cuts) == 0: return current_solution best_solution = None tested_lengths = [] for idx_try in range(len(remaining_cuts)): cut = remaining_cuts[idx_try] # avoid testing same length again if cut.Length in tested_lengths: continue tested_lengths.append(cut.Length) # create temporary solution for trial trysol = CutSolution() if current_solution is None else current_solution.copy() # add cut to temporary solution if len(trysol.Rods) == 0 or not trysol.Rods[-1].canAddCut(cut): trysol.Rods.append(CutableRod()) trysol.Rods[-1].addCut(cut) # create list of remaining other cuts remaining_cuts_without_try = [] for idx_other in range(len(remaining_cuts)): if idx_other != idx_try: cut_other = remaining_cuts[idx_other] remaining_cuts_without_try.append(cut_other) # solve for tried remaining cuts trysol = self._iterate(trysol, remaining_cuts_without_try) # keep best solution if best_solution is None or trysol.isBetterThan(best_solution): best_solution = trysol if current_solution is None: print("Better Solution: ", end="") best_solution.print(True) return best_solution def arrangeCuts(self): time_start = time.time() solution = self._iterate(None, self.Cuts) solution.print() time_stop = time.time() print("duration: %0.1f" % (time_stop - time_start)) if __name__ == "__main__": cr = CutRequest() cr.add(8, 660); cr.add(16, 620); cr.add(1, 905); cr.add(2, 442); cr.add(2, 848); cr.add(4, 414); cr.arrangeCuts()