С# - сложность времени для генерации всех пар в массиве
Учитывая массив чисел, сгенерируйте все уникальные пары.
Например, учитывая [ 1, 2, 3, 4, 5 ]
единственная пара чисел будет:
(1, 2), (1, 3), (1, 4), (1, 5)
(2, 3), (2, 4), (2, 5)
(3, 4), (3, 5)
(4, 5)
Мое решение таково:
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
HashSet<Pair> pairs = new HashSet<Pair>();
for(int i = 0; i < numbers.Length; i++)
{
for(int j = i + 1, j < numbers.Length; j++)
{
pairs.Add(new Pair(numbers[i], numbers[j]));
}
}
Я думаю, что временная сложность для этого выглядит как O (n 2 - 1), вычитая 1, потому что итерация j
всегда 1 короче i
Проведя немного исследований в этой проблеме, я не могу найти окончательных ответов о том, можно ли это сделать быстрее. Есть ли лучшие решения, чем O (n 2 - 1)?
Ответы
Ответ 1
Один из способов подумать о "есть ли более быстрый способ решения проблемы" - это посмотреть на размер вывода для определенного формата (который вы считаете "вероятно самым большим/самым трудным для решения").
Если выход O(n^2)
, то вы не можете решить проблему быстрее, чем в O(n^2)
, потому что вам нужно потратить не менее O(1)
для каждого вывода.
Вы можете увидеть шаблон там, если у вас есть 5 номеров в формате [1, 2, 3, 4, 5]
, уникальные пары принимают
4 pairs in first row
3 pairs in second row
2 pairs...
1 pair
потому что они выглядят как
(1, 2), (1, 3), (1, 4), (1, 5)
(2, 3), (2, 4), (2, 5)
(3, 4), (3, 5)
(4, 5)
Если у вас есть 20 переменных в массиве (в формате [1, 2, 3,... 18, 19, 20]
), это будет следующим:
19 pairs
18 pairs
...
2 pairs
1 pair
Поэтому выходной размер равен (n-1) + (n-2) + (n-3)... + 3 + 2 + 1
. Вы должны это суммировать (посмотрите, как суммировать серию), а результат - O(n^2)
Что было доказано?
В худшем случае сценарий AT LEAST O(n^2)
.
Также обратите внимание, что в настоящий момент мы не знаем реальной сложности худшего случая: alghorithm может быть еще медленнее (мы просто обнаруживаем, что некоторый вход принимает O(n^2)
). Мы точно знаем, что по крайней мере эти данные принимают O(n^2)
. Это может быть быстрее или медленнее для разных входных данных.
Включение: у нас есть доказательство того, что алгоритм занимает не менее O(n^2)
времени (в худшем случае), вы создали алгоритм, который работает максимум O(n^2)
(как описано в spyc post) = У вас есть оптимальный алгоритм.
Дополнительная информация для решения OP: Обнаружение столкновений с HashSet - это только "псевдоконстант" и только для небольших чисел и "удачи". Для большого количества чисел требуется O(n)
. Таким образом, вы можете получить выход n^2
и каждый из них будет обрабатывать n
до процесса, что приведет к сложности n^3
.
Вы можете решить эту проблему с предварительной обработки задачи:
1) Сортировка - требуется только n log n
, поэтому не влияет на n^2
любом случае
2) Удалите числа, повторяющиеся более двух раз [1, 3, 3, 3, 5] → [1, 3, 3, 5]
, это O(n)
3) Затем используйте свой алгоритм с этим обновлением:
3.1) В начале цикла for i
: if (number[i] == number[i-1]) continue;
3.2) В начале цикла for j
: помните последнюю пару. При добавлении новой пары, посмотрите на последнюю пару и проверьте, одинаково она или нет. Если так - continue;
Пример:
Input: [1, 3, 3, 5]
1)i=0, j=1, number[0]=1, number[1]=3 -> add (1, 3)
2)i=0, j=2, number[0]=1, number[2]=3 -> same as last pair, use continue
3)i=0, j=3, number[0]=1, number[3]=5 -> add (1, 5)
4)i=1, j=2, number[1]=3, number[2]=3 -> add (3, 3)
5)i=1, j=3, number[1]=3, number[3]=5 -> add (3, 5)
6)i=2, before go to j-cycle, check number[i] === number[i-1] It is true, use continue
Ответ 2
Это выглядит следующим образом:
first for loop - O(n)
second for loop - O(n-1)
Оптимальная сложность времени:
- Несмотря на то, что эта итерация ничтожно мала, и вы должны рассчитать сложность времени для наихудшего сценария, который является
Вы также можете использовать биномиальный коэффициент для перестановок, чтобы получить число перестановок определенной строки. Например:
Если у вас есть 6 цифр {0,1,2,3,4,5} (n = 6), и вы хотите знать, сколько различных перестановок вы можете сделать, то есть: (3,5), (5,3) и т.д.... тогда (k = 2, две цифры в каждой группе), количество перестановок будет:
разные перестановки, обратите внимание, что в этом случае (3,5), (5,3) учитываются индивидуально, поэтому порядок этого имеет значение. Если вы хотите (5,3) и (3,5) считаться одной комбинацией, то уравнение имеет следующий вид:
Пример реализации, вычисление перестановок!
static long factorial(long x) // calcs the factorial TimeCmplx = O(n)
{
if (x == 1)
return x;
return x * factorial(x - 1);
}
static long permutations(long n , long k) //Check that (n , k) >= 0
{
// Permutations , n!/(n-k)!
return factorial(n) / factorial(n - k);
}
Ответ 3
Я думаю, что временная сложность для этого выглядит как O (n2 - 1), вычитающая 1, потому что итератор j всегда 1 короче i
Если бы это имело значение (большая-O-нотация, как правило, вы только пишете термин с самым быстрым ростом), то у вас есть итерации я над [0, n), каждая из которых содержит итерацию j над [i + 1, n), так что число итераций (n ∙ (n -1))/2 не n² -1.
Кроме того, изменение в HashSet вместо изменения списка приведет к наихудшему исполнению, но не к амортизированному значению - если Pair.GetHashCode() всегда возвращал одно и то же значение, вы бы натолкнули его на O (n³), как в случаи, когда коллизии являются общими установками хэш-набора, становятся O (n), а не постоянными.
Ответ 4
Это область треугольного алгоритма.
У вас есть N входов, и вам нужен треугольник выходов.
Ваш выходной треугольник имеет высоту N-1 и ширину N-1.
Area of a triangle = height * width / 2
= (N-1) * (N-1) / 2
= (N^2 - 2N + 1) / 2
O(n^2 - n)
всегда будет минимальной/оптимальной стоимостью алгоритма!