Ok, hay algunas maneras de responder esto.
Para algo relativamente simple, como y = x ^ 2 + 3, la respuesta es igualmente simple. Esta ecuación se traduciría a los siguientes pasos:
- Obtenga la dirección de memoria de la variable x y almacene ese valor en uno de los registros del procesador.
- Obtenga el contenido de la dirección de memoria señalada por esa dirección y colóquela en el acumulador.
- Multiplica ese valor por sí mismo.
- Añadir 3.
- Obtenga la dirección de memoria de la variable y y almacene ese valor en uno de los registros del procesador.
- Tome los contenidos del acumulador y colóquelos en la ubicación de memoria señalada.
Cada variable tiene, asociada con ella, una ubicación en la memoria y la computadora puede extraer el valor desde allí o introducir un nuevo valor allí. Esa ubicación en la memoria también se almacena en la memoria. A veces, un compilador creará una tabla de búsqueda que hace esta asociación, y simplemente sustituirá esa dirección en todas partes donde aparezca la variable en el programa. Otras veces, se adjunta una tabla de búsqueda a ese segmento de código y la variable se sustituirá por el lugar de la tabla donde debe buscar la dirección. La dirección se crea en el momento en que se ejecuta ese segmento de código.
- ¿Será difícil ingresar a una escuela de posgrado en astronomía de un entorno no tradicional (especializaciones diferentes a astronomía, física, matemáticas, CS, etc.)?
- ¿Cuándo es una función sub o supermultiplicativa?
- ¿Qué es un diagrama de máquina de Turing y cómo diseño uno?
- Cómo calcular la función de rango en Excel
- Teóricamente, ¿se puede implementar algún algoritmo en el marco de MapReduce?
Para aclarar esto un poco, imaginemos que hay una función llamada add, que simplemente suma dos números, colocando el resultado en x. En pseudocódigo:
la función add (entero a, entero b) devuelve un entero
empezar
entero x;
x = a + b;
retorno (x);
fin
Ok, entonces cuando se llama a esta función, tendrá una tabla de búsqueda que se parece a:
a: (inicio del espacio de datos para agregar) + 0;
b: (inicio del espacio de datos para agregar) + 2; (Los enteros son dos bytes).
x: (inicio del espacio de datos para agregar) + 4;
El paso 1 se convierte en esto:
1.1: La variable de detección que se utilizará es la tercera variable en la tabla (por lo tanto, se le asigna un valor 0f 2, ya que los valores comienzan en 0).
1.2: Las direcciones tienen una longitud fija, por lo tanto, multiplique 2 por el ancho de la dirección. En las computadoras modernas, eso es ocho bytes. Entonces leemos en ocho bytes, comenzando en la posición 16 desde el comienzo de la tabla.
1.3 Almacenar esos datos en un registro temporal, llamémoslo registro b.
El registro b ahora apunta a la memoria para la variable de interés. Podría entrar en direccionamiento indirecto, etc., pero realmente no vale la pena. Tienes la idea básica.
Entonces, ¿qué pasa con las cosas realmente complicadas, donde realmente ingresas en la ecuación cuando el programa se está ejecutando? ¡No puede hacer esto de la misma manera, seguramente!
Bueno, en cierto modo, lo hace de la misma manera. La cadena se analiza primero, en otras palabras, conviértela en una representación que la computadora pueda usar.
Un método para analizar esto es identificar cada vez que se usa una variable y cómo se usa, pero en el método que voy a mostrar, lo hace de la última a la primera. Entonces, en este caso, tendrías: +, 3, *, x, +, x. Esto se empuja sobre lo que se llama una pila. Con una pila, lo primero que se empuja es lo último que se recupera, por lo que se invierte el orden. Como resultado, lo que sale siempre está en el orden inverso. En este método, comienza con un valor de 0 en su registro de trabajo y siempre extrae dos elementos de la pila. El primero es el número o la variable con la que está trabajando, el segundo es un operador. No hay excepciones La secuencia de operaciones se convierte en:
- Establezca el acumulador en 0.
- Recupere los dos valores superiores en la pila.
- El primero es x, por lo tanto, recupere el valor de x e introdúzcalo en el registro temporal b.
- El segundo es agregar. La operación se realiza entre el registro temporal y el acumulador.
- El resultado se empuja al acumulador.
- Los pasos 2 a 5 se repiten hasta que no queden elementos.
Esto da como resultado el siguiente cálculo: ((0 + x) * x) + 3, que es lo mismo que x ^ 2 + 3. Como el código no cambia, siempre es un operador nuevo y un operando nuevo, Es muy rápido de realizar.
Debido a que este método convierte lo que ha escrito en una operación simple entre dos parámetros, no puede hacer ciertos cálculos. (7 + 3) * (8 + 2) no se pudo hacer porque no hay forma de expandir dos conjuntos de paréntesis. Lo que puede hacer es modificar este enfoque para crear valores intermedios, luego tratar los valores intermedios como variables de las que puede extraer el valor.
Lo que está haciendo aquí es dividir un cálculo en muchos cálculos mucho más simples, cada uno de los cuales es solo una operación simple entre dos valores.
Este enfoque es realmente muy poderoso en las computadoras modernas, porque puedes hacer muchas cosas en paralelo. En lugar de hacer cálculos simples uno tras otro, puede hacerlos todos al mismo tiempo, cada uno realizado en una CPU o GPU diferente. Los resultados intermedios se recopilan y el proceso se repite. Los cálculos se vuelven increíblemente rápidos. La Universidad de Manchester intentó escribir compiladores que hicieron exactamente esto, en la década de 1970, pero las computadoras en ese entonces no estaban a la altura y la velocidad de comunicación era tan grande que no hubo ningún beneficio. Hoy, algunos microprocesadores intentan hacer este tipo de cosas internamente.
Las ecuaciones realmente complicadas, como las gráficas, dan como resultado que el cálculo se realice muchas veces. Dado que ya hemos descrito un método para hacer muchos cálculos diferentes y almacenar sus resultados en diferentes lugares (los resultados intermedios antes de hacer la siguiente ronda de cálculos), lo que he descrito anteriormente es tan bueno para gráficos, vectores y matrices como para valores individuales
Los probadores de teoremas, la siguiente etapa más allá de esto, usan analizadores bastante más sofisticados para reducir los teoremas a una forma más manejable. Existen muchos métodos posibles, pero dos de ellos son la simplificación y la resolución. La simplificación simplemente sustituye valores y se cancela. Si esto da como resultado que el teorema declare que algo es igual a sí mismo (lo cual es cierto), entonces el teorema es obviamente cierto. Nunca puede no igualarse a sí mismo. Resolver es similar a la simplificación, ya que está tratando de mostrar que los dos lados son iguales, pero lo hace numéricamente en lugar de algebraicamente. Eso está bien cuando trabajas con un número finito (en realidad, pequeño) de valores discretos para cada variable, de modo que se puedan probar las posibilidades. Es bueno para números discretos porque los estados indefinidos no son necesariamente estados iguales. Un teorema puede ser verdadero en todos los casos sobre los que se define, pero nominalmente falso en algún caso indefinido. Si intenta usar métodos algebraicos solos cuando usa lógica discreta, es probable que obtenga resultados muy extraños.