Немного смущен с Big O - PullRequest
1 голос
/ 02 июня 2011

Итак, у меня есть быстрый вопрос о том, как проверить большой O функции.

например: алгоритм быстрой сортировки, сортирующий массив из 5000000 элементов, дает интервал времени 0,008524 секунды, а выполнение того же алгоритма с элементом 1000000 дает 0,017909. Как бы я проверил большую O, если бы моя быстрая сортировка была / не большой O из n * log (n) ??

Что, я думаю, я понимаю: n увеличилось на 2, поэтому время выполнения должно увеличиться на 2 * log (2)?

f (n) = 0,008524 -> n log (n)

f (2n) = 0,017909 -> 2n * log (2n)

Ответы [ 5 ]

2 голосов
/ 02 июня 2011

Обозначение Big-O асимптотическое.Это означает, что он применяется только в пределе, когда n становится большим.

Есть много причин, по которым сортировка с 50 и 100 элементами может не отслеживать O (n log n), вероятными кандидатами являются эффекты кэширования.Если вы попробуете 100 000 против 200 000 против 1 миллиона, вы, вероятно, обнаружите, что они отслеживаются немного лучше.

Другая возможность состоит в том, что большинство реализаций быстрой сортировки имеют в среднем только O (n log n);некоторые входы займут больше времени.Вероятность встретить такой патологический вклад выше для 50 элементов, чем для 100 000.

В конечном счете, вы не «проверяете» время выполнения big-O;Вы доказываете это на основе того, что делает алгоритм.

1 голос
/ 02 июня 2011

Необходимо помнить пару моментов: в первую очередь, при изменении размеров аппаратное обеспечение (по крайней мере, на обычном компьютере) также будет оказывать влияние. В частности, когда ваши данные становятся слишком большими, чтобы поместиться в определенный размер кэша, вы можете ожидать существенного скачка во времени выполнения, который полностью не зависит от рассматриваемого алгоритма.

Чтобы получить хорошее представление о самом алгоритме, вы должны (вероятно) начать со сравнения с каким-то алгоритмом с действительно очевидной сложностью, но работающим с тем же размером данных. Для одной очевидной возможности, время, сколько требуется, чтобы заполнить ваш массив случайными числами. По крайней мере, если предположить достаточно типичный PRNG, он, безусловно, должен быть линейным.

Затем оцените ваш алгоритм и посмотрите, как он относительно меняется на линейный алгоритм для тех же размеров. Например, вы можете использовать такой код:

#include <vector>
#include <algorithm>
#include <iostream>
#include <time.h>
#include <string>
#include <iomanip>

class timer { 
    clock_t begin;
    std::ostream &os;
    std::string d;
public:
    timer(std::string const &delim = "\n", std::ostream &rep=std::cout) 
        : os(rep), begin(clock()), d(delim)
    {}
    ~timer() { os << double(clock()-begin)/CLOCKS_PER_SEC << d; }
};

int main() { 
    static const unsigned int meg = 1024 * 1024;

    std::cout << std::setw(10) << "Size" << "\tfill\tsort\n";
    for (unsigned size=10000; size <512*meg; size *= 2) {
        std::vector<int> numbers(size);
        std::cout << std::setw(10) << size << "\t";
        { 
            timer fill_time("\t");
            std::fill_n(numbers.begin(), size, 0);
            for (int i=0; i<size; i++)
                numbers[i] = rand();
        }
        {
            timer sort_time;
            std::sort(numbers.begin(), numbers.end());
        }
    }
    return 0;
}

Если я нарисую время и время для сортировки, я получу что-то вроде этого:

enter image description here

Поскольку наши размеры экспоненциальные, наш линейный алгоритм показывает (приблизительно) экспоненциальную кривую. Время сортировки, очевидно, все еще увеличивается (несколько) быстрее.

Редактировать: Справедливости ради, я, вероятно, должен добавить, что log (N) растет настолько медленно, что почти для любого практического объема данных он вносит очень небольшой вклад. Для большинства практических целей вы можете просто считать быструю сортировку (например) линейной по размеру, просто с несколько большим постоянным коэффициентом, чем заполнение памяти. Линейное увеличение размера и отображение результатов делает это более очевидным:

enter image description here

Если вы посмотрите внимательно, вы можете увидеть верхнюю линию, показывающую только небольшую восходящую кривую от коэффициента «log (N)». С другой стороны, я не уверен, что вообще заметил бы искривление, если бы не знал, что оно должно .

1 голос
/ 02 июня 2011

Нотация Big-O обычно не связана с временем выполнения в секундах. Это касается количества операций , которые были выполнены алгоритмом. Единственный способ установить это - взглянуть на код функции.

Не только на время выполнения будут влиять термины младшего разряда (помните, что нотация big-O касается только термина старшего порядка ), но и такие вещи, как накладные расходы при запуске программы, кэш эффективность и отраслевой прогноз.

Сказав все это, возможно, что ни один из этих других эффектов не будет значительным в вашем случае. В этом случае, если удвоить n , можно ожидать увеличения времени выполнения с knlog (n) до k.2n.log (2n) = k (2n.log (2) + 2n.log (n)) .

0 голосов
/ 02 июня 2011

Этот код, который вы просматриваете, зависит от того, сколько времени потребуется методу для прохождения 50 элементов по сравнению с 100, а не от самой синхронизации. Как если бы я перебирал массив, это было бы линейным временем O (n), потому что массив должен проходить через каждый индекс до конца. N представляет размер массива. Кроме того, обозначение Big O предназначено для общей схемы вещей в долгосрочной перспективе. Если бы вы усреднили время для 50, 100, 1000, 100000, 1000000, вы бы увидели на Среднее , что оно будет иметь O (nlog (n)).

0 голосов
/ 02 июня 2011

Двух точек данных на самом деле недостаточно.

Однако 2 * n * log (2 * n) = 2 * n * (log (n) + log (2)), так что вы можетеобратите внимание, что множитель должен быть примерно 2 * log (2), когда вы удваиваете размер.Это выглядит правдоподобно для номеров, которые вы дали.Вы должны добавить еще несколько пунктов и дважды проверить.

Обратите внимание, что в вашем времени может быть некоторый постоянный термин, по крайней мере, если вы включите в него запуск программы - это может быть значительным.Если, с другой стороны, вы только время фазы сортировки без запуска, это более точно.Вы должны повторить процесс для многих перестановок входных данных, чтобы убедиться, что вы получаете репрезентативный набор значений.

...