Escriba un programa (o función) que exhiba cuatro complejidades comunes de tiempo de O grande dependiendo de cómo se ejecute. En cualquier forma, toma un número entero positivo N que puede suponer que es menor que 2 31 .
Cuando el programa se ejecuta en su forma original , debe tener una complejidad constante . Es decir, la complejidad debe ser Θ (1) o, de manera equivalente, Θ (1 ^ N) .
Cuando el programa se invierte y ejecuta, debe tener una complejidad lineal . Es decir, la complejidad debe ser Θ (N) o, de manera equivalente, Θ (N ^ 1) .
(Esto tiene sentido ya queN^1
se1^N
invierte).Cuando se programa un doble , es decir, concatenado a sí mismo, y ejecutarlo debe tener exponencial complejidad, específicamente 2 N . Es decir, la complejidad debe ser Θ (2 ^ N) .
(Esto tiene sentido ya que el2
en2^N
es el doble de la1
en1^N
.)Cuando el programa se duplicó y revirtió y ejecutarlo debe tener polinomio complejidad, específicamente N 2 . Es decir, la complejidad debe ser Θ (N ^ 2) .
(Esto tiene sentido ya queN^2
se2^N
invierte).
Estos cuatro casos son los únicos que necesita manejar.
Tenga en cuenta que, para mayor precisión, estoy usando la notación theta grande (Θ) en lugar de la gran O porque los tiempos de ejecución de sus programas deben estar limitados tanto por encima como por debajo de las complejidades requeridas. De lo contrario, solo escribir una función en O (1) satisfaría los cuatro puntos. No es demasiado importante entender los matices aquí. Principalmente, si su programa está haciendo operaciones k * f (N) para alguna k constante, entonces es probable que esté en Θ (f (N)).
Ejemplo
Si el programa original fuera
ABCDE
luego ejecutarlo debería llevar un tiempo constante. Es decir, si la entrada N es 1 o 2147483647 (2 31 -1) o cualquier valor intermedio, debe terminar aproximadamente en la misma cantidad de tiempo.
La versión inversa del programa.
EDCBA
debería llevar un tiempo lineal en términos de N. Es decir, el tiempo que tarda en terminar debe ser aproximadamente proporcional a N. Por lo tanto, N = 1 toma menos tiempo y N = 2147483647 toma más.
La versión duplicada del programa.
ABCDEABCDE
debe tomar tiempo de dos-a-la N en términos de N. Es decir, el tiempo que se necesita para terminar debe ser aproximadamente proporcional a 2 N . Entonces, si N = 1 termina en aproximadamente un segundo, N = 60 tomaría más tiempo que la edad del universo para terminar. (No, no tienes que probarlo).
La versión duplicada y revertida del programa.
EDCBAEDCBA
debería tomar el tiempo al cuadrado en términos de N. Es decir, el tiempo que tarda en terminar debe ser aproximadamente proporcional a N * N. Entonces, si N = 1 termina en aproximadamente un segundo, N = 60 tardaría aproximadamente una hora en terminar.
Detalles
Debe mostrar o argumentar que sus programas se ejecutan en las complejidades que usted dice que son. Dar algunos datos de tiempo es una buena idea, pero también trate de explicar por qué teóricamente la complejidad es correcta.
Está bien si en la práctica los tiempos que toman sus programas no son perfectamente representativos de su complejidad (o incluso deterministas). por ejemplo, la entrada N + 1 a veces puede correr más rápido que N.
El entorno en el que ejecuta sus programas sí importa. Puede hacer suposiciones básicas sobre cómo los lenguajes populares nunca pierden tiempo intencionalmente en algoritmos, pero, por ejemplo, si sabe que su versión particular de Java implementa el ordenamiento de burbujas en lugar de un algoritmo de ordenamiento más rápido , entonces debe tener eso en cuenta si hace algún ordenamiento .
Para todas las complejidades aquí, supongamos que estamos hablando de los peores escenarios , no el mejor de los casos o el caso promedio.
La complejidad espacial de los programas no importa, solo la complejidad del tiempo.
Los programas pueden generar cualquier cosa. Solo importa que tomen el entero positivo N y que tengan las complejidades de tiempo correctas.
Se permiten comentarios y programas multilínea. (Puede suponer que
\r\n
revertido es\r\n
por compatibilidad con Windows).
Big O Recordatorios
De más rápido a más lento es O(1), O(N), O(N^2), O(2^N)
(orden 1, 2, 4, 3 arriba).
Los términos más lentos siempre dominan, por ejemplo O(2^N + N^2 + N) = O(2^N)
.
O(k*f(N)) = O(f(N))
para constante k. Así O(2) = O(30) = O(1)
y O(2*N) = O(0.1*N) = O(N)
.
Recuerda O(N^2) != O(N^3)
y O(2^N) != O(3^N)
.
Cuidadosamente grande O hoja de trucos.
Puntuación
Este es el código normal de golf. El programa original más corto (el de tiempo constante) en bytes gana.
fuente
n = input(); for i in xrange(n): pass
tiene una complejidad exponencial, ya que toma2 ** k
pasos, dondek = log_2(n)
está el tamaño de entrada. Debe aclarar si este es el caso, ya que cambia drásticamente los requisitos.Respuestas:
Python 3 , 102 bytes
Pruébalo en línea!
Esto es de O (1 ^ n), ya que esto es lo que hace el programa:
Invertido:
Pruébalo en línea!
Esto es de O (n ^ 1), ya que esto es lo que hace el programa:
Doblado:
Pruébalo en línea!
Esto es de O (2 ^ n), ya que esto es lo que hace el programa:
Doblado y revertido:
Pruébalo en línea!
Esto es de O (n ^ 2), ya que esto es lo que hace el programa:
fuente
input()
?k
es la entrada yl
es una, así que todavía estás computando1**k
, ¿verdad? ¿Qué debería tomarO(log(k))
tiempo, a pesar de que tú y yo y todos sabemos de antemano que es uno?Perl 5,
827371 + 1 (para el indicador -n) = 72 bytesEstoy seguro de que puedo jugar esto (mucho) más, pero es hora de dormir, he pasado suficiente tiempo depurando como está, y de todos modos estoy orgulloso de lo que tengo hasta ahora.
El programa en sí no usa la entrada, y solo evalúa una cadena que comienza con un comentario y luego realiza una sola sustitución de cadena, por lo que esto es claramente en tiempo constante. Básicamente es equivalente a:
Doblado:
El bit que realmente toma tiempo exponencial es la segunda evaluación: evalúa el comando
say for(1..2**$_)
, que enumera todos los números del 1 al 2 ^ N, que claramente toma tiempo exponencial.Invertido:
Esto (ingenuamente) calcula la suma de la entrada, que claramente toma tiempo lineal (ya que cada adición es en tiempo constante). El código que realmente se ejecuta es equivalente a:
La última línea es solo para que cuando se repitan estos comandos tome tiempo cuadrático.
Invertido y doblado:
Esto ahora toma la suma de la suma de la entrada (y la agrega a la suma de la entrada, pero lo que sea). Como ordena
N^2
adiciones, esto lleva tiempo cuadrático. Básicamente es equivalente a:La segunda línea encuentra la suma de la entrada, haciendo
N
adiciones, mientras que la cuarta hacesummation(N)
adiciones, que esO(N^2)
.fuente
$x.=q;##say...
lugar de$x.=q;#say...
sin embargo (con dos en#
lugar de 1). (Eso explicaría por qué contó 76 bytes en lugar de 75). Además, debe contar la-n
bandera en su bytecount, ya que su programa no hace mucho sin ella.eval
y loss///
comandos. Si haces loeval
primero solo necesitas el uno#
. ¡Buena atrapada!#
:$x=~s/#//;
producciones invertidas;//#/s~=x$
, que en su contexto no hace nada, como lo haría con un líder#
. (No lo he probado sin embargo). En cualquier caso, ¡ten un +1!En realidad , 20 bytes
Pruébalo en línea!
Entrada:
5
Salida:
Invertido:
Pruébalo en línea!
Salida:
Doblado:
Pruébalo en línea!
Salida:
Doblado y revertido:
Pruébalo en línea!
Salida:
Idea principal
En realidad es un lenguaje basado en pila.
abc
es un programa que tiene complejidad O (1 n ), y su doble tiene complejidad O (2 n ).def
es un programa que tiene complejidad O (n 1 ), y su doble tiene complejidad O (n 2 ).Entonces, mi sumisión es
"abc"ƒ"fed"
, dondeƒ
se evalúa. De esa manera,"fed"
no será evaluado.Generación de programa individual.
El pseudocódigo del primer componente
;(1╖╜ⁿr
:El pseudocódigo del segundo componente
;(1╖╜ⁿ@r
:fuente
Jalea , 20 bytes
En parte inspirado por la solución de Leaky Nun en realidad .
Las nuevas líneas iniciales y finales son significativas.
Normal:
Pruébalo en línea!
Entrada:
5
Salida:
Invertido:
Pruébalo en línea!
Entrada:
5
Salida:
Doblado
Pruébalo en línea!
Entrada:
5
Salida:
Doblado e invertido
Pruébalo en línea!
Entrada:
5
Salida:
Explicación
La clave aquí está en
Ŀ
, que significa "llama al enlace en el índice n como una mónada". Los enlaces se indexan de arriba a abajo a partir de 1, excluyendo el enlace principal (el más bajo).Ŀ
también es modular, por lo que si intenta llamar al enlace número 7 de 5 enlaces, en realidad llamará al enlace 2.El enlace que se llama en este programa siempre es el que se encuentra en el índice 10 (
⁵
) sin importar la versión del programa que sea. Sin embargo, qué enlace está en el índice 10 depende de la versión.Lo
⁵
que aparece después de cada unoĿ
está allí para que no se rompa cuando se invierte. El programa generará un error en tiempo de análisis si no hay un número antesĿ
. Tener un⁵
after es un nilad fuera de lugar, que solo va directamente a la salida.La versión original llama al enlace
‘
, que calcula n + 1.La versión inversa llama al enlace
R
, que genera el rango 1 .. n.La versión duplicada llama al enlace
2*R
, que calcula 2 n y genera el rango 1 .. 2 n .La versión duplicada e inversa llama al enlace
²R
, que calcula n 2 y genera el rango 1 .. n 2 .fuente
Befunge-98 , 50 bytes
Normal
Este es, con mucho, el programa más simple de los 4: los únicos comandos que se ejecutan son los siguientes:
Este programa hace algunas cosas irreverentes antes de presionar un comando "girar a la derecha" (
]
) y una flecha. Luego golpea 2 comandos "tomar entrada". Debido a que solo hay 1 número en la entrada y a la forma en que TIO trata el&
s, el programa sale después de 60 segundos. Si hay 2 entradas (y porque puedo sin agregar bytes), la IP viajaría a la función "finalizar programa".Pruébalo en línea!
Invertido
Este es un poco más complicado. Los comandos relevantes son los siguientes:
que es equivalente a
La parte importante aquí es el
:. 1-:!k@
bit. Es útil porque siempre que empujemos la complejidad correcta a la pila antes de ejecutarla en un tiempo menor, podemos obtener la deseada. Esto se utilizará en los 2 programas restantes de esta manera.Pruébalo en línea!
Doblado
Y los comandos relevantes son:
Este programa entra en 2 bucles. Primero, sigue el mismo camino que el programa normal, que empuja 1 y N a la pila, pero en lugar de
&
pasar al segundo , la IP salta un comentario en un bucle que empuja2^N
:Los otros bits en la línea 4 se saltan usando
;
sDespués de empujar (2 ^ N) en la pila, nos movemos hacia abajo en una línea hacia el bucle de impresión antes mencionado. Debido al primer bucle, la complejidad del tiempo es Θ (N + 2 ^ N), pero eso se reduce a Θ (2 ^ N).
Pruébalo en línea!
Doblado e invertido
Los comandos relevantes:
Esto funciona casi de manera idéntica al programa invertido, pero
N^2
no se elimina de la pila, porque la primera línea de la segunda copia del programa se agrega a la última línea de la primera, lo que significa que el comando soltar ($
) nunca se ejecuta , así que vamos al ciclo de impresión conN^2
en la parte superior de la pila.Pruébalo en línea!
fuente