Какой у этого решения time и space complexity?

Решил задачку на List Permutation:
Т.е для [1,2,3] пермутациями являются
['1', '2', '3']
['1', '3', '2']
['2', '1', '3']
['2', '3', '1']
['3', '1', '2']
['3', '2', '1']

Мое решение:
def getPermutations(array):
	if len(array) == 0:
		return []
  elif len(array) == 1:
		return [array]
		
	last_elem = array[-1:]
	all_elem_except_last = array[:-1]
	
	permutations = getPermutations(all_elem_except_last)
	
	new_permutations = []
	
	for permutation in permutations:
		for i in range(len(permutation)+1):
			new_permutation = permutation[:i] + last_elem + permutation[i:]
			new_permutations.append(new_permutation)
			
	return new_permutations
  • Вопрос задан
  • 466 просмотров
Решения вопроса 1
wataru
@wataru Куратор тега Алгоритмы
Разработчик на С++, экс-олимпиадник.
Давайте составим рекурентное соотношение. Пусть F(n) - сколько работает ваша функция при входе в n элементов.

В функции:
- Выделяется новый массив: n операций
- Вызывается функция для укороченного массива: F(n-1) операций
- Цикл по всем перестановкам n-1 элемента: (n-1)! повторений
- Вложенный цикл по позициям: n*(n-1)! = n! повторений
- вложенное в циклы построение новой перестановки: n*n! операций

Итого F(n) = n + F(n-1) +n*n!

Можно раскрыть рекурсивно и получим:
F(n) = 1 + 2 + 3 + ... + n + n*n! + (n-1)*(n-1)! + (n-2)*(n-2)! + ... +1
Аккуратно посмотрев на это можно понять, что все члены, начиная от (n-1)*(n-1)!, cумарно меньше n*n!, а первые n членов вообще мизерные и их можно отбросить.
В итоге, F(n) = O(n*n!).

Обратите внимание, тут мало просто взять самый большой член. Их же n штук и если бы они были примерно одинакового порядка, то возник бы еще один множитель n в ответе. Но тут остальные члены как минимум в n раз меньше самого крупного.

Сложность по памяти такая же - ведь вы там в итоге храните все перестановки, их n! и каждая занимает n. Но это оценка ассимптотики - O(n*n!). На самом деле будет чуть больше n*n! элементов, потому что хранится список ответ и список для меньших перестановок, плюс стек и промежуточные массивы при конкатенации.

Это хорошая ассимптотика - для построения всех перестановок меньше нельзя.
Однако, если вам не надо все перестановки хранить в памяти (да и для кэша, кстати) будет заметно быстрее генерировать следующую перестановку из текущей на месте, как описано тут. Хоть и с такой же ассимптотикой, этот алгоритм будет работать в несколько раз быстрее.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы