Autor Tema: Programación de Calculadoras

0 Usuarios y 1 Visitante están viendo este tema.

30 Enero, 2012, 01:57 pm
Leído 743 veces

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,797
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
(Lo voy a resubir al foro público cuando esté algo más "rellenito")

Problema de la Calculadora

Abstract

En este artículo resolveremos el problema de la calculadora en cada uno de los lenguajes de programación más populares.


Es común que aparezca como ejercicio el problema de hacer un programa que simule una calculadora.
Este problema en general es de fácil solución, sin embargo depende obviamente del lenguaje de programación en que debe implementarse.

¿Para qué?
Las operaciones aritméticas y las funciones matemáticas son el modo más sencillo de sumergirse en el mundo de la programación. Uno tiene un cálculo que hacer, y para ello utiliza las herramientas matemáticas del lenguaje a fin de resolverlo.

Al hacer esto intervienen varios factores sutiles e importantes, que merecen discusión, y a los que prestaremos debida atención:

  • La computadora no tiene a su disposición infinitos dígitos, ni infinita precisión. Luego los números enteros tienen un máximo valor posible, y los números decimales están limitados a un rango muy limitado de números racionales (ni hablar de los irracionales, que no tienen representación directa).

  • La falta de rango y/o de precisión de la computadora hace que inmediatamente obtengamos resultados "erróneos" en los cálculos, y tengamos que ajustar parámetros o dar rodeos para dar con la solución matemática correcta.

  • También está el problema de cómo introducir o mostrar los datos numéricos. Para ello se deben atender distintos formatos posibles, y todos ellos a su vez limitados por las capacidades de representación de las computadoras.

  • Una complicación adicional es que diferentes entornos de computación, o diferentes computadoras, tienen distintos rangos y/o grados de precisión aritmética. Esto trae, pues, una complicación adicional.

El mero intento, pues, de querer utilizar una computadora para resolver problemas matemáticos, está rodeado de variadas complicaciones, cuyo entendimiento nos hará comprender mejor cómo funcionan las máquinas, y cómo se distinguen del pensamiento matemático.


Sistemas numéricos en la matemática.

En la matemática tenemos los siguientes típicos conjuntos de números:

  • \( \color{blue}\mathbb{N} \): Conjunto de números naturales:
    \( \color{blue}\mathbb{N}=\{0,1,2,3,\ldots\}. \)

  • \( \color{blue}\mathbb{Z} \): Conjunto de números enteros:
    \( \color{blue}\mathbb{Z}=\{\ldots,-3,-2,-1,0,1,2,3,\ldots\}. \)

  • \( \color{blue}\mathbb{Q} \): Conjunto de números racionales:
    \( \color{blue}\mathbb{Q}=\{\dfrac pq:\quad p\in\mathbb{Z},\quad q\in\mathbb{N}\}. \)

  • \( \color{blue}\mathbb{R} \): Conjunto de números reales:
    \( \displaystyle\color{blue}\mathbb{R}=\{\sum_{k=-\infty}^n d_k\cdot 10^{k}:\quad n\in\mathbb{Z},\quad \forall k\leq n: \, d_k=0,1,\ldots,9\}. \)

  • \( \color{blue}\mathbb{C} \): Conjunto de números complejos:
    \( \color{blue}\mathbb{C}=\{a+ib:\quad a,b\in\mathbb R\}. \)


Cada conjunto puede pensarse como un subconjunto del que le sigue, así:

\( \mathbb{N}\subset\mathbb{Z}\subset\mathbb{Q}\subset\mathbb{R}\subset\mathbb{C} \)



Aritmética y funciones matemáticas en la computadora.

En los sistemas computacionales no tenemos acceso a estos conjuntos infinitos y maravillosos de números. Tenemos que conformarnos con subconjuntos reducidos, y con aproximaciones.


La principal diferencia entre una computadora y una mente humana es la distintición entre lo finito y lo infinito.

Nuestra mente intuye lo infinito al parecer sin dificultad alguna.
Pero para una máquina el concepto de infinito es un sinsentido.
No es físicamente posible escribir infinitos dígitos, ni hacer cálculos con un grado de precisión arbitrariamente grande en forma directa.

Se logra también distinguir el infinito "real" del infinito "potencial".

Las computadoras permiten aproximarnos a la versión "iterativa" del infinito: yendo un paso a la vez, siempre hacia adelante. Mientras que con la imaginación de nuestra mente podemos imaginar un conjunto infinito "de un solo golpe". Esto computacionalmente no es posible.

Así pues, de un problema sencillo, podemos toparnos en el camino con muchos asuntos de interés teórico.




A continuación damos un listado de los lenguajes (de programación) en los que deseamos abordar el problema de la calculadora.

  • Python
  • C
  • C++
  • Pascal
  • Ruby
  • Matlab
  • Maple
  • Java
  • C#
  • ¿Algún otro?

En caso de que ocurran muchos comentarios, se abrirá un hilo adicional para los mismos.

30 Enero, 2012, 02:08 pm
Respuesta #1

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,797
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
0. Instalando Python

Para trabajar en Python vamos a elegir la versión 3.0 y posteriores modificaciones, las cuales se engloban como Python 3.x.
La instalación se puede realizar del siguiente modo:

  • Ir a la página web de descargas de Python:


  • Buscar la versión que corresponde a nuestro sistema. Ejemplificaremos con Windows, por ser el sistema de más amplio uso actualmente:

    Python 3.2.2 Windows x86 MSI Installer (Windows binary -- does not include source)

  • Hacemos clic allí y seguimos todas las instrucciones de descarga e instalación.

  • Una vez finalizada la instalación, vamos al menú de Programas, y hacemos clic en el submenú que corresponde a Python 3, y allí hacemos clic en la opción

    IDLE (Python GUI)

  • Se abrirá una ventana con un entorno que permite ejecutar instrucciones y programas del lenguaje Python.
    Esperamos a que aparezca el prompt >>>, el cual indica que el entorno de ejecución está listo para recibir órdenes.



Nota importante: Principalmente realizaré los ejemplos en la versión 3.0 de Python. En caso de que la diferencia de versiones produzca resultados muy diferentes o erróneos, háganmelo saber.

31 Enero, 2012, 12:02 am
Respuesta #2

argentinator

  • Consultar la FIRMAPEDIA
  • Administrador
  • Mensajes: 7,797
  • País: ar
  • Karma: +0/-0
  • Sexo: Masculino
1. Tipos de datos y operaciones aritméticas en Python

Para lo que sigue nos apoyamos en el tutorial oficial de Python.
En concreto, el tema de usar Python como una calculadora:


Como es usual en muchos lenguajes de programación, los operadores aritméticos básicos en Python son los siguientes:

  • \( + \): Suma.
  • \( - \): Resta.
  • \( * \): Multiplicación.
  • \( / \): División.

También existen en Python estas otras operaciones:

  • \( // \): Cociente de la división entera.
  • %: Resto de la división entera.
  • \( ** \): Potenciación.

Conviene experimenar con estas operaciones a ver qué es lo que hace Python con ellas.

Ejemplos:



>>> 2+2
4
>>> 6.1-4.1
2.0
>>> 2.1*5.37
11.277000000000001
>>> 7/3
2.3333333333333335
>>> 11//3
3
>>> 5 % 3
2
>>> -3**6
-729
>>> (-3)**6
729
>>> 5**(4/3)
8.5498797333834844
>>>




Se observa rápidamente que cuando aparecen números no enteros en los cálculos, Python pareciera cometer errores en las últimas cifras.

En realidad esto se debe a los siguientes motivos:

  • El intérprete Python no trabaja directamente con dígitos en el formato decimal.
  • Más bien, Python toma los datos que le hemos dado en formato decimal, y los convierte a base binaria.
  • Una vez hecha la conversión, la representación en base 2 de los datos intenta ajustarse al formato estándar de las variables de punto flotante que toda computadora maneja internamente.
    Por lo general, la representación en binario introduce un desarrollo periódico que se ve se ve truncado abruptamente por la computadora, al intentar encajar en una variable de punto flotante.
  • Estos truncamientos introducen errores de cómputo obligadamente.
    Al finalizar los cálculos, ocurren de nuevo truncaciones intermedias.
  • El resultado final se vuelve a convertir a base decimal, y los errores intermedios que hubo en las conversiones y truncamientos se reflejan en la aparición de errores de cómputo en los últimos dígitos, que a veces son obvios.

Ahora bien. Si el programa nos va a dar casi siempre resultados erróneos, ¿para qué sirve?

Bueno, se supone que uno, como programador, conoce las limitaciones de las computadoras, y sabe que estos errores de cómputo son inevitables.
Entonces se conforma con saber que los resultados obtenidos son aproximaciones al resultado real, y procura tener una estimación del error.

Uno se queda, pues, sólo con los primeros dígitos de un cierto cálculo, que son los dígitos "confiables" de la operación efectuada.

Si los errores caen dentro de cierto umbral de tolerancia, entonces uno puede manejarse con confianza en el mundo de los números en punto flotante.



Python 3.x soporta los siguientes tipos de datos numéricos:

(Ver http://docs.python.org/py3k/reference/datamodel.html#the-standard-type-hierarchy)

  • Entero: Representa un número entero de tamaño arbitrario.

    O sea, se supone que cualquier elemento \( n \) del conjunto \( \mathbb Z \) de números enteros puede representarse con el correspondiente tipo de datos en Python.

    El manual de referencia indica que sólo la memoria virtual, o quizás otras limitaciones de nuestra computadora, son las únicas que pueden poner cotas al tamaño de los números enteros representados.

    O sea que Python tiene, en teoría, una estructura que le permite representar cualquier número entero.

    Yo tengo mis reservas sobre esto.
    Y no porque no pueda hacerse, sino que estuve espiando el código fuente de la implementación del tipo entero de Python, y no me quedó claro que sea capaz de lograr lo que realmente promete.

    Es claro sin embargo que Python puede representar enteros muy, pero que muy grandes.
    Esto es una ventaja respecto a la mayoría de los lenguajes de programación existentes.

  • Punto Flotante: Con este tipo de dato se intenta simular el conjuntos de los números reales. Pero en verdad, dado que el número de dígitos es finito, sólo se puede representar un subconjunto de números racionales.

    Se pueden representar números de valor absoluto muy grande o muy pequeño, pero sin embargo hay una limitación en el número máximo de dígitos con que se puede trabajar.
    Esta limitación depende del sistema subyacente.

    Para "moverse" a través de distintos cantidades muy grandes o muy pequeñas, se utiliza la notación exponencial. Por ejemplo, un número como 45000000, en punto flotante, se puede representar en Python de forma abreviada como 4.5e+7, que significa \( 4.5 \cdot 10^{7} \), y que equivale en Python a  4.5 * 10 ** 7.

  • Complejo: Es un dato que se considera como un par de dos números en punto flotante. La primer componente del par vendría a ser la parte real, y la segunda componente la parte imaginaria.

    La parte imaginaria se acompaña de la unidad imaginaria, que en Python se denota con j. He aquí un ejemplo de multiplicación de complejos:



    >>> (3+5j)*(2-2j)
    (16+4j)





Algunas peculiaridades de la aritmética con Python.

Cuando un cálculo con números en punto flotante sobrepasa los límites de la máxima cantidad representable, se considera una situación de error. El intérprete de Python nos avisa que se ha producido un error de overflow, que significa que el resultado es demasiado grande para poder ser almacenado en una variable de tipo punto flotante.

Un ejemplo rápido que muestra esto:


>>> 2.1 ** 1000
Traceback (most recent call last):
  File "<pyshell#45>", line 1, in <module>
    2.1 ** 1000
OverflowError: (34, 'Result too large')


Como es lógico, si ponemos un número mayor que 2.1 en el cálculo anterior, obtendremos un resultado aún más grande, y por lo tanto habrá un OverflowError otra vez.

Sin embargo, si el número en cuestión fuese entero, este error no se produce, y en cambio Python nos da un resultado concreto y exacto:


>>> 3 ** 1000
13220708194808066368904552597521443659654220327521481676649203682268285973467048995
40778313850608061963909777696872582355950954582100618911865342725257953674027620225
19832080387801477422896484127439040011758861804112894781562309443806156617305408667
44905061781254803444055470543970388958174653682549161362208302685637785822902284163
98307887896918556404084898937609373242171846359938695516765018940588109060426089671
438864102814350385648747165832010614366132173102768902855220001


Obviamente, esto pasa porque Python no tiene problemas con los números enteros de cualquier tamaño. Sólo tiene problemas con los números en punto flotante.

Esta curiosidad yo la considero más bien una anomalía de Python, una especie de inconsistencia.