176 lines
4.8 KiB
Python
176 lines
4.8 KiB
Python
|
|
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()
|