У меня есть класс, который решает проблему точного покрытия, используя рекурсивный алгоритм поиска с возвратом. Первоначально я реализовал класс с помощью функции обратного вызова, которую я передал объекту во время инициализации. Этот обратный вызов вызывается всякий раз, когда найдено решение. Глядя на чью-то реализацию той же проблемы, я увидел, что они использовали операторы yield для передачи решения, другими словами, их код был генератором Python. Я подумал, что это интересная идея, поэтому сделал новую версию своего класса, чтобы использовать урожайность. Затем я провел сравнительные тесты между двумя версиями и, к своему удивлению, обнаружил, что версия генератора работает в 5 раз медленнее, чем версия обратного вызова. Обратите внимание, что, за исключением переключения доходности для обратного вызова, код идентичен.
Что здесь происходит? Я предполагаю, что, поскольку генератору необходимо сохранить информацию о состоянии, прежде чем уступить, а затем восстановить это состояние при перезапуске при следующем вызове, именно это сохранение / восстановление является причиной того, что версия генератора работает намного медленнее. Если это так, сколько информации о состоянии генератор должен сохранить и восстановить?
Есть идеи от экспертов по Python?
- Отредактировано 7:40 PDT
Вот код решателя, который использует yield . Замените первый yield ниже вызовом функции обратного вызова и измените следующий цикл вторым yield на просто рекурсивный вызов для решения исходной версии этого кода.
def solve(self):
for tp in self.pieces:
if self.inuse[tp.name]: continue
self.inuse[tp.name] = True
while tp.next_orientation() is not None:
if tp.insert_piece():
self.n_trials += 1
self.pieces_in += 1
self.free_cells -= tp.size
if self.pieces_in == len(self.pieces) or self.free_cells == 0:
self.solutions += 1
self.haveSolution = True
yield True
self.haveSolution = False
else:
self.table.next_base_square()
for tf in self.solve():
yield tf
tp.remove_piece()
self.pieces_in -= 1
self.table.set_base_square(tp.base_square)
self.free_cells += tp.size
self.inuse[tp.name] = False
tp.reset_orientation()
Почтовый цикл, который вызывает решатель (после инициализации, конечно)
start_time = time.time()
for tf in s.solve():
printit(s)
end_time = time.time()
delta_time = end_time - start_time
В версии обратного вызова цикл завершается с помощью всего лишь одного вызова для решения.