#c #performance #time-complexity
#c #Производительность #временная сложность
Вопрос:
У меня здесь есть 2 программы, которые отчаянно нуждаются в оценке эффективности с использованием нотации big o. Что касается первого, я не уверен в эффекте оператора if. У этого есть вложенный цикл, и они обычно имеют эффективность O (n ^ 2), но я не могу быть уверен. Другой также имеет вложенный цикл, но с командой break; .
/* modifies a so that all the even numbers appear before
all of the odd numbers. there are multiple possible results
-the order of the even numbers does not matter
-the order of the odd numbers does not matter
*/
void even_first(int a[], int len) {
int temp = 0;
for (int i = 0; i < len; i) {
if (a[i] % 2 == 0) {
temp = a[i];
for (int j = i; j > 0; --j) {
a[j] = a[j - 1];
}
a[0] = temp;
}
}
}
////////////////////////////////////////////////////////////////////
/* finds the single integer in the inclusive range 1...[len 1] that does
not appear in a. Because the elements of a are unique and all in the range
1...(len 1), there must be a single integer in that range
that is "missing" from a */
int missing(int a[], int len) {
for (int j = 1; j <= len 1; j) {
bool found = false;
for (int i = 0; i < len; i) {
if (a[i] == j) {
found = true;
break;
}
}
if (!found) {
return j;
}
}
return -1;
}
Комментарии:
1. Если вы действительно заботитесь об эффективности, тогда разберите оптимизированный код и поищите там. «Big O» в основном устарел, поскольку количество итераций не обязательно равно производительности. В настоящее время алгоритмы линейного поиска с использованием грубой силы во многих случаях превосходят бинарный поиск.
2. Простой способ выяснить это: протестируйте этот код при различных условиях N , например, начиная с 1 и повышаясь в 10 раз с каждым шагом. Вы очень быстро обнаружите характеристики производительности.
3. @Lundin Это, вероятно, домашнее задание / упражнение для теоретического курса информатики или что-то в этом роде. Это бесполезно.
4. @tadman Однако это не поможет вам получить доказательства…
5. @user202729 Не доказательство, как в «какое обозначение O это имеет», а эмпирические данные о том, как оно работает на реальном оборудовании. Обычно они коррелируют таким образом, что вы можете подогнать кривую к своим точкам, и она будет явно напоминать одну из основных функций O .
Ответ №1:
Первый имеет временную сложность в наихудшем случае O (n 2) (все четные числа или, по крайней мере, количество четных чисел, масштабируемых по отношению к n, например, случайные целые числа). Эффект оператора if заключается в том, что он улучшает временную сложность в лучшем случае (все нечетные числа, поэтому оператор if всегда false и внутренний цикл никогда не выполняется), что равно O (n) . Сам внутренний цикл равен O (n), потому что число итераций все еще увеличивается на n .
Второй — O (n 2) в худшем случае, потому что он масштабируется с длиной массива и самим пропущенным числом. Если бы отсутствующий элемент всегда был равен 1, это было бы O (n), потому что внешний цикл выполнялся бы только один раз. Предполагая, что отсутствующий элемент является случайным, это O (n 2), потому что тогда оба цикла будут масштабироваться по длине.
Комментарии:
1. Вы уверены? Разве O(n * log n) не
for(i=0; i<n; i ) for(j=i; j<n; j )
является O(n * log n) ?2. @klutt Внутренний цикл выполняется время от
1
n
времени. Среднее число итераций, таким образом(1 n) / 2
, равно илиn/2 0.5
. Это масштабируется сn
, неlog(n)
.3. Но хммм, ну, когда я думаю об этом, это выглядит как пузырьковая сортировка
4. @klutt действительно, и сортировка пузырьков имеет временную сложность O (n ^ 2)
Ответ №2:
Эффект оператора if в первой функции заключается в том, что второе n в n ^ 2 применяется только для четных чисел. Эффект более короткого цикла заключается в том, что он как бы делит второе n пополам. Если вы посмотрите на худший случай, все четы, вы получите n * (n 1) / 2, из-за этого более короткого цикла, который доходит до O (n ^ 2). Средний случай по-прежнему будет O (n ^ 2), хотя и с коэффициентом, близким к 1/4.
Редактировать: после прочтения некоторых комментариев я думаю, что мне следует добавить наилучшие, наихудшие и средние сценарии:
В лучшем случае нет четных, O (n)
В худшем случае все четные, O (n * (n 1) / 2) = O (n ^ 2)
Средний случай, половина четных O (n * (n 1) / 4) (средний результат для половины четных соответствует половине посещений в худшем случае) = O (n ^ 2)
Разрыв во второй функции сокращает второе n в n ^ 2 примерно до половины, так что, опять же, все еще O (n ^ 2).
Если вы пытаетесь добиться большего, чем O (n ^ 2), я почти уверен, что вы можете решить обе проблемы с помощью O (n), если просто создадите дополнительный массив, но вы только спросили, каков эффект оператора if и какова нотация big O.
Комментарии:
1. Вы правы в отношении оператора if в первом, но внутренний цикл не проходит через весь массив
2. Он переходит из текущего местоположения i в 1, так что в среднем половина — это то, что я рассчитал. Вот почему я сказал, что в среднем коэффициент составляет 1/4, примерно в половине случаев он переходит во второй цикл и примерно на полпути в среднем по списку на протяжении всего цикла. Может быть, это не точно, но я не вижу, как это умножает эффективность на что-либо, кроме скаляра, в среднем случае, что означает, что все равно O (n ^ 2)
3. Циклы в форме
for(i=0; i<n; i ) for(j=i; j<n; j )
— O (n * log n), если я не ошибаюсь4. Ну, я не уверен. Я оставляю это. 🙂
5. Хорошо, рассматривая наихудший сценарий, он лишь немного хуже половины n ^ 2 (то есть n (n 1) / 2), поэтому 1/4, вероятно, неверно. Несмотря на это, я думал, что весь смысл был отчасти спорным, поскольку обе проблемы должны быть решаемы с помощью O (n), если вам разрешено использовать больше памяти.