Есть ли O (1 / n) алгоритмы?
Или что-нибудь еще, что меньше, чем O (1)?
Есть ли O (1 / n) алгоритмы?
Или что-нибудь еще, что меньше, чем O (1)?
Ответы:
Этот вопрос не так глуп, как может показаться. По крайней мере, теоретически, что-то вроде O (1 / n ) совершенно разумно, когда мы берем математическое определение обозначения Big O :
Теперь вы можете легко заменить g ( x ) на 1 / x ... очевидно, что приведенное выше определение все еще верно для некоторого f .
Для оценки асимптотического роста во время выполнения это менее жизнеспособно ... значимый алгоритм не может работать быстрее по мере роста входных данных. Конечно, вы можете построить произвольный алгоритм для выполнения этого, например, следующий:
def get_faster(list):
how_long = (1 / len(list)) * 100000
sleep(how_long)
Ясно, что эта функция тратит меньше времени по мере увеличения размера ввода… по крайней мере, до некоторого предела, навязанного аппаратными средствами (точность чисел, минимум времени, которое sleep
может ждать, время для обработки аргументов и т. Д.): Тогда этот предел будет постоянная нижняя граница, так что фактически вышеприведенная функция все еще имеет время выполнения O (1).
Но это на самом деле реальных алгоритмов , где среда выполнения может уменьшаться (по крайней мере , частично) , когда увеличивается размер входного. Обратите внимание, что эти алгоритмы не будут демонстрировать поведение во время выполнения ниже O (1). Тем не менее, они интересные. Например, возьмем очень простой алгоритм поиска текста Хорспула . Здесь ожидаемое время выполнения будет уменьшаться по мере увеличения длины шаблона поиска (но увеличение длины стога сена еще раз увеличит время выполнения).
Да.
Существует точно один алгоритм с временем выполнения O (1 / n), «пустой» алгоритм.
Если алгоритм равен O (1 / n), это означает, что он выполняется асимптотически за меньшее количество шагов, чем алгоритм, состоящий из одной инструкции. Если он выполняется менее чем за один шаг для всех n> n0, он должен состоять из абсолютно никаких инструкций для этих n. Поскольку проверка 'if n> n0' стоит как минимум 1 инструкцию, она не должна содержать команду для всех n.
Подводя итог: единственный алгоритм , который является O (1 / N) является пустой алгоритм, состоящий из не инструкции.
острый зуб правильный, O (1) - наилучшая возможная производительность. Тем не менее, это не означает быстрое решение, просто решение с фиксированным временем.
Интересный вариант, и, возможно, то, что действительно предлагается, заключается в том, какие проблемы становятся легче по мере роста населения. Я могу думать о 1, хотя надуманный и насмешливый ответ:
Есть ли у двух людей в наборе один и тот же день рождения? Когда n превышает 365, верните true. Хотя для менее чем 365 это O (n ln n). Возможно, не очень хороший ответ, так как проблема не становится легче, а просто становится O (1) для n> 365.
Это невозможно. Определение Big-O не больше, чем неравенство:
A(n) = O(B(n))
<=>
exists constants C and n0, C > 0, n0 > 0 such that
for all n > n0, A(n) <= C * B(n)
Таким образом, B (n) на самом деле является максимальным значением, поэтому, если оно уменьшается при увеличении n, оценка не изменится.
Из моего предыдущего изучения больших обозначений O, даже если вам нужен 1 шаг (например, проверка переменной, выполнение присваивания), то есть O (1).
Обратите внимание, что O (1) совпадает с O (6), потому что «константа» не имеет значения. Вот почему мы говорим, что O (n) совпадает с O (3n).
Таким образом, если вам нужен хотя бы один шаг, это O (1) ... и, поскольку вашей программе требуется по крайней мере 1 шаг, минимум алгоритма может быть равен O (1). Если мы не сделаем этого, то это O (0), я думаю? Если мы вообще что-то делаем, то это O (1), и это минимум, на который можно пойти.
(Если мы решим не делать этого, то это может стать вопросом дзен или дао ... в области программирования O (1) по-прежнему является минимальным).
Или как насчет этого:
программист : босс, я нашел способ сделать это за O (1) раз!
босс : нет необходимости делать это, мы обанкротились сегодня утром.
программист : о, тогда он становится O (0).
Нет, это невозможно:
Поскольку n стремится к бесконечности в 1 / n, мы в конечном итоге достигаем 1 / (inf), что фактически равно 0.
Таким образом, биг-о-классом задачи будет O (0) с массивным n, но ближе к постоянному времени с низким n. Это не имеет смысла, так как единственное, что можно сделать быстрее, чем постоянное время, это:
void nothing() {};
И даже это спорно!
Как только вы выполняете команду, вы попадаете как минимум в O (1), поэтому нет, у нас не может быть класса big-oh O (1 / n)!
Как насчет того, чтобы вообще не запускать функцию (NOOP)? или используя фиксированное значение. Это считается?
Я часто использую O (1 / n) для описания вероятностей, которые становятся меньше по мере увеличения входных данных - например, вероятность того, что честная монета выпадет в хвост на log2 (n) бросках, равна O (1 / n).
O (1) просто означает «постоянное время».
Когда вы добавляете ранний выход в цикл [1], вы (в нотации big-O) превращаете алгоритм O (1) в O (n), но делаете его быстрее.
Хитрость в том, что алгоритм с постоянным временем является лучшим, а линейный лучше, чем экспоненциальный, но при малых значениях n экспоненциальный алгоритм может быть на самом деле быстрее.
1: Предполагая длину статического списка для этого примера
Для тех, кто читает этот вопрос и хочет понять, о чем идет речь, это может помочь:
| |constant |logarithmic |linear| N-log-N |quadratic| cubic | exponential |
| n | O(1) | O(log n) | O(n) |O(n log n)| O(n^2) | O(n^3) | O(2^n) |
| 1 | 1 | 1 | 1| 1| 1| 1 | 2 |
| 2 | 1 | 1 | 2| 2| 4| 8 | 4 |
| 4 | 1 | 2 | 4| 8| 16| 64 | 16 |
| 8 | 1 | 3 | 8| 24| 64| 512 | 256 |
| 16 | 1 | 4 | 16| 64| 256| 4,096 | 65536 |
| 32 | 1 | 5 | 32| 160| 1,024| 32,768 | 4,294,967,296 |
| 64 | 1 | 6 | 64| 384| 4,069| 262,144 | 1.8 x 10^19 |
Я считаю, что квантовые алгоритмы могут делать несколько вычислений "одновременно" посредством суперпозиции ...
Я сомневаюсь, что это полезный ответ.
у многих людей был правильный ответ (Нет). Вот еще один способ доказать это: для того, чтобы иметь функцию, вы должны вызвать функцию и вернуть ответ. Это занимает определенное постоянное количество времени. ДАЖЕ ЕСЛИ для остальной обработки потребовалось меньше времени для больших входных данных, то распечатка ответа (который мы можем считать единичным битом) занимает, по крайней мере, постоянное время.
Если решение существует, оно может быть подготовлено и доступно в постоянное время = немедленно. Например, используя структуру данных LIFO, если вы знаете, что сортировочный запрос для обратного порядка. Затем данные уже отсортированы, учитывая, что была выбрана соответствующая модель (LIFO).
Какие проблемы становятся легче по мере роста населения? Одним из ответов является нечто вроде bittorrent, где скорость загрузки является обратной функцией числа узлов. В отличие от автомобиля, который замедляется при загрузке, сеть обмена файлами, такая как bittorrent, ускоряет подключение большего количества узлов.
Вы не можете опуститься ниже O (1), однако O (k), где k меньше N, возможно. Мы назвали их сублинейными алгоритмами времени . В некоторых задачах алгоритм сублинейного времени может дать только приблизительные решения конкретной задачи. Однако иногда приблизительные решения просто хороши, возможно, из-за того, что набор данных слишком велик или что он слишком дорог для вычислений, чтобы вычислить все.
Как насчет этого:
void FindRandomInList(list l)
{
while(1)
{
int rand = Random.next();
if (l.contains(rand))
return;
}
}
с ростом размера списка ожидаемое время выполнения программы уменьшается.
constains
O (1)
O (1 / n) не меньше, чем O (1), это означает, что чем больше у вас данных, тем быстрее работает алгоритм. Скажем, вы получаете массив и всегда заполняете его до 10 100 элементов, если в нем меньше, и ничего не делаете, если есть больше. Это, конечно, не O (1 / n), а что-то вроде O (-n) :) Слишком плохая запись O-big не допускает отрицательных значений.
Как уже указывалось, кроме возможного исключения нулевой функции, не может быть никаких O(1/n)
функций, так как время, которое потребуется, должно приблизиться к 0.
Конечно, есть некоторые алгоритмы, подобные определенным Конрадом, которые кажутся такими, что их должно быть меньше, чем O(1)
в каком-то смысле.
def get_faster(list):
how_long = 1/len(list)
sleep(how_long)
Если вы хотите исследовать эти алгоритмы, вы должны либо определить собственное асимптотическое измерение, либо свое собственное представление о времени. Например, в приведенном выше алгоритме я мог бы разрешить использование ряда «свободных» операций определенное количество раз. В приведенном выше алгоритме, если я определяю t ', исключая время для всего, кроме сна, тогда t' = 1 / n, что равно O (1 / n). Вероятно, есть лучшие примеры, поскольку асимптотическое поведение тривиально. На самом деле, я уверен, что кто-то может придумать чувства, которые дают нетривиальные результаты.
Большинство остальных ответов интерпретируют big-O как исключительно время выполнения алгоритма. Но поскольку в вопросе об этом не упоминалось, я подумал, что стоит упомянуть другое применение big-O в численном анализе, которое связано с ошибкой.
Многие алгоритмы могут быть O (h ^ p) или O (n ^ {- p}) в зависимости от того, говорите ли вы о размере шага (h) или количестве делений (n). Например, в методе Эйлера вы ищете оценку y (h), учитывая, что вы знаете y (0) и dy / dx (производная от y). Ваша оценка y (h) тем точнее, чем ближе h к 0. Таким образом, чтобы найти y (x) для некоторого произвольного x, нужно взять интервал от 0 до x, разбить его до n частей и запустить метод Эйлера в каждой точке переходить от y (0) к y (x / n) к y (2x / n) и так далее.
Таким образом, метод Эйлера - это алгоритм O (h) или O (1 / n), где h обычно интерпретируется как размер шага, а n - как число делений интервала.
Вы также можете иметь O (1 / ч) в реальных приложениях для численного анализа из-за ошибок округления с плавающей запятой . Чем меньше ваш интервал, тем больше отмена происходит при реализации определенных алгоритмов, тем больше потери значащих цифр и, следовательно, больше ошибок, которые распространяются через алгоритм.
Для метода Эйлера, если вы используете числа с плавающей запятой, используйте достаточно маленький шаг и отмену, и вы добавляете небольшое число к большому, оставляя большое число без изменений. Для алгоритмов, которые вычисляют производную путем вычитания друг от друга двух чисел из функции, вычисленной в двух очень близких положениях, аппроксимирующих y '(x) с (y (x + h) - y (x) / h), в гладких функциях y (x + h) приближается к y (x), что приводит к большой отмене и оценке производной с меньшим количеством значащих цифр. Это, в свою очередь, будет распространяться на любой алгоритм, для которого требуется производная (например, краевая задача).
Хорошо, я немного подумал об этом, и, возможно, существует алгоритм, который мог бы следовать этой общей форме:
Вам нужно вычислить задачу коммивояжера для графа 1000 узлов, однако вам также предоставляется список узлов, которые вы не можете посетить. По мере того, как список невидимых узлов увеличивается, проблема становится легче решать.
Я вижу алгоритм, который по общему признанию равен O (1 / n):
У вас есть большая серия входов, которые меняются из-за чего-то внешнего для рутины (возможно, они отражают аппаратное обеспечение или это может быть даже какое-то другое ядро процессора), и вы должны выбрать случайное, но действительное.
Теперь, если он не меняется, вы просто составляете список предметов, выбираете один случайным образом и получаете O (1) раз. Тем не менее, динамический характер данных не позволяет составить список, вы просто должны проверить случайным образом и проверить достоверность проверки. (И обратите внимание, что по сути нет никакой гарантии, что ответ по-прежнему действителен, когда он будет возвращен. В нем все еще могут использоваться, скажем, ИИ для отряда в игре. Он может стрелять по цели, которая выпала из поля зрения, пока он был спусковой крючок.)
Это имеет худшую производительность бесконечности, но средняя производительность падает, когда пространство данных заполняется.
При численном анализе алгоритмы аппроксимации должны иметь субконстантную асимптотическую сложность в допуске аппроксимации.
class Function
{
public double[] ApproximateSolution(double tolerance)
{
// if this isn't sub-constant on the parameter, it's rather useless
}
}
Я думаю, что меньше, чем O (1) невозможно. Любое время, занимаемое алгоритмом, называется O (1). Но для O (1 / n), как насчет функции ниже. (Я знаю, что есть много вариантов, уже представленных в этом решении, но я предполагаю, что у них всех есть некоторые недостатки (не основные, они хорошо объясняют концепцию). Так что вот один, просто для аргументации:
def 1_by_n(n, C = 10): #n could be float. C could be any positive number
if n <= 0.0: #If input is actually 0, infinite loop.
while True:
sleep(1) #or pass
return #This line is not needed and is unreachable
delta = 0.0001
itr = delta
while delta < C/n:
itr += delta
Таким образом, при увеличении n функция будет занимать все меньше и меньше времени. Также гарантируется, что если input на самом деле равен 0, функция вернется навсегда.
Можно утверждать, что это будет ограничено точностью машины. таким образом, sinc eit имеет верхнюю границу, это O (1). Но мы также можем обойти это, взяв значения n и C в строку. И сложение и сравнение делается на строку. Идея состоит в том, что при этом мы можем уменьшить n сколь угодно малым. Таким образом, верхний предел функции не ограничен, даже если мы игнорируем n = 0.
Я также считаю, что мы не можем просто сказать, что время выполнения равно O (1 / n). Но мы должны сказать что-то вроде O (1 + 1 / n)
Может быть возможно построить алгоритм, который является O (1 / n). Одним примером может быть цикл, который повторяет несколько кратных f (n) -n раз, где f (n) - это некоторая функция, значение которой гарантированно будет больше n, а предел f (n) -n, когда n приближается к бесконечности, равен нуль. Вычисление f (n) также должно быть постоянным для всех n. Я не знаю, как выглядит f (n) или какое приложение будет иметь такой алгоритм, на мой взгляд, однако такая функция могла бы существовать, но результирующий алгоритм не имел бы никакой цели, кроме как доказать возможность алгоритма с O (1 / п).
Я не знаю об алгоритмах, но сложности меньше, чем O (1) появляются в рандомизированных алгоритмах. На самом деле, o (1) (мало o) меньше, чем O (1). Этот вид сложности обычно появляется в рандомизированных алгоритмах. Например, как вы сказали, когда вероятность какого-либо события имеет порядок 1 / n, они обозначают его как o (1). Или, когда они хотят сказать, что что-то происходит с высокой вероятностью (например, 1 - 1 / n), они обозначают это как 1 - o (1).
Если ответ одинаковый независимо от входных данных, то у вас есть алгоритм O (0).
или, другими словами, - ответ известен перед отправкой входных данных - функция может быть оптимизирована - поэтому O (0)
Нотация Big-O представляет наихудший сценарий для алгоритма, который отличается от типичного времени выполнения. Просто доказать, что алгоритм O (1 / n) является алгоритмом O (1). По определению
O (1 / n) -> T (n) <= 1 / n, для всех n> = C> 0
O (1 / n) -> T (n) <= 1 / C, так как 1 / n <= 1 / C для всех n> = C
O (1 / n) -> O (1), поскольку запись Big-O игнорирует константы (т. Е. Значение C не имеет значения)
hashtable-contains
алгоритма, которое можно обозначить как O (1), а наихудший случай можно очень точно определить как Theta (n)! Омега и тэта могут просто использоваться для обозначения других границ, но для повторения : они не имеют никакого отношения к среднему или лучшему случаю.
Ничто не меньше, чем O (1) нотация Big-O подразумевает наибольший порядок сложности для алгоритма
Если время выполнения алгоритма равно n ^ 3 + n ^ 2 + n + 5, то оно равно O (n ^ 3). Более низкие степени здесь вообще не имеют значения, поскольку при n -> Inf n ^ 2 не будет иметь значения по сравнению с п ^ 3
Точно так же, как n -> Inf, O (1 / n) будет неактуальным по сравнению с O (1), следовательно, 3 + O (1 / n) будет таким же, как O (1), таким образом делая O (1) наименьшим возможным вычислительным сложность
inline void O0Algorithm() {}
Вот простой алгоритм O (1 / n). И это даже делает что-то интересное!
function foo(list input) {
int m;
double output;
m = (1/ input.size) * max_value;
output = 0;
for (int i = 0; i < m; i++)
output+= random(0,1);
return output;
}
O (1 / n) возможно, поскольку оно описывает, как выходные данные функции изменяются при увеличении размера ввода. Если мы используем функцию 1 / n для описания количества инструкций, которые выполняет функция, тогда не требуется, чтобы функция принимала нулевые инструкции для любого размера ввода. Скорее, дело в том, что для каждого входного размера, n выше некоторого порога, количество требуемых инструкций ограничено сверху положительной константой, умноженной на 1 / n. Поскольку нет фактического числа, для которого 1 / n равно 0, а константа положительна, то нет никаких причин, по которым функция будет вынуждена принимать 0 или меньше инструкций.