¿Hay alguna manera de calcular la complejidad temporal de un algoritmo programáticamente? Por ejemplo, ¿cómo podría calcular la complejidad de una función fibonacci(n)
?¿Puede un programa calcular la complejidad de un algoritmo?
Respuesta
La indecidibilidad del halting problem dice que ni siquiera se puede decir si un algoritmo termina. Estoy bastante seguro de que de esto se desprende que generalmente no se puede resolver la complejidad de un algoritmo.
Cuente las operaciones aritméticas, los accesos de memoria y el espacio de memoria utilizado dentro de fibbonacci()
o lo que sea, mida su tiempo de ejecución. Haga esto con diferentes entradas, vea las tendencias emergentes, el comportamiento asintótico.
No en general. Si el algoritmo consiste en bucles simples anidados for
, p.
for (int i=a; i<b; ++i)
entonces usted sabe que esto contribuirá (b-a)
pasos. Ahora, si b
o a
o ambos dependen de n
, entonces puede obtener una complejidad de eso. Sin embargo, si usted tiene algo más exótico, como
for (int i=a; i<b; i=whackyFunction(i))
entonces usted realmente necesita entender lo que hace whackyFunction(i)
.
De manera similar, las declaraciones break
pueden arruinar esto, y las declaraciones while
pueden ser una causa perdida ya que es posible que ni siquiera sea capaz de saber si el ciclo finalizó.
Las medidas generales como cyclomatic complexity son útiles para darle una idea de las partes más complejas de su código, pero es un mecanismo relativamente simple.
Si bien es imposible de hacer en todos los casos (a menos que ejecute su propio analizador de código y solo observe los bucles y los impactos en sus valores), todavía es posible hacer una prueba de recuadro negro con un límite superior tiempo establecido. Es decir, tiene alguna variable que se establece para determinar que una vez que la ejecución de un programa pasa esta vez se considera que se ejecuta para siempre.
A partir de esto, su código sería similar a este (código rápido y sucio lo siento, es un poco detallado y la matemática podría estar desactivada para potencias más grandes que no he comprobado).
Puede mejorarse mediante el uso de un conjunto de valores de entrada en lugar de generar aleatoriamente algunos, y también al verificar un rango más amplio de valores, debe poder verificar cualquier entrada en comparación con otras dos entradas y determinar todas las patrones de duración del método
Estoy seguro de que hay formas mucho mejores (es decir, más precisas) de calcular O
entre un conjunto de números dados que el que se muestra aquí (que no relaciona demasiado el tiempo de ejecución entre los elementos).
static void Main(string[] args)
{
var sw = new Stopwatch();
var inputTimes = new Dictionary<int, double>();
List<int> inputValues = new List<int>();
for (int i = 0; i < 25; i++)
{
inputValues.Add(i);
}
var ThreadTimeout = 10000;
for (int i = 0; i < inputValues.Count; i++)
{
int input = inputValues[i];
var WorkerThread = new Thread(t => CallMagicMethod(input)) { Name = "WorkerThread" };
sw.Reset();
Console.WriteLine("Input value '{0}' running...", input);
sw.Start();
WorkerThread.Start();
WorkerThread.Join(ThreadTimeout);
sw.Stop();
if (WorkerThread.IsAlive)
{
Console.WriteLine("Input value '{0}' exceeds timeout", input);
WorkerThread.Abort();
//break;
inputTimes.Add(input, double.MaxValue);
continue;
}
inputTimes.Add(input, sw.Elapsed.TotalMilliseconds);
Console.WriteLine("Input value '{0}' took {1}ms", input, sw.Elapsed.TotalMilliseconds);
}
List<int> indexes = inputTimes.Keys.OrderBy(k => k).ToList();
// calculate the difference between the values:
for (int i = 0; i < indexes.Count - 2; i++)
{
int index0 = indexes[i];
int index1 = indexes[i + 1];
if (!inputTimes.ContainsKey(index1))
{
continue;
}
int index2 = indexes[i + 2];
if (!inputTimes.ContainsKey(index2))
{
continue;
}
double[] runTimes = new double[] { inputTimes[index0], inputTimes[index1], inputTimes[index2] };
if (IsRoughlyEqual(runTimes[2], runTimes[1], runTimes[0]))
{
Console.WriteLine("Execution time for input = {0} to {1} is roughly O(1)", index0, index2);
}
else if (IsRoughlyEqual(runTimes[2]/Math.Log(index2, 2), runTimes[1]/Math.Log(index1, 2), runTimes[0]/Math.Log(index0, 2)))
{
Console.WriteLine("Execution time for input = {0} to {1} is roughly O(log N)", index0, index2);
}
else if (IsRoughlyEqual(runTimes[2]/index2, runTimes[1]/index1, runTimes[0]/index0))
{
Console.WriteLine("Execution time for input = {0} to {1} is roughly O(N)", index0, index2);
}
else if (IsRoughlyEqual(runTimes[2]/(Math.Log(index2, 2) * index2), runTimes[1]/(Math.Log(index1, 2) * index1), runTimes[0]/(Math.Log(index0, 2) * index0)))
{
Console.WriteLine("Execution time for input = {0} to {1} is roughly O(N log N)", index0, index2);
}
else
{
for (int pow = 2; pow <= 10; pow++)
{
if (IsRoughlyEqual(runTimes[2]/Math.Pow(index2, pow), runTimes[1]/Math.Pow(index1, pow), runTimes[0]/Math.Pow(index0, pow)))
{
Console.WriteLine("Execution time for input = {0} to {1} is roughly O(N^{2})", index0, index2, pow);
break;
}
else if (pow == 10)
{
Console.WriteLine("Execution time for input = {0} to {1} is greater than O(N^10)", index0, index2);
}
}
}
}
Console.WriteLine("Fin.");
}
private static double variance = 0.02;
public static bool IsRoughlyEqual(double value, double lower, double upper)
{
//returns if the lower, value and upper are within a variance of the next value;
return IsBetween(lower, value * (1 - variance), value * (1 + variance)) &&
IsBetween(value, upper * (1 - variance), upper * (1 + variance));
}
public static bool IsBetween(double value, double lower, double upper)
{
//returns if the value is between the other 2 values +/- variance
lower = lower * (1 - variance);
upper = upper * (1 + variance);
return value > lower && value < upper;
}
public static void CallMagicMethod(int input)
{
try
{
MagicBox.MagicMethod(input);
}
catch (ThreadAbortException tae)
{
}
catch (Exception ex)
{
Console.WriteLine("Unexpected Exception Occured: {0}", ex.Message);
}
}
Y un ejemplo de salida:
Input value '59' running...
Input value '59' took 1711.8416ms
Input value '14' running...
Input value '14' took 90.9222ms
Input value '43' running...
Input value '43' took 902.7444ms
Input value '22' running...
Input value '22' took 231.5498ms
Input value '50' running...
Input value '50' took 1224.761ms
Input value '27' running...
Input value '27' took 351.3938ms
Input value '5' running...
Input value '5' took 9.8048ms
Input value '28' running...
Input value '28' took 377.8156ms
Input value '26' running...
Input value '26' took 325.4898ms
Input value '46' running...
Input value '46' took 1035.6526ms
Execution time for input = 5 to 22 is greater than O(N^10)
Execution time for input = 14 to 26 is roughly O(N^2)
Execution time for input = 22 to 27 is roughly O(N^2)
Execution time for input = 26 to 28 is roughly O(N^2)
Execution time for input = 27 to 43 is roughly O(N^2)
Execution time for input = 28 to 46 is roughly O(N^2)
Execution time for input = 43 to 50 is roughly O(N^2)
Execution time for input = 46 to 59 is roughly O(N^2)
Fin.
¿Qué muestra el método mágico es probable O(N^2)
para las entradas dadas +/- 2% de la varianza
y otro resultado aquí:
Input value '0' took 0.7498ms
Input value '1' took 0.3062ms
Input value '2' took 0.5038ms
Input value '3' took 4.9239ms
Input value '4' took 14.2928ms
Input value '5' took 29.9069ms
Input value '6' took 55.4424ms
Input value '7' took 91.6886ms
Input value '8' took 140.5015ms
Input value '9' took 204.5546ms
Input value '10' took 285.4843ms
Input value '11' took 385.7506ms
Input value '12' took 506.8602ms
Input value '13' took 650.7438ms
Input value '14' took 819.8519ms
Input value '15' took 1015.8124ms
Execution time for input = 0 to 2 is greater than O(N^10)
Execution time for input = 1 to 3 is greater than O(N^10)
Execution time for input = 2 to 4 is greater than O(N^10)
Execution time for input = 3 to 5 is greater than O(N^10)
Execution time for input = 4 to 6 is greater than O(N^10)
Execution time for input = 5 to 7 is greater than O(N^10)
Execution time for input = 6 to 8 is greater than O(N^10)
Execution time for input = 7 to 9 is greater than O(N^10)
Execution time for input = 8 to 10 is roughly O(N^3)
Execution time for input = 9 to 11 is roughly O(N^3)
Execution time for input = 10 to 12 is roughly O(N^3)
Execution time for input = 11 to 13 is roughly O(N^3)
Execution time for input = 12 to 14 is roughly O(N^3)
Execution time for input = 13 to 15 is roughly O(N^3)
¿Qué muestra el método mágico es probable O(N^3)
para la dada en pone +/- 2% de varianza
Así que es posible determinar programáticamente la complejidad de un algoritmo, debe asegurarse de no introducir algún trabajo adicional que lo haga más largo de lo que cree (como construir toda la entrada para la función antes de comenzar a temporizarla).
Además de esto, también debe recordar que tomará un tiempo significativo para probar una gran serie de valores posibles y devolver cuánto tiempo tomó, una prueba más realista es simplemente llamar a su función en un gran realista valor límite superior y determinar si el tiempo de respuesta es suficiente para su uso.
Es probable que solo necesite hacer esto si está realizando pruebas de caja negra sin código fuente (y no puede usar algo como Reflector para ver la fuente), o si debe probar a un PHB que los algoritmos codificados son tan rápidos como pueden (ignorando las mejoras en las constantes), como usted dice.
- 1. ¿Cómo calcular la complejidad exacta de un algoritmo?
- 2. Complejidad del tiempo de un algoritmo recursivo
- 3. cálculo de tiempo complejidad de un algoritmo
- 4. complejidad del algoritmo
- 5. Implementación de un algoritmo simple (para calcular la probabilidad)
- 6. Complejidad del algoritmo
- 7. 2^n algoritmo de complejidad
- 8. qué algoritmo para un programa de programación
- 9. Calcular Complejidad Ciclomática para Javascript
- 10. Complejidad de la búsqueda de un Lucene
- 11. Complejidad del programa factorial recursiva
- 12. Complejidad del tiempo del algoritmo de Prim
- 13. Algoritmo para estimar la complejidad de la palabra
- 14. complejidad Tiempo de Criba de Eratóstenes algoritmo
- 15. Algoritmo de banca calculado complejidad de tiempo
- 16. Algoritmo de anagrama con complejidad mínima
- 17. ¿Puede un programa asignar la memoria directamente?
- 18. Complejidad del tiempo del algoritmo de Euclides
- 19. ¿Qué algoritmo usar para calcular un dígito de control?
- 20. Complejidad del tiempo del algoritmo de Fleury
- 21. Simplificando la Complejidad Big-O de este Algoritmo Exponencial
- 22. Complejidad del espacio del algoritmo recursivo
- 23. Complejidad computacional de un algoritmo de ruta más larga con un método recursivo
- 24. algoritmo de multiplicación de matrices complejidad del tiempo
- 25. Algoritmo de votación: cómo calcular el rango?
- 26. ¿Cómo escribirías un algoritmo no recursivo para calcular los factoriales?
- 27. algoritmo para calcular Puntos máxima en PointSet
- 28. Análisis de la complejidad de la imagen
- 29. Algoritmo Complejidad y seguridad: MD5 o SHA1?
- 30. Obtenga la estructura completa de un programa?
Generalmente no se puede, pero bueno, todavía venden Microsoft Windows y escriben software para aeronaves y sistemas de soporte de vida, ya sea en la complejidad es asombrosa y es de suma importancia no detención. –
Esto es incorrecto. El problema de detención indica que no existe un algoritmo para determinar si se detendrá un par de entrada de programa arbitrario en un lenguaje de Turing completo. Sin embargo, (implementaciones de) algoritmos, por definición, se garantiza que se completen en un número finito de pasos. – Nabb
@Nabb Lo que usted dice es correcto en el supuesto de que el algoritmo espera alguna entrada que cumpla con un conjunto de requisitos predefinidos. El algoritmo no tiene que garantizar su finalización en un número finito de pasos para ninguna otra entrada (inesperada). –