El "Hacker" en el nombre de la prueba sugiere tratamos de encontrar la informática orientada a la solución.
Vamos a comenzar con un programa de fuerza bruta enumeración de (a) la "favorable" en los casos en que un número entero es el doble de la otra y (b) de todos los casos posibles. La respuesta sería entonces su relación. Yo he programado una solución general. Su entrada es un número entero positivo n
, y su salida es la probabilidad.
n=100
all=favorable=0
for i=1 to n
for j=1 to n
if (i != j) all=all+1 {1}
if (j == 2*i) favorable = favorable+1 {2}
if (i == 2*j) favorable = favorable+1 {3}
return(favorable / all)
(La prueba de la corrección se basa en el hecho de que $i \ne 2i$ para cualquier número positivo $i$.)
Este programa requiere de $3$ pruebas y hasta el $3$ incrementos para cada iteración del bucle interno. Por lo tanto, se necesitan entre $3n$ $6n$ cálculos cada vez que el bucle interno se realiza, o $3n^2$ $6n^2$total. Eso es $O(n^2)$ rendimiento: OK para la pequeña $n$ como $n=100$, pero terrible una vez $n$ supera $10000$ o así.
Como un hacker, una de las primeras cosas que usted querrá hacer es eliminar el cuadrática rendimiento mediante la simplificación del bucle interno (si es posible). Para este fin, de manera sistemática que ir a través de las líneas en el bucle interno (con números) y tenga en cuenta lo siguiente:
La línea 1 se ejecuta todos, pero una vez para cada valor de i
y, por tanto, all
se incrementa el $n-1$ veces. En consecuencia, para el cálculo de all
, el lazo sobre j
puede ser reemplazado por el incremento all
por n-1
.
La línea 2 se ejecuta exactamente una vez al $2i \le n$ y no lo contrario. Por lo tanto, puede ser sustituido por el incremento all
$1$ siempre $2i \le n$.
La línea 3 se ejecuta una vez, siempre i
es incluso.
Aquí es el transformado del programa.
n=100
all=favorable=0
for i=1 to n
all = all + (n-1) {1'}
if (2*i <= n) favorable = favorable+1 {2'}
if (even(i)) favorable = favorable+1 {3'}
return(favorable / all)
Podemos ir más allá y eliminar su bucle?
La línea 1 es ejecutado $n$ veces. Por lo tanto, all
se incrementa en n*(n-1)
.
La línea 2' se ejecuta sólo cuando se $2i \le n$. Una manera de contar esto es $\lfloor n/2\rfloor$ (el mayor entero menor o igual a $n/2$).
La línea de 3' sólo se ejecuta incluso para valores de $i$. De nuevo, lo que sucede $\lfloor n/2 \rfloor$ veces.
La segunda transformación del programa es:
n=100
all=favorable=0 {0}
all = all + n * (n-1) {1''}
favorable = favorable + floor(n/2) {2''}
favorable = favorable + floor(n/2) {3''}
return(favorable / all)
Esto ya es un logro tremendo: una $O(n^2)$ algoritmo se ha reducido a un $O(1)$ algoritmo (que puede ser considerado como un "cerrado" fórmula para la respuesta).
Por último, hay algunos algebraicas simples transformaciones que podemos hacer por hacer rodar la inicialización (línea 0) en el primer uso de cada variable y la combinación de líneas de 2" y 3":
n=100
all = n * (n-1)
favorable = 2 * floor(n/2)
return(favorable / all)
En este punto, un humano podría ejecutar el programa. Vamos a hacerlo con $n=100$:
all = 100 * (100-1) = 100*99
favorable = 2 * floor(100/2) = 2*50 = 100
favorable/all = 100 / (100*99) = 1/99
El resultado por tanto es $1/99$.
Para resumir, un ataque de fuerza bruta algoritmo puede ser transformado de forma sistemática el uso de simple programa de reglas de reescritura en un elegante, elegante, $O(1)$ programa.