Сложность для генерации перестановок строки
Я пытаюсь выяснить, как сложность кода (от взлома книги Интервью кодирования) для генерации всех перестановок данной строки равна O (n!).
Я знаю, что это наилучшая возможная сложность, поскольку у нас есть n! перестановки, но я хотел бы понять это кодово, потому что не каждый алгоритм, который делает это, будет O (n!).
Код :
import java.util.*;
public class QuestionA {
public static ArrayList<String> getPerms(String str) {
if (str == null) {
return null;
}
ArrayList<String> permutations = new ArrayList<String>();
if (str.length() == 0) { // base case
permutations.add("");
return permutations;
}
char first = str.charAt(0); // get the first character
String remainder = str.substring(1); // remove the first character
ArrayList<String> words = getPerms(remainder);
for (String word : words) {
for (int j = 0; j <= word.length(); j++) {
String s = insertCharAt(word, first, j);
permutations.add(s);
}
}
return permutations;
}
public static String insertCharAt(String word, char c, int i) {
String start = word.substring(0, i);
String end = word.substring(i);
return start + c + end;
}
public static void main(String[] args) {
ArrayList<String> list = getPerms("abcde");
System.out.println("There are " + list.size() + " permutations.");
for (String s : list) {
System.out.println(s);
}
}
}
Это то, о чем я думал до сих пор. :
При любом вызове функции количество доступных слов равно (n-1) ; предполагая, что мы находимся в месте, где остаток имеет длину (n-1). Теперь, чтобы вставить N-й элемент во все возможные места для всех этих (n-1) слов, требуется(n-1)*(n-1) Время.
Таким образом, через выполнение, это должно быть (n-1)^2+(n-2)^2+(n-3)^2+....2^2+1^2 операции, которые я не думаю, что n!.
Что я пропустил здесь?
2 ответа:
Я думаю, что временная сложность
getPerms
равнаO((n + 1)!)
.Обозначим время работы
getPerms
черезT(n)
, гдеn
- Длина входного сигнала.===================================================================
Две ветви
if
и линияchar first = str.charAt(0)
занимаютO(1)
Время. И следующая строка занимаетO(n)
Время:String remainder = str.substring(1); // remove the first character
Следующая строка занимает время
T(n - 1)
:ArrayList<String> words = getPerms(remainder);
Теперь рассмотрим время выполнения вложенного
for-loops
. Размер самого внешнийfor-loop
- это(n-1)!
:for (String word : words) {
А размер внутреннего
for-loop
равенn + 1
:for (int j = 0; j <= word.length(); j++) {
И сложность
Таким образом, общее время выполнения вложенногоinsertCharAt
такжеO(n)
.for-loops
равно(n + 1) * (n - 1)! * O(n) = O((n + 1)!)
.Поэтому мы имеем следующее отношение:
T(n) = T(n - 1) + O(n) + O((n + 1)!) = T(n - 1) + O(n) + O((n + 1)!) = (T(n - 2) + O(n - 1) + O(n!) + O(n) + O((n + 1)!) = T(n - 2) + ( O(n - 1) + O(n) ) + ( O(n!) + O((n + 1)!) ) = ... = O(n2) + (1 + ... + O(n!) + O((n + 1)!) ) = O((n + 1)!)
Если вы изучаете это, было бы лучше изучить общие решения, а не только реализацию, представленную в вашем примере. Седжвик сделал лучший анализ, который я знаю. Я преподаю его в своем классе.
Https://www.cs.princeton.edu/~РС/переговоры/завивка.формат PDF
Сложность каждого вызова функции generate равна O(n). Следовательно, стоимость равна O (n!).
Представленный вами код крайне неэффективен. Существует огромный постоянный фактор, потому что вы создание множества строковых объектов массива-одна из самых неэффективных вещей, которые вы можете сделать в Java.
Если вы просто хотите пройти через все перестановки, перестановку одного объекта, не создавайте список. Вот более быстрая реализация:Другой метод, который показывает Седжвик, - это Heaps, который представляет собой только один своп на перестановку вместо 2. Вот реализация C++:public class Permute { private int[] a; private void swap(int i, int j) { int temp = a[i]; a[i] = a[j]; a[j] = temp; } public Permute(int n) { a = new int[n]; for (int i = 0; i < n; i++) a[i] = i+1; this.generate(n); } public void generate(int N) { // System.out.println("generate(" + N + ")"); if (N == 0) doit(); for (int c = 0; c < N; c++) { // System.out.print("Swapping: " + c + "," + N); swap(c, N-1); //swap(0, 7) generate(N-1); // System.out.print("Swapping: " + c + "," + N); swap(c, N-1); } } public void doit() { for (int i = 0; i < a.length; i++) System.out.print(a[i] + " "); System.out.println(); } public static void main(String[] args) { Permute p = new Permute(4); } }
#include <vector> #include <iostream> using namespace std; class Heaps { private: vector<int> p; public: Heaps(int n) { p.reserve(n); for (int i = 0; i < n; i++) p.push_back(i+1); generate(n); } void doit() { cout << "doit size=" << p.size() << '\n'; for (int i = 0; i < p.size(); i++) cout << p[i]; cout << '\n'; } void generate(int N) { // cout << "generate(" << N << ")\n"; if (N == 0) doit(); for (int c = 0; c < N; c++) { generate(N-1); swap(p[N % 2 != 0 ? 0 : c], p[N-1]); } } }; int main() { Heaps p(4); }