Технически это алгоритм O (1) для «Hello World»?


117

Было бы это классифицировано как алгоритм O (1) для "Hello, World!" ??

public class Hello1
{
   public static void Main()
   {
      DateTime TwentyYearsLater = new DateTime(2035,01,01);
      while ( DateTime.Now < TwentyYearsLater )
      { 
          System.Console.WriteLine("It's still not time to print the hello ...");
      }
      System.Console.WriteLine("Hello, World!");
   }
}

Я подумываю использовать

DateTime TwentyYearsLater = new DateTime(2035,01,01);
while ( DateTime.Now < TwentyYearsLater )
{ 
   // ... 
}

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


15
Это не O(N)сложностьO(1)
Fabjan 02

19
@SubparWebDev Нет, вы не знаете, сколько раз он пройдет цикл, даже если вы знаете точную разницу во времени между запуском программы и указанной датой. Это зависит от того, насколько быстро работает компьютер, что еще на нем запущено, как ЦП планирует задачу и т. Д.
Servy

131
@Fabjan Нет N, от чего зависит алгоритм, поэтому на самом деле нельзя сказать, что это алгоритм O (N).
Servy

29
Технически ввода нет, так Nчто даже смысла нет. Но вы можете рассмотреть DateTime.Nowввод, который по-прежнему зависит от результата. Если вы можете принять реальное значение для DateTime.Now, тогда да, программа будет повторяться постоянное количество раз.
тыкают

43
Постановка задачи должна определять, что такое N.
Якуб Массад,

Ответы:


406

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

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


6
О чем O(max(1, 2035 - yearTheProgramIsStarted))?
Берги

19
@Bergi [На самом деле нет [( stackoverflow.com/questions/34048740/… ), вы не можете просто описать количество итераций цикла, основываясь только на времени, когда вы выполняете задание. И, конечно, вы комбинируете это с тем фактом, что системные часы могут быть изменены пользователем в любое время на любое время, которое они хотят, и т. Д., И у вас все еще нет хорошо сформированного ввода, который можно было бы точно связать с рядом операции, необходимые для производства продукции. Черт возьми, даже сам результат не согласован.
Servy

23
Можно утверждать, что состояние системы (включая часы) является частью входных данных для программы. В этом смысле вы можете использовать дату в качестве входного параметра, хотя и не явно. Хотя это странно.
Коннор Кларк

9
Чтобы быть более ясным, «неявный» ввод - это дельта между 1 января 2035 года и сегодняшним днем.
Коннор Кларк

6
@Hoten Но системное время не является фиксированным значением. Эта функция не то же самое, что просто принять DateTimeв качестве ввода время начала. Как я сказал ранее, системные часы могут меняться со временем . И снова вы не можете напрямую сопоставить квази-ввод, который вы описываете, с фиксированным выводом. Неизвестно количество операций, выполняемых для данного времени запуска или даже для двух программ, которые всегда получают разумное значение DateTime.Now, поэтому вы не можете связать эти две операции при изменении времени, потому что вы даже не можете связать их, когда время не меняется.
Servy,

88

Обозначение Big-O примерно означает «учитывая операцию над объемом работы N, сколько времени вычислений, пропорционального N, занимает алгоритм?». Например, сортировка массива размером N может занять N ^ 2, Nlog (N) и т. Д.

У него нет количества входных данных, чтобы действовать. Так что это не так O(anything).

Еще хуже; Технически это не алгоритм. Алгоритм - это метод вычисления значения математической функции. Математические функции - это отображение одного входа на выход. Поскольку это не требует ввода и ничего не возвращает, это не функция в математическом смысле. Из википедии:

Алгоритм - это эффективный метод, который можно выразить в ограниченном пространстве и времени и на четко определенном формальном языке для вычисления функции. Начиная с начального состояния и начального ввода (возможно, пустого), инструкции описывают вычисление, которое при выполнении проходит через конечное число четко определенных последовательных состояний, в конечном итоге производя «вывод» и завершаясь в конечном конечном состоянии.

Технически это и есть система управления. Из википедии;

Система управления - это устройство или набор устройств, которые управляют, управляют, направляют или регулируют поведение других устройств или систем.

Для людей, которые хотят получить более подробный ответ о разнице между математическими функциями и алгоритмами, а также о более мощных способностях компьютеров выполнять побочные действия, такие как вывод на консоль, отображение графики или управление роботами, имеют прочтите этот документ на Сильная гипотеза Черча-Тьюринга

Аннотация

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

Принятию взаимодействия в качестве новой парадигмы препятствует сильный тезис Черча-Тьюринга (SCT), широко распространенное мнение, что машины Тьюринга (TM) охватывают все вычисления, поэтому модели вычислений, более выразительные, чем TM, невозможны. В этой статье мы показываем, что SCT переосмысливает исходный тезис Черча-Тьюринга (CTT) таким образом, которого Тьюринг никогда не предполагал; его общепринятая эквивалентность оригиналу - миф. Мы выявляем и анализируем исторические причины широко распространенной веры в SCT. Только признав, что это ложно, мы можем начать принимать взаимодействие как альтернативную парадигму вычислений.


Это не обязательно должна быть последовательность. Это всего лишь ввод некоторых данных, а нотация Ландау описывает время работы по отношению к некоторой метрике (ам) этих данных - обычно что-то связано с размером.
Берги

@Bergi - да, ты понял! На самом деле, это просто приближение, но да - если вы можете измерить объем работы, которую нужно сделать, и количество шагов, которые нужно сделать, чтобы добраться до нее, большое-о отражает соотношение этих двух показателей. Ближе?
Стив Купер,

@kapep - это не чистая функция, потому что это метод void, но если мы посчитаем вывод консоли, он все равно будет случайным; он может выводить любое из {"Hello, World!", "Еще не время печатать привет ... \ nHello, World!", "Еще не время печатать привет ... Еще не время печатать привет ... \ nПривет, мир! ", ...}
Стив Купер

1
Печать в стандартный вывод не выводится?
rpax

4
@rpax Не математически, нет. Функция - это неизменный перевод входов в выходы; например, «квадрат» - это функция, которая всегда возвращает 9, если вы вводите 3. Метод C # является математической функцией только в том случае, если вызов с одинаковыми параметрами всегда дает одно и то же возвращаемое значение. В противном случае - если у него есть побочные эффекты, такие как запись в консоль, отображение графики, выделение памяти - это не математические функции. (Я добавлю ссылку на свой ответ, в которой он подробно описан :))
Стив Купер

41

Нет, ваш код имеет временную сложность O(2^|<DeltaTime>|),

Для правильного кодирования текущего времени.
Пожалуйста, позвольте мне сначала извиниться за мой английский.

Что такое Big O и как работает в CS

Обозначение Big O не используется для привязки ввода программы к времени ее выполнения .
Обозначение Big O - это способ выразить асимптотическое отношение двух величин без строгости. .

В случае анализа алгоритма эти две величины не являются входными данными (для которых нужно сначала иметь функцию «измерения») и временем работы.
Это длина кода экземпляра задачи 1 и интересующий показатель.

Обычно используемые метрики:

  1. Количество шагов, необходимых для завершения алгоритма в данной модели вычислений.
  2. Пространство, необходимое для модели вычислений, если такая концепция существует.

Косвенно предполагаются ТМ в качестве модели , так что первая точка переводит к числу применений перехода 2 функции , т.е. «ступеньки», а второй переводит число различных ленточных клеток , написанных по меньшей мере , один раз .

Также часто неявно предполагается, что мы можем использовать полиномиально связанную кодировку вместо исходной, например, функция, которая выполняет поиск в массиве от начала до конца, имеет O(n)сложность, несмотря на то, что кодирование экземпляра такого массива должно иметь длину n*b+(n-1)где b- (постоянное) количество символов каждого элемента. Это потому, что bсчитается константой вычислительной модели, поэтому выражение выше иn асимптотически одинаковы.

Это также объясняет, почему алгоритм, подобный Trial Division, является экспоненциальным алгоритмом, несмотря на то, что он по сути for(i=2; i<=sqr(N); i++)похож на алгоритм 3. .

Смотрите это .

Это также означает, что большая нотация O может использовать столько параметров, сколько может потребоваться для описания проблемы, разве нет ничего необычного в том, чтобы иметь k параметра для некоторых алгоритмов.

Так что это не речь о «вводе» или о том, что «ввода нет».

Изучите кейс сейчас

Обозначение Big O не ставит под сомнение ваш алгоритм, оно просто предполагает, что вы знаете, что делаете. По сути, это инструмент, применимый везде, даже для алгоритмов, которые могут быть намеренно хитрыми (например, ваш).

Чтобы решить вашу проблему, вы использовали текущую дату и дату в будущем, поэтому они каким-то образом должны быть частью проблемы; Проще говоря: они являются частью проблемы.

В частности, экземпляр:

<DeltaTime>

Где <>означает любое, непатологическое, кодирование по выбору.

Ниже приведены очень важные пояснения.

Таким образом, ваше время большой сложности O справедливо O(2^|<DeltaTime>|), потому что вы выполняете количество итераций, которые зависят от значения текущего времени. Нет смысла вводить другие числовые константы, поскольку асимптотическая запись полезна, поскольку она исключает константы (поэтому, например, использование O(10^|<DeltaTime>|*any_time_unit)бессмысленно).

Где сложная часть

Мы сделали одно важное предположение выше: что модель вычислений reificates 5 раза, а время я имею в виду физическое времени (реальный?). В стандартной вычислительной модели такой концепции нет, ТМ не знает времени, мы связываем время с количеством шагов, потому что так работает наша реальность 4 .

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

Чтобы понять это, следует отметить, что ничто не мешает Framework использовать ложное время, которое выполняется в два, пять, десять раз быстрее физического времени. Таким образом, ваш код будет выполняться «половину», «одну пятую», «одну десятую» «времени».

Это отражение важно для выбора кодировки <DeltaTime>, это, по сути, сокращенный способ записи <(CurrentTime, TimeInFuture)>. Поскольку время изначально не существует, кодирование CurrentTime вполне может быть словом « Сейчас» (или любым другим вариантом), за день до этого может быть закодировано как « Вчера» , нарушив предположение о том, что длина кодирования увеличивается с увеличением физического времени. идет вперед (а DeltaTime уменьшается)

Мы должны правильно моделировать время в нашей вычислительной модели, чтобы делать что-то полезное.

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

Ваше замешательство, если таковое имеется, возникает из-за того, что слово время в фразах "Какова его временная сложность?" и "Сколько времени это займет?" означает очень разные вещи

Увы, в терминологии используются те же слова, но вы можете попробовать использовать в уме «сложность шагов» и снова задать себе свой вопрос, я надеюсь, что это поможет вам понять, что на самом деле ответ ^ _ ^


1 Это также объясняет необходимость асимптотического подхода, поскольку каждый экземпляр имеет разную, но не произвольную длину.
2 Надеюсь, я использую здесь правильный английский термин.
3 Также поэтому мы часто находим log(log(n))термины в математике.
4 То есть шаг должен занимать некоторый конечный, но не нулевой, и не связанный интервал времени.
5 Это означает, что вычислительный режим как знание физического времени в нем, то есть может выразить его с его терминами. Аналогия заключается в том, как универсальные шаблоны работают в платформе .NET.


3
«Значит, ваше время бега большого О - это просто» .. Я уверен, вы имели в виду «большую сложность О»? Кроме того, мы все еще можем просто называть deltaTime нашим «n» правильно ... так что вы говорите O (2 ^ N) что-то вроде сложности алгоритма Фибоначчи. Как вы попали в "2 ^"?
Росс

@Ross, спасибо за точку. Пришел с 2 по привычке для работы с двоичными числами. Дело в том, что шаги линейны с длиной представления числа. Фактическая база на самом деле не важна и зависит от конкретной кодировки. Он псевдолинейный .
Yuni Mj

Извините, но не могли бы вы уточнить в своем ответе, как вы пришли к выводу, что это сложность O(2^n)? Новичкам непонятно.
Артуро Торрес Санчес

2
@YuniMj Хотя ваши рассуждения технически не так, я думаю , что, настаивая на том , чтобы измерить размер от DeltaTimeвместо его стоимости , вы просто добавить дополнительную путаницу. Например, но это рассуждение не имеет оптимального алгоритма сортировки с временной сложностью $ O (n \ cdot log n) $. Зачем? Потому что вы можете сортировать только конечное количество различимых объектов, и в этом случае вы всегда можете использовать сортировку по корзине для сортировки в $ O (n) $. Или размер вашего объекта неограничен, и в этом случае $ O (n \ cdot log n) $ не будет выполняться, поскольку одно сравнение больше не будет иметь постоянного времени ...
fgp 04


29

Хотя здесь есть несколько отличных ответов, позвольте мне немного перефразировать их.

Нотация Big-O существует для описания функций . Применительно к анализу алгоритмов это требует от нас сначала определения некоторых характеристик этого алгоритма в терминах функции . Обычный выбор - рассматривать количество шагов как функцию от размера ввода . Как отмечалось в других ответах, создание такой функции в вашем случае кажется странным, потому что нет четко определенного «ввода». Но мы все еще можем попробовать это сделать:

  • Мы можем рассматривать ваш алгоритм как постоянную функцию, которая принимает любой ввод любого размера, игнорирует его, ожидает фиксированное количество времени и завершает работу. В этом случае его время выполнения равно f (n) = const , и это алгоритм времени O (1). Это то, что вы ожидали услышать, верно? Да, технически это O (1) -алгоритм .
  • Мы можем рассматривать TwentyYearsLaterпараметр как интересующий параметр типа «размер ввода». В этом случае время выполнения равно f (n) = (nx), где x - «текущее время» в момент вызова. Если смотреть с этой точки зрения, это алгоритм за время O (n). Ожидайте этого контраргумента всякий раз, когда вы собираетесь показывать свой технически O (1) -алгоритм другим людям.
  • О, но подождите, если k =TwentyYearsLater является входом, то его размер n фактически равен количеству битов, необходимых для его представления, то есть n = log (k) . Следовательно, зависимость между размером ввода n и временем выполнения f (n) = 2 ^ n - x . Похоже, ваш алгоритм только что стал экспоненциально медленным! Тьфу.
  • Другой вход в программу - это, по сути, поток ответов, данных ОС на последовательность DateTime.Nowвызовов в цикле. На самом деле мы можем представить, что вся эта последовательность предоставляется в качестве входных данных в момент запуска программы. Затем можно считать, что время выполнения зависит от свойства этой последовательности, а именно от ее длины до первого TwentyYearsLaterэлемента. В этом случае время выполнения снова f (n) = n, а алгоритм - O (n) .

Но опять же, в своем вопросе вы даже не сказали, что вас интересует среда выполнения. Что, если вы имели в виду использование памяти? В зависимости от того, как вы моделируете ситуацию, вы можете сказать, что алгоритм является O (1) -памятью или, возможно, O (n) -памятью (если реализацияDateTime.Now требует то отслеживать всю последовательность вызовов).

И если вашей целью было придумать что-то абсурдное, почему бы вам не пойти ва-банк и не сказать, что вас интересует, как размер кода алгоритма в пикселях на экране зависит от выбранного уровня масштабирования. Это может быть что-то вроде f (zoom) = 1 / zoom, и вы можете с гордостью заявить, что ваш алгоритм имеет размер O (1 / n) -пикселей!


+1. Я считаю, что «поток ответов, предоставляемых ОС на последовательность DateTime.Nowвызовов», является реальным вводом здесь. Но я думаю, что вывод должен заключаться не в том, что это O (n), а в O (k), где k - длина до первого TwentyYearsLaterэлемента
половина 03

7
На данный момент это лучший ответ - для того, чтобы Big O был значимым, вы должны применить математическую семантику / предположения к физической реализации (по сути, определяя математическую модель для программы с осмысленным определением «ввода»). В этом смысле сложность «программы» зависит от применяемой вами семантики - если вы предположите, что N - это разница во времени, которая линейно масштабируется с количеством операций, это O (n). Если вы предполагаете фиксированное количество операций в результате фиксированного периода времени, это O (1).
Ant P

21

Я должен немного не согласиться с Серви. В эту программу есть вход, даже если он не очевиден, и это время системы. Возможно, это формальность, которую вы не планировали, но ваша TwentyYearsFromNowпеременная сейчас не через двадцать лет от времени системы , а статически присвоена 1 января 2035 года.

Итак, если вы возьмете этот код и выполните его на машине с системным временем 1 января 1970 года, это займет 65 лет, независимо от того, насколько быстро компьютер (могут быть некоторые отклонения, если его часы неисправны. ). Если вы возьмете этот код и выполните его на машине с системным временем 2 января 2035 года, он завершится почти мгновенно.

Я бы сказал, что ваш вклад, nесть January 1st, 2035 - DateTime.Now, и это O (n).

Кроме того, существует проблема количества операций. Некоторые люди отметили, что более быстрые компьютеры будут быстрее выполнять цикл, вызывая больше операций, но это не имеет значения. При работе с нотацией большого O мы не учитываем скорость процессора или точное количество операций. Если вы возьмете этот алгоритм и запустите его на компьютере, а затем снова запустите его, но в 10 раз дольше на том же компьютере, можно ожидать, что количество операций вырастет в тот же раз в 10 раз.

Что касается этого:

Я подумываю использовать фрагмент кода [отредактированный код] в качестве цикла занятости и вставлять в шутку всякий раз, когда кто-то запрашивает алгоритм определенной сложности. Было бы это правильно?

Нет, не совсем. Другие ответы охватили это, поэтому я просто хотел упомянуть об этом. Как правило, годы выполнения нельзя соотносить с какой-либо нотацией с большой буквы. Например. Невозможно сказать, что 20 лет казни = O (n ^ 87) или что-то еще в этом отношении. Даже в указанном вами алгоритме я мог бы изменить TwentyYearsFromNowгод на 20110, 75699436 или 123456789, а большой O по-прежнему будет O (n).


7
Время не является входом в функцию, это постоянно меняющееся состояние , которое наблюдается на протяжении всего выполнения метода. Системные часы можно изменить даже во время работы функции . Чтобы Big O имел смысл, вам также нужно, чтобы каждый вход соответствовал 1-1 выходному значению, а также ряд операций, необходимых для его вычисления. Для этой операции выходной сигнал даже не соответствует для того же входа (на самом деле она меняется дико ), в дополнение к числу операций , выполняемых также различной дико.
Обслуживание

When working with big-O notation, we don't consider the speed of the processor or the exact number of operations.Это ложное заявление. Практически любая разумная операция, для которой вы попытаетесь вычислить значение Big O, не изменит количество операций, выполняемых в зависимости от оборудования, но эта операция меняет . Big O - это просто способ соотнести количество операций с размером ввода. Для большинства операций, не зависящих от системного оборудования. В данном случае это не так .
Servy

If you took this algorithm and ran it on a computer, and then ran it again but for 10x longer on the same computer, you would expect the number of operations to grow by the same factor of 10x.Это тоже ложное заявление. Окружение не обязательно будет изменять количество операций в цикле линейно. Например, на компьютере могут быть другие программы, которые используют больше или меньше процессорного времени в разные моменты времени, постоянно меняя время, отведенное этому приложению.
Servy,

Я с @Servy по этому поводу, но по несколько другой причине. Основная функция не принимает параметров и не возвращает ввод. Это функция nil => nil, если хотите. Неважно, сколько времени, он все равно ничего не возвращает.
Стив Купер,

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

13

Анализ Big-O имеет дело с объемом обработки, поскольку объем обрабатываемых данных неограниченно увеличивается.

Здесь вы действительно имеете дело только с одним объектом фиксированного размера. Таким образом, применение анализа с большим О сильно зависит (в первую очередь?) От того, как вы определяете свои термины.

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

Однако количество основных операций обычно довольно точно соответствует затраченному времени, поэтому нетрудно рассматривать их как синонимы. К сожалению, однако, мы все еще застряли на другой части: объем обрабатываемых данных неограниченно увеличивается. В этом случае никакая фиксированная задержка, которую вы можете наложить, действительно не сработает. Чтобы приравнять O (1) к O (N), вам придется ввести бесконечную задержку, чтобы любой фиксированный объем данных печатался бесконечно, как и бесконечное количество данных.


10

big-O относительно чего?

Кажется, вы интуитивно понимаете, что twentyYearsLaterэто «вход». Если вы действительно написали свою функцию как

void helloWorld(int years) {
   // ...
}

Это будет O (N), где N = лет (или просто скажите O(years) ).

Я бы сказал, что ваш алгоритм O (N) относительно любого числа, которое вы пишете в строке кода, начиная с twentyYearsLater =. Но люди обычно не считают числа в исходном коде исходными данными. Они могут рассматривать ввод командной строки как ввод или ввод сигнатуры функции как ввод, но, скорее всего, не сам исходный код. Вот о чем вы спорите со своим другом - это «вход»? Вы настраиваете свой код таким образом, чтобы он интуитивно казался вводом, и вы определенно можете спросить его большое время работы O относительно числа N в строке 6 вашей программы, но если вы используете такой выбор не по умолчанию в качестве входных данных вам действительно нужно четко указать на это.

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

Аналогичный вопрос - каково время выполнения:

void sortArrayOfSizeTenMillion(int[] array)

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


Жесткое кодирование ввода не означает, что ввод исчезает. Ни в одном случае быстрая сортировка и пузырьковая сортировка не имеют временной сложности O (1). bigocheatsheet.com
Тео Бринкман

@TheoBrinkman Если вы хотите быть техническим, то в модели машины Тьюринга кодирование того, что вы думаете о вводе, в саму машину Тьюринга делает его, по определению, не вводом. Затем машина Тьюринга будет работать с постоянным временем, независимо от того, какие входные данные она имеет. В каком-то смысле он не выполняет «пузырьковую сортировку», поскольку он ничего не сортирует, а работает с собственным представлением, однако в нетехнических терминах, конечно, вы можете описать алгоритм как пузырьковую сортировку.
djechlin

В равной степени «нетехническими терминами» вы могли бы описать рассматриваемый алгоритм как подвесной мост.
Тео Бринкман,

@TheoBrinkman нет, ты не мог. Это ни для кого не имеет смысла.
djechlin

Это имеет столько же смысла, сколько описывать это как пузырьковую сортировку O (1).
Тео Бринкман,

8

Этот «алгоритм» правильно описывается как O (1) или постоянное время. Утверждалось, что в эту программу нет ввода, следовательно, нет N для анализа с точки зрения Big Oh. Я не согласен с тем, что ввода нет. Когда он компилируется в исполняемый файл и вызывается, пользователь может указать любой ввод произвольной длины. Эта входная длина - N.

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


Но количество операций не обязательно одинаково, даже при одинаковом времени запуска и оборудовании. Кроме того, претендовать на O (1) алгоритм вывода должен был бы быть всегда постоянным, а это не так , она будет варьироваться дико на основе времени начала и аппаратного обеспечения. Он также очень легко может быть бесконечным, что, конечно, непостоянно. Нет никакой связи между введенными вами данными и количеством выполненных операций. Это не постоянно, это просто не определено. Вы не можете назвать конечное число и знать, что операций всегда будет меньше.
Servy

Максимальное реальное время, которое на это потребуется, - 20 лет. Если мы начнем это в будущем, да, это займет больше времени. Предположим, что существует конечная нижняя граница количества времени, которое занимает итерация цикла, и что мы работаем на последовательном оборудовании. Затем я могу ограничить количество запусков цикла, что означает, что все вычисления могут быть ограничены постоянной функцией, независимо от размера игнорируемого ввода.
waldol1

Let's suppose that there is a finite lower bound on the amount of time a loop iteration takesЭто ложное предположение. Программа может работать вечно. Все, что мне нужно сделать, это установить мои системные часы на 50 лет, запустить их, и они никогда не закончатся. Или я мог продолжать переводить часы назад быстрее, чем они движутся вперед, или запускать их с неопределенной точки в прошлом . Вы просто не можете предположить, что существует нижняя граница продолжительности работы программы; он может работать вечно. Но даже если мы примем ваше (ложное) предположение за истинное, вы все равно не сможете соотнести количество выполненных операций с вводом.
Servy

Одна итерация цикла занимает конечное время. Возможно, он будет выполняться бесконечное количество раз, но каждый из них должен быть примерно постоянным. Я не вижу проблем с этим предположением.
waldol1

По этой [совершенно неверной] логике каждый отдельный алгоритм всегда равен O (1), потому что каждая отдельная операция всегда постоянна. Вы просто демонстрируете, что даже не знаете, что такое Big O. Это инструмент для (в контексте) описания взаимосвязи между размером входных данных и количеством выполняемых соответствующих операций. O (1) означает, что существует постоянное количество операций, выполняемых независимо от ввода. Здесь не существует постоянного количества операций, выполняемых независимо от ввода, существует потенциально бесконечное количество выполняемых операций, бесконечное! = Константа.
Обслуживание

6

Одна вещь, которую я удивлен, еще не упомянута: нотация большого О - это верхняя граница!

Проблема, которую все заметили, заключается в том, что нет N, описывающего входные данные для алгоритма, поэтому не с чем проводить большой анализ. Однако это легко смягчается некоторыми простыми хитростями, такими как принятие int nи печать времени «Hello World» n. Это позволит обойти эту жалобу и вернуться к реальному вопросу о том, как DateTimeработает это чудовище.

Нет никакой гарантии, что цикл while когда-либо завершится. Нам нравится думать , что это должно в какой - то момент, но считают , что , DateTime.nowвозвращает дату и время . На самом деле нет никакой гарантии, что это монотонное увеличение. Вполне возможно, что какая-то патологически обученная обезьяна постоянно меняет системную дату и время до 12:00:00 UTC 21 октября 2015 года, пока кто-то не даст обезьяне автоматическую обувь и ховерборд. Этот цикл может работать бесконечно долго!

Когда вы на самом деле копаетесь в математическом определении нотации большого О, это верхняя граница. Они демонстрируют наихудший сценарий, каким бы маловероятным он ни был. Наихудший сценарий * здесь - это бесконечное время выполнения, поэтому мы вынуждены заявить, что не существует нотации большого O для описания сложности выполнения этого алгоритма. Его не существует, как не существует 1/0.

* Изменить: из моего обсуждения с KT, не всегда верно предполагать, что сценарий, который мы моделируем с обозначением большого O, является наихудшим. В большинстве случаев, если человек не может указать, какой случай мы используем, он намеревался изучить худший вариант. Однако вы можете провести большой анализ сложности в среде исполнения в лучшем случае.


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

Сложность @KY в лучшем случае называется small-o, а ожидаемая сложность - big-theta. big-o - всегда сложность наихудшего случая по математическому определению.
Cort Ammon

Нет, здесь вы ошибаетесь. Еще раз проверьте определения.
КТ.

@KT Ладно, перепроверяю. Вы их тоже перепроверяете. en.wikipedia.org/wiki/Big_O_notation Это относится к семейству нотаций Бахмана – Ландау
Корт Аммон

Я полагаю, вы могли бы сделать что-то безумное, например, взять функцию fи объявить функцию gтакой же, как f, но с ограниченным доменом, чтобы включать только fлучший случай, а затем сделать что-то безумное g, но это начинает казаться вырожденным, когда вы это делаете который.
Cort Ammon

5

Сложность используется для измерения вычислительной «лошадиных сил» в терминах времени / пространства. Обозначение Big O используется для сравнения, какие задачи являются «вычислимыми» или «невычислимыми», а также для сравнения, какие решения - алгоритмы - лучше других. Таким образом, вы можете разделить любой алгоритм на две категории: те, которые могут быть решены за полиномиальное время, и те, которые не могут.

Проблемы, подобные Решету Эратостена, имеют размер O (n ^ exp) и, следовательно, разрешимы для малых значений n. Они вычислимы, но не за полиномиальное время (NP), и поэтому, когда их спрашивают, является ли данное число простым или нет, ответ зависит от величины такого числа. Более того, сложность не зависит от оборудования, поэтому более быстрые компьютеры ничего не меняют ...

Hello World - это не алгоритм, и поэтому бессмысленно пытаться определить его сложность - а это не так. Простой алгоритм может выглядеть примерно так: по случайному числу определить, четное оно или нечетное. Имеет ли значение, что данное число состоит из 500 цифр? Нет, потому что вам просто нужно проверить, четная или нечетная последняя цифра. Более сложный алгоритм заключался бы в том, чтобы определить, делится ли данное число равномерно на 3. Хотя некоторые числа «легко» вычислить, другие - «сложно», и это из-за их величины: сравните время, необходимое для определения напоминания между число из одной цифры и другое из 500 цифр.

Более сложным случаем было бы декодирование текста. У вас есть очевидный случайный массив символов, который, как вы также знаете, передает сообщение тем, у кого есть ключ дешифрования. Допустим, отправитель использовал ключ слева, и ваш Hello World прочитал бы: Gwkki Qieks. Решение «большой молоток, без мозгов» будет производить все комбинации для этих букв: от Aaaa до Zzzz, а затем искать в словаре слов, чтобы определить, какие слова являются действительными, и разделять две общие буквы в зашифрованном виде (i, k) в та же позиция. Эта функция преобразования - это то, что измеряет Big O!


4

Большинство людей упускают две очень важные вещи.

  1. Программа делает у входа. Это жестко заданная дата / время, с которыми сравнивается системное время. Входные данные находятся под контролем человека, выполняющего алгоритм, а системное время - нет. Единственное, что может контролировать человек, запускающий эту программу, - это дату и время, которые он жестко запрограммировал для сравнения.

  2. Программа варьируется в зависимости от входного значения , но не от размера входного набора , что и имеет отношение к нотации большого O.

Следовательно, это неопределенное значение, и лучшая нотация «большого О» для этой программы, вероятно, будет O (ноль) или, возможно, O (NaN).


1
(2) категорически неверно. Обычно учитывается «длина входа». Для списка или массива объектов фиксированного размера (например, целых чисел) это действительно будет размер набора. Чтобы разложить на множитель число, подобное 1395195191600333, это будет длина его двоичного (или десятичного и т. Д.) Представления, то есть количество цифр. Как было сказано, ваше определение в (2) запрещает использование big-O для обсуждения сложности "findPrimeFactors (int num)", против чего будет возражать большинство криптографов.
djechlin

4

Все правильно отметили, что вы не определяете N , но при наиболее разумной интерпретации ответ будет отрицательным. Если N - длина печатаемой строки и «привет, мир!» это просто пример, как мы могли бы вывести из описания этого алгоритма «для hello, world!», тогда алгоритм будет O ( N ), потому что у вас может быть строка вывода, печать которой занимает тридцать, сорок или пятьдесят лет, и вы Добавляем к этому только постоянное время. O ( kN + c ) ∈ O ( N ).

Приложение:

К моему удивлению, это кто-то оспаривает. Напомним определения большого O и большого. Предположим, у нас есть алгоритм, который ожидает постоянное количество времени c, а затем распечатывает сообщение длины N за линейное время. (Это обобщение исходного образца кода.) Предположим произвольно, что мы ждем двадцать лет, чтобы начать печать, и что печать триллиона символов занимает еще двадцать лет. Пусть, например, c = 20 и k = 10¹², но подойдут любые положительные действительные числа. Это показатель d = c / k (в данном случае 2 × 10⁻¹¹) лет на символ, поэтому время выполнения f ( N ) асимптотически cdN + лет. Когда N > k , dN = c / k N > c . Следовательно, dN < dN + c = f ( N ) <2 dN для всех N > k и f ( N ) ∈ Θ ( N ). QED


Где у нас N = 13.
djechlin 03 дек.15,

Но он не просто печатает «Hello world», он печатает неизвестное количество строк «Еще не время». Кроме того, Big O на самом деле не используется для сравнения размера ввода с размером вывода, он обычно используется для сравнения размера ввода с количеством операций или объемом используемой памяти.
Servy

@Servy Это постоянная память, но я неявно ограничивал время выполнения. Размер вывода также составляет O ( N ) для произвольной строки: строка, которую мы печатаем, когда пришло время, может быть сколь угодно большой, даже по сравнению с сообщениями подождите двадцать лет.
Дэвислор

@Servy Я отредактировал, чтобы уточнить, что нет, N - это не размер вывода. Не знаю, как я произвел такое впечатление, но устраню двусмысленность.
Дэвислор

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

4

Я думаю , что люди становятся все скидываются , потому что код не выглядеть как традиционный алгоритм. Вот перевод кода, который более правильно сформирован, но остается верным духу вопроса OP.

void TrolloWorld(long currentUnixTime, long loopsPerMs){
    long laterUnixTime = 2051222400000;  //unix time of 01/01/2035, 00:00:00
    long numLoops = (laterUnixTime-currentUnixTime)*loopsPerMs;

    for (long i=0; i<numLoops; i++){
        print ("It's still not time to print the hello …");
    }
    print("Hello, World!");
}

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

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


2

На данный момент да

Этот алгоритм имеет неявный ввод, а именно время запуска программы. Время выполнения будет линейно изменяться 1 в зависимости от того, когда оно было запущено. В течение 2035 года и позже цикл while немедленно завершается, а программа завершается после постоянных операций 2 . Таким образом, можно сказать, что время выполнения равно O(max(2035 - start year, 1))3 . Но поскольку наш начальный год имеет минимальное значение, выполнение алгоритма никогда не займет более 20 лет (т. Е. Постоянное значение).

Вы можете сделать свой алгоритм более соответствующим вашему намерению, определив DateTime TwentyYearsLater = DateTime.Now + new TimeSpan(365*20,0,0,0);4

1 Это справедливо для более технического смысла времени выполнения, измеряемого как количество операций, поскольку существует максимальное количество операций в единицу времени.
2 Предполагая, что выборка DateTime.Now- это постоянная операция, что разумно.
3 Я несколько злоупотребляю здесь большой нотацией O, потому что это убывающая функция по отношению к start year, но мы могли бы легко исправить это, выразив это в терминах years prior to 2035.
4 Тогда алгоритм больше не зависит от неявного ввода времени начала, но это не имеет значения.


1

Я бы сказал, что это O (n). используя http://www.cforcoding.com/2009/07/plain-english-explanation-of-big-o.html в качестве ссылки.

Что такое Big O?

Обозначение Big O стремится описать относительную сложность алгоритма путем снижения скорости роста до ключевых факторов, когда ключевой фактор стремится к бесконечности.

и

Лучший пример Big-O, который я могу придумать, - это арифметика. В школе мы изучили следующие основные арифметические операции:

добавление; вычитание; умножение; и деление. Каждый из них - это операция или проблема. Метод их решения называется алгоритмом.

Для вашего примера,

с учетом ввода n = 20 (с единицами лет).

алгоритм представляет собой математическую функцию f (). где f () ожидает n лет с промежуточными строками отладки. Коэффициент масштабирования равен 1. f () можно уменьшить / или увеличить, изменив этот коэффициент масштабирования.

в этом случае выход также равен 20 (изменение входа изменяет выход линейно).

по сути функция

f(n) = n*1 = n
    if  n = 20, then 
f(20) = 20 
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.