jueves, 6 de mayo de 2021

Comentarios sobre la implementación de la división entera en alguna herramientas de cálculo

Las instrucciones básicas de muchas herramientas de cálculo no dan el resultado esperado al utilizar las instrucciones al uso para efectuar calcular el resto y el cociente de una división entera si el dividendo o bien el divisor son, en general, negativos. Probando algunas de estas instrucciones con el lenguaje de programación/herramienta de cálculo Python me he dado cuenta de ello. También lo he comprobado con la calculadora TI Voyage 200 y con el programa de cálculo simbólico MAXIMA, con los mismos resultados incorrectos de acuerdo con el teorema de la división entera (TDE), que recordemos que afirma lo siguiente:

Sean dos números enteros $a$ ( dividendo ) y $b\neq 0$ ( divisor ), entonces existen otros dos números enteros $q$ (cociente) y $r$ (resto), únicos, tales que:
i) $a=bq+r$
ii) $0\le r \prec |b|$

Por tanto, hay que tener muy en cuenta estos resultados inesperados en los cálculos con programas y herramientas de cálculo en los que se hagan uso de las instrucciones 'cociente' y 'resto' al trabajar con números enteros. No obstante, todo va bien si el dividendo y el divisor son positivos, incluso si el dividendo es negativo y el divisor positivo, pero se obtienen respuestas incorrectas.

-oOo-

He hecho las siguientes pruebas, por si queréis reproducirlas:

Toda va bien ...
Python responde con los resultados correctos del cociente y el resto si el dividendo y el divisor son positivos, incluso si el dividendo es menor que el divisor; y también si el dividendo es negativo y el divisor es positivo. Así, por ejemplo, según el TDE, si a:=9 y b:=5, entonces $r=4$ y $q=1$ ( lápiz y papel ), y, mediante Python, se obtienen estos resultados: para a//b (cociende de a/b), 1 y para a%b (resto de a/b ), 4. Así, pues, todo va bien con números enteros positivos. También se obtienen los resultados correctos si $a\prec b$ ( el dividendo es menor que el divisor ): el cociente ha de ser cero, q=0, y el resto ha de ser igual al divisor, r=b; en efeto, pongamos que $a:=1$ y $b:=2$, con Python obtenemos también en este caso lo que se espera a//b es 0 y de a%b, 2. Incluso si el dividendo es negativo y el divisor postivo, también se obtiene lo correcto; por ejemplo, si $a:=-9$ y $b:=5$, el cociente, según el TDE, ha de ser -2 y el resto 1, y, así es, en efecto: haciendo a//b se obtiene -2 y haciendo a%b, 1, como debe ser.

Pero se obtienen respuestas inesperadas en otros casos ...
Ahora bien, Python da respuestas incorrectas -- como también las da MAXIMA, así como la calculadora TI Voyage 200 ( No lo he probado de momento con otras herramientas ) -- para el resto y el cociente en los otros casos, cuando el dividendo es positivo y el divisor es negativo, y cuando ambos son negativos. Por ejemplo, según el TDE, si a:=9 y b:=-5, entonces r=4 y q=-1; y, sin embargo, estas no son las respuestas de Python: haciendo a//b se obtiene como respuesta -2 (cociente) y haciendo a%b, -1 (resto). Veamos a continuación otro ejemplo de la incorrección de los resultados obtenidos, para a:=-9 y b:=-5, según el TDE, r=1 y q=-2; ahora bien, con Python, a//b responde 1 ( y no -2) y a&b, -4, en lugar de 1.

$\square$

Expresión de un número entero (dado en base decimal) en el sistema hexadecimal

En ingeniería electrónica y programación se utilizan también otros sistemas de numeración además del decimal. El sistema binario, en el que cada palabra se codifica con unos y ceros, es el código más próximo a la máquina, si bien los ingenieros, al programar los microprocesadores, también utilizan a menudo el sistema hexadecimal para direccionar la información en los registros de memoria. En el sistema hexadecimal se utilizan 16 caracteres para componer 'palabras': {0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F}, por lo que
$0_{10}=0_{16}$
$1_{10}=1_{16}$
$2_{10}=2_{16}$
$3_{10}=3_{16}$
$4_{10}=4_{16}$
$5_{10}=5_{16}$
$6_{10}=6_{16}$
$7_{10}=7_{16}$
$8_{10}=8_{16}$
$9_{10}=9_{16}$
$10_{10}=A_{16}$
$11_{10}=B_{16}$
$12_{10}=C_{16}$
$13_{10}=D_{16}$
$14_{10}=E_{16}$
$15_{10}=F_{16}$
$\vdots$

Para expresar el número $589_{10}$ en hexadecimal hacemos lo siguiente:
1.º Lo expresamos en base 2, de la manera que ya habéis estudiado en clase y tal como podéis repasar en esta otra entrada del blog
    $583_{10}=001001001101_{2}$
2.º A continuación, separamos dicha palabra binaria en grupos de cuatro, empezando por el bit de la derecha ( el bit menos significativo ), rellenando con los ceros necesarios a la izquierda para completar el último grupo de cuatro bits ( en este caso es innecesario, pues el número total de bits es múltiplo 12, que es múltiplo de 4 )
    $0010\;0100\;1101$
3.º Ahora expresamos cada grupo de cuatro bits en base 10 ( empezando por el primero a la izquierda ), y, según las correspondencias que hemos deducido arriba, escribimos el carácter en hexadecimal que tenga asociado. Así,
    $0010_{2}=2_{10}=2_{16}$
    $0100_{2}=4_{10}=4_{16}$
    $1101_{2}=13_{10}=D_{16}$
4.º Y finalmente concatenamos los resultados obtenidos:
    $583_{10}=24D_{16}$

NOTA: Observemos que, de acuerdo con el desarrollo de pontencias en base $16$, ésto significa que
$$24D_{16}=2\cdot 16^2+4\cdot 16^1+ 13\cdot 16^0=2\cdot 256+4\cdot 16+13\cdot 1=589_{10}$$
como debe ser. $\square$

Conversión de un número entero (dado en el sistema de numeración decimal) al sistema binario

En ingeniería electrónica y computación es importante saber expresar un número (dado en el sistema de numeración decimal) en el sistema binario. El sistema binario consta de sólo dos caracteres 'alfabéticos', el 0 y el 1. Cada uno de estos dos valores da nombre a la unidad de información matemática, el bit ( del inglés, 'binary unit' ).

Así, concatenando los signos básicos, podemos decir que
$0_{10}=0_{2}$
$1_{10}=1_{2}$
$2_{10}=10_{2}$
$3_{10}=11_{2}$
$4_{10}=100_{2}$
$\vdots$

Démonos cuenta de que ésto es así porqué, al expresar los números en suma de potencias de base 2, efectivamente se obtienen los números en base 10; por ejemplo:
$100_{2}=1\cdot 2^2+0\cdot 2^1+0\cdot 2^0=4+0+0=4_{10}$

Nos interesa, ahora, asentar un procedimiento que permita la transformación de decimal a binario a partir de un número arbitrariamente grande, y no sólo uno de los números pequeños a los que nos hemos referido arriba como ejemplos; así, por ejemplo:

$19_{10}=\square\,\square\,\ldots\,\square_{2}$
Observemos que al efectuar la división entera $17$ entre $2$ ( que es el número de símbolos del 'alfabeto' binario ) obtenemos ( por el teorema de la división entera):
$19=9\cdot 2 +1$
y dividiendo otra vez,
$9=4\cdot 2 +1$
y seguimos ...
$4=2\cdot 2 +0$
hasta que, finalmente, llegamos a la última división posible,
$2=1\cdot 2 +0$

Pues, bien, concatenando los unos y ceros que se obtienen empezando por el cociente de la última división, y, siguiendo la concatenación con los restos, desde la última división a la primera ( de bits menos significativos a bits más significativos ), resulta:
$$19_{10}=10011_{2}$$
Cualquier número expresado en base 10 se convierte, siguiendo este algoritmo, en su expresión en base 2.

Comprobémoslo, en el ejemplo expuesto, escribiendo el desarrollo en sumas de potencias de base 2, para ver si obtenemos el número dado expresado en base 10. En efecto,
$$10011_{2}=1\cdot 2^4+0\cdot 2^3+0\cdot 2^2+1\cdot 2^1+1\cdot 2^0=16+0+0+2+1=19_{10}$$
$\square$


Cómo expresar en el sistema de numeración binario la parte fraccionaria de un número dado en el sistema de numeració decimal

En otras entradas del blog ya hemos hablado de cómo expresar en el sistema binario ( con unos y ceros, que es su expresión en el lenguaje máquina ) un número entero que venga dado en el sistema de numeración decimal. Consideremos por ejemplo el número $11_{10}$ ( en base $10$ ), resultando que $11_{10}=1011_{2}$ (en base binaria o base $2$, con tan sólo los símbolos $\{0,1\}$, a los que denominamos bits.

Ésto es así porqué, dividiendo sucesivamente por $2$ ( que es el número de símbolos del alfabeto de numeración binario ) el número en cuestión, y teniendo en cuenta el teorema de la división entera, al llevar a cabo las divisiones sucesivas de $11$ entre $2$:
$11=5 \cdot 2+\textbf{1}$
$5=2\cdot 2+\textbf{1}$
$2=\textbf{1}\cdot 2+\textbf{0}$
hasta llegar a una división con resto cero - en este caso, la tercera -, tomamos el bit menos significativa como el valor del último cociente, seguido por el bit que corresponde al resto de dicha división, y, así, siguiendo con las divisiones anteriores hasta obtener el bit más significativo, que corresponde al resto de la primera división. En efecto, al escribir el número en forma desarrollada, $1\cdot 2^3+0\cdot 2^2+1\cdot 2^1+ 1\cdot 2^0$, vemos que dicha suma, $8+2+1$, es igual a $11_{10}$

-oOo-

Ya sabemos hacer lo anterior; sin embargo, nos interesa también aprender a expresar en base binaria la parte fraccionaria de un número que viene dado en base diez. Consideremos pues la parte fraccionaria de un número dado en el sistema de numración decimal; por ejemplo, de $11,316_{10}$, esto es $\text{frac}(11,316_{10})=0,316_{10}$ . Veamos cómo podemos expresarla pues en el sistema de numeración binario ( con ceros y unos ).

Como observación preliminar - que tal vez resulte una obviedad al operar en base $10$, si bien nos guiará para hacer lo análogo al operar en base $2$ - démonos cuenta de que para obtener de manera mecánica las cifras decimales de dicho número podemos proceder de la siguiente manera ( pongamos que quién lo haga es un ordenador, y así deberíamos programarlo ):

i) Tomemos la parte fraccionaria, $0,316_{10}$, y multipliquémosla por la base de numeración - que es $10$ -, $0,316_{10} \cdot 10 =3,16_{10}$, lo cual, obviamente, nos proporciona la cifra de las décimas: $3$

ii) Tomamos ahora la parte fraccionaria de $3,16_{10}$, $\text{frac}(3,16_{10})=0,16_{10}$, y multipliquémosla por la base de numeración, $10$, así $0,16_{10} \cdot 10 =1,6_{10}$, lo cual nos proporciona la cifra de las centésimas: $1$

iii) Y, finalmente - puesto que sólo hay tres cifras decimales -, tomamos la parte fraccionaria de $1,6_{10}$, $\text{frac}(1,6_{10})=0,6_{10}$, y multipliquémosla por la base de numeración, $10$, así $0,6_{10} \cdot 10 =6_{10}$, lo cual nos proporciona la cifra de las milésimas: $6$

Fijémonos en el hecho de que, al escribir dicha cantidad en forma desarrollada: $$3\cdot 10^{-1}+1\cdot 10^{-2}+6\cdot 10^{-3}$$ obtenemos, como es bien claro, dicha parte fraccionaria:
$$0,3+0,01+0,006= 0,316$$

-oOo-

Pues bien, ahora vamos a proceder de manera análoga, con $0,632_{10}$ ( la parte decimal de $11,632_{10}$ ) pero expresándonos en base $2$, al objeto de ir obteniendo las cifras ( unos y ceros ) de las partes enteras que vayan resultando en los sucesivos cálculos, y que iremos concatenando desde el bit más significativo al menos significativo, $$.\square \,\square\, \ldots$$ en el orden que vayamos realizando dichas operaciones -- Nota: por $\text{ent}(.)$ nos referiremos a la cifra que quede a la izquierda de la coma/punto decimal, que será un uno o bien un cero --:

$\text{frac}(0,632_{10})\cdot 2=0,316_{10}\cdot 2=0,632 \rightarrow \text{ent}(\textbf{0},632_{10})=\textbf{0} \rightarrow \;.\textbf{0} \square \, \square \, \ldots_{2}$
$\text{frac}(0,632_{10})\cdot 2=0,632_{10}\cdot 2=1,264 \rightarrow \text{ent}(\textbf{1},264_{10})=\textbf{1} \rightarrow \;.0\textbf{1} \square \, \square \, \ldots_{2}$
$\text{frac}(1,264_{10})\cdot 2=0,264_{10}\cdot 2=0,528 \rightarrow \text{ent}(\textbf{0},528_{10})=\textbf{0} \rightarrow \;.01\textbf{0} \square \, \square \, \ldots_{2}$
$\text{frac}(0,528_{10})\cdot 2=0,528_{10}\cdot 2=1,056 \rightarrow \text{ent}(\textbf{1},056_{10})=\textbf{1} \rightarrow \;.010\textbf{1} \square \, \square \, \ldots_{2}$
$\text{frac}(1,056_{10})\cdot 2=0,056_{10}\cdot 2=0,112 \rightarrow \text{ent}(\textbf{0},112_{10})=\textbf{0} \rightarrow \;.0101\textbf{0} \square \, \square \, \ldots_{2}$
$\text{frac}(0,112_{10})\cdot 2=0,112_{10}\cdot 2=0,224 \rightarrow \text{ent}(\textbf{0},224_{10})=\textbf{0} \rightarrow \;.01010\textbf{0} \square \, \square \, \ldots_{2}$
$\text{frac}(0,224_{10})\cdot 2=0,224_{10}\cdot 2=0,448 \rightarrow \text{ent}(\textbf{0},448_{10})=\textbf{0} \rightarrow \;.010100\textbf{0} \square \, \square \, \ldots_{2}$
$\text{frac}(0,448_{10})\cdot 2=0,448_{10}\cdot 2=0,896 \rightarrow \text{ent}(\textbf{0},896_{10})=\textbf{0} \rightarrow \;.0101000\textbf{0} \square \, \square \, \ldots_{2}$
$\vdots$

-oOo-

Nos encontramos ahora con la necesidad de reflexionar acerca del error de representación finita de dicha cantidad (en los registros binarios de un computador, que, naturalmente, son de longitud finita), puesto que, al tener ésta infinitos bits, necesariamente debe ser aproximada para poder ser representada (con el número finito de bits de los registros de memoria de datos de la máquina).

Démonos cuenta de que el proceso no termina nunca, esto es, la parte decimal del número expresada en el sistema binario tiene infinitas cifras, a pesar de que al expresarse en el sistema decimal dicha parte decimal sea finita. Ésto plantea un problema importante: a la hora de representar en los registros del computador dicha cantidad - que tienen una longitud finita - deberemos cortar la parte decimal en algún lugar, lo cual introduce un inevitable error (de representación).

En la representación finita ( de la máquina ) en coma flotante en lo que viene a llamarse "simple precisión", la mantisa - que es el número decimal del tipo $.01010000 \ldots$ - tiene una longitud de $23$ bits. Así que podríamos continuar el proceso anterior hasta sacar $15$ cifras binarias (bits) más. Sin embargo, veamos que sucede con el error de representación si nos quedásemos sólo con $8$ bits, esto es $0,632_{10} \approx .01010000_{2}$ [ Nota: Al expresar la cantidad en el sistema binario, suele utilizarse el punto decimal (en lugar de la coma, usada en nuestro país habitualmente en la expresiones en el sistema de numeración decimal) para indicar que a su derecha escribiremos las cifras binarias correspondientes a la parte fraccionaria del número en cuestión ].

La representación finita $.01010000_{2}$ corresponde a la siguiente cantidad
$0\cdot 2^{-1}+1\cdot 2^{-2}+0\cdot 2^{-3}+1\cdot 2^{-4}+0\cdot 2^{-5}+0\cdot 2^{-6}+0\cdot 2^{-7}+0\cdot 2^{-8}=$
$=2^{-2}+2^{-4}$
$=1/4+1/16$
$=5/16$
$=0,3125_{10} \neq 0,316_{10}$

Así pues, el error absoluto de la aproximación es $|0,316-0,3125|=0,0035$, y por tanto el error relativo es de $\dfrac{0,0035}{0,316}\approx 0,00111 = 1,11\,\%$. Es claro que para aumentar la precisión debemos aumentar el número de bits de la representación, por lo que con $23$ bits, el error relativo es, seguro, muchísimo menor. Dejo a la persona lectora el interesante ejercicio de calcularlo, retomando los cálculos donde los he dejado ( en el octavo bit ).
$\square$

Expresión de un número decimal en binario

ENUNCIADO. Exprésese en base $2$ el número $13,3$ (que viene dado en base $10$), conservando en la representación finita $23$ bits en la parte fraccionaria.

SOLUCIÓN. La parte entera, $13$, es igual a $1101_{2}$; en efecto, dividiendo sucesivamente por $2$ ( que es el número de símbolos del alfabeto de numeración binario ) el número en cuestión, y teniendo en cuenta el teorema de la división entera, al llevar a cabo las divisiones sucesivas de $13$ entre $2$:
$13=6 \cdot 2+\textbf{1}$
$6=3\cdot 2+\textbf{0}$
$3=\textbf{1}\cdot 2+\textbf{1}$
hasta llegar a una división con resto cero - en este caso, la tercera -, tomamos el bit menos significativa como el valor del último cociente, seguido por el bit que corresponde al resto de dicha división, y, así, siguiendo con las divisiones anteriores hasta obtener el bit más significativo, que corresponde al resto de la primera división. En efecto, al escribir el número en forma desarrollada, $1\cdot 2^3+1\cdot 2^2+0\cdot 2^1+ 1\cdot 2^0$, vemos que dicha suma, $8+4+1$, es igual a $13_{10}$

Ocupémonos ahora de la parte fraccionaria (decimal):
$0.3\cdot 2 = 0.6 \rightarrow 0$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--\,
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$0.8\cdot 2 = 1.6 \rightarrow 1$
$0.6\cdot 2 = 1.2 \rightarrow 1$
--
$0.2\cdot 2 = 0.4 \rightarrow 0$
$0.4\cdot 2 = 0.8 \rightarrow 0$
$\vdots$
y, así, indefinidamente, por lo que la parte fraccionaria consta de infinitos bits ( unos y ceros ), con periodicidad $1100$ a partir del quinto bit de la partea fraccionaria. Así, pues, $13,3_{10}=1101.0100\,1100\,1100\,1100\,\ldots_{2}$
Como hay infinitos bits en la parte fraccionaria, tenemos que quedarnos con un número finito de ellos para poder representar dicha cantidad en los registros de memoria de un computador ( que, obviamente, tienen longitud finita ); pongamos que cortamos el bit vigésimo tercero de la parte fraccionaria, entonces:
$13,3_{10}=1101.\,0100\,1100\,1100\,1100\,1100\,1100\ldots_{2} \approx$
$\approx 1101.\,0100\,1100\,1100\,1100\,1100\,110_{2}$
El valor ( en base $10$ ) que corresponde a esta representación finita es
$1\cdot 2^3+1\cdot 2^2+0\cdot 2^1+1\cdot 2^0 + 0\cdot 2^{-1}+1\cdot 2^{-2}+0\cdot 2^{-3}+0\cdot 2^{-4}+\ldots+0\cdot 2^{-23}=$
$=2^3+2^2+1+2^{-2}+2^{-5}+2^{-6}+2^{-9}+2^{-10}+2^{-13}+2^{-14}+2^{-17}+2^{-18}+2^{-21}+2^{-22}$
$=13,2999\,9995\neq 13$
Observemos que el error absoluto de la aproximación ( por representación finita ) es igual a $$|13,3-13,2999\,9995|=0,0000\,0005$$
(esto es, de $5\times 10^{-8}$), luego el error relativo es igual a $$\dfrac{5\times 10^{-8}}{13,3}\approx 4\times 10^{-9}=4\times 10^{-7}\,\%$$
$\square$

Multiplicación de matrices en lenguaje Java

/* Aquest fitxer Matrius.java cal que estigui
ubicat dins la mateixa carpeta que el fitxer
amb les classes auxiliars

 Es compila fent
%javac Matrius.java
per obtenir el fitxer Matrius.class

I, finalment, s'engega fent
%java Matrius
*/

 
import java.io.*;
public class ProvaMatrius{ 

// aquest programa fa ús dels mètodes de la classe Matrius (Matrius.java)

  public static void main(String args[]) throws IOException {
    // inicialitzem una matriu 2x2 (cal canvira el valors
    // per poder treballar amb una matriu de diferent grandària
    int mtr[][]={{0,0},{0,0}};
    int nf=2; //nombre de files
    int nc=2; //nombre de columnes

    Matrius m = new Matrius();
    m.demanaMatriu(mtr,nf,nc);
    System.out.println("= = = the input matrix is:");
    m.mostraMatriu(mtr,nf,nc);
  }
}

Un ejercicio sencillo en lenguaje Java ( salida de un dato con formato )

/**
* Programa que dóna format a una data
**/
public class UnesDates {
public static void main(String[] args) {
java.util.Date data = new java.util.Date();
System.out.println("Una data tal qual");
System.out.println(data);
System.out.println("Una data formatada");
System.out.println(
java.text.DateFormat.getDateInstance().format(data));
}
}

Cálculos sencillos utilizando 'clases' en lenguaje Java

Aquesta classe (Volums) conté un mètode que serveix
per calcular el volum d'un cilindre (calculaVolum).
Farem ús d'aquest mètode (i, per tant, d'aquesta classe)
des d'una altra classe (ProvaPrimeraClasse) desde
la qual entrem el valor de les dades

Cal que el fitxer que conté aquesta classe (Volums.java)
estigui a la mateixa carpeta que el fitxer que conté
la classe des de la qual se'n farà ús (ProvaPrimeraClasse.java)

Cada classe de tipus 'public' ha d'estar arxivada en un fitxer
independent; no obstant això, es podria reunir vàries classes
en un sol fitxer si aquestes fossin del tipus 'private' o bé
'protected'

Classe principal


 */

public class Volums{
  public double calculaVolum(double a, double r){
    final double PI=3.14159; // una constant es declara com a 'final'
    double v;
    v = PI*Math.pow(r,2)*a;
    return v;
  }
}


Classe auxiliar


import java.io.*;

public class ProvaPrimeraClasse{
public static void main(String args[]) throws IOException {
double altura=0, radi=0;
String text;
InputStreamReader flux = new InputStreamReader(System.in);
BufferedReader teclat = new BufferedReader(flux);

System.out.println("height's value ?");
text=teclat.readLine();
altura=(Double.valueOf(text)).doubleValue();

System.out.println("value of radius ?");
text=teclat.readLine();
radi=(Double.valueOf(text)).doubleValue();

Volums v = new Volums();
System.out.println("Volum of cylinder ="+v.calculaVolum(altura,radi));
// Observem que els arguments del mètode calculaVolum (de l'objecte v)
// li són passats per valor (sempre es fa així en Java)
}
}

Multiplicación de matrices en lenguaje Java

/* Aquest fitxer ProvaMatrius.java, amb les classes auxiliars, cal que estigui
ubicat dins la mateixa carpeta que la classe principal

 Es compila fent
%javac ProvaMatrius.java

per obtenir el fitxer ProvaMatrius.class
*/

import java.io.*;

public class Matrius{

 public void demanaMatriu (int matriu[][], int nfiles, int ncolumnes ){
  int i,j;
  String text;

  InputStreamReader flux=new InputStreamReader(System.in);
  BufferedReader teclat=new BufferedReader(flux);

  try{
    for (i=0;i<nfiles;i++){
      for (j=0;j<ncolumnes;j++){
        System.out.print(" (" + i + "," + j +")=");
        text=teclat.readLine();
        matriu[i][j]=(Integer.valueOf(text)).intValue();
        }
      System.out.println(" ");
     }
  }catch (Exception e){
    System.out.println(e.getMessage());
  }
 }

 public void mostraMatriu (int matriu[][], int nfiles, int ncolumnes ){
  int i,j;
 
    for (i=0;i<nfiles;i++){
      for (j=0;j<ncolumnes;j++){
        System.out.print(matriu[i][j]+" ");
        }
      System.out.println(" ");
     }
  }


public void multiplicaMatrius (int matriu1[][], int nfiles1, int ncolumnes1, int matriu2[][], int nfiles2, int ncolumnes2, int matriu3[][], int nfiles3, int ncolumnes3){
  int i,j,k,s;
 
    for (i=0;i<nfiles1;i++){
      for (j=0;j<ncolumnes2;j++){
        s=0;
        for (k=0;k<ncolumnes2;k++){
          s+=matriu1[i][k]*matriu2[k][j]; 
        }
       matriu3[i][j]=s;
     }
  }
}
}

Un ejercicio de cálculo sencillo en lenguaje Java

import java.lang.Math;
// la classe java.lang.Math no cal expressar específicament que s'importi
//(es fa per defecte)

public class ProvaMath{
public static void main (String args[] ){

// declaració de tipus de cada variable
double p; //potència
double b; //base
double e; //exponent

// assignació de valor
b=2.0D;
e=3.0D;

// càlcul de la potència
p=Math.pow(b,e);

System.out.println("$b^e=p$");
System.out.println(b+"^"+e+"="+p);
}
}

Años bisiestos

NOTA PRELIMINAR:

Comproveu abans que tingueu instal·lat el plug in de Java (JRE versió 1.6.0_16 o bé una versió superior ) al vostre navegador.

Algunes observacions dirigides a qui li interessi la programació en llenguatge Java:
Quan vaig compilar el codi font (Anyb.java) tot just havia acabat d'actualitzar el meu JDK i el JRE a la versió més recent v1.6.0_* (octubre de 2009) i, per bé que al meu ordinador funciona perfectament (el fitxer amb els bytecodes, Anyb.class, generat en la compilació), he pogut comprovar que, malauradament, en d'altres ordinadors que encara no han actualitzat el JRE (la màquina virtual de Java o plug in dels navegadors a aquesta última versió (10/2009), no s'engega l'applet en carrregar-se. El mateix m'ha passat quan he provat de posar-lo en marxa en un Mac. Per contra, altres applets que ja havia preparat fa temps amb versions anteriors del JDK funcionen perfectament en aquestes màquines. És per això que he mirat de trobar l'antiga versió del JDK amb què treballava fa temps (j2sdk1.4.2_14) per tornar a compilar el fitxer amb el codi font (Anyb.java) [ les antigues versions les podeu trobar a http://java.sun.com/products/archive ].

És de suposar que en actualitzar-se el plug in (sovint es fa de manera automàtica) els applets generats amb les noves versions del JDK v1.6.* (o superiors) funcionaran normalment.

Espero que aquestes notes siguin d'utilitat per als companys que estiguin també preparant materials didàctics amb les versions més noves del JDK de Sun (>octubre de 2009). L'experiència també m'ha servit per aprendre a compilar amb els Mac desde la línia de comandes (comandes UNIX). El meu Mac incorpora (octubre de 2009) el jdk v1.5.* i el jre v1.5.* i interpreta bé el que he compilat amb el jdk v1.4.* en un ordinador sota Windows.
/**
* MIRANT SI UN ANY a ÉS BIXSEST
*
* Recordem que:
* Un any terrestre no equival exactament a 365 dies sino, amb més precisió,
* a 365 dies i 1/4 de dia; quan es va reformar el calendari (365 dies), per ajustar
* es va establir que, a partir d'un any bixsest,
* cada quatre anys s'afegiria un dia al mes de febrer (sempre i quan les dues darreres
* xifres no fossin zeros, a execepció dels anys múltiples de 400).
*
* Sobre l'entrada de dades del programa fent ús del teclat:
* Aquest programa fa ús del teclat per demanar quin any es vol analitzar
* quan el programa ja s'ha engegat. Per això, en general, cal definir un CANAL,
* un FLUX i un FILTRE per les dades que l'usuari comunica al programa
*/

import java.io.*; // cal importar el PAQUET java.io per poder treballar amb les
// classes que aquest incorpara d'"entrada-sortida"

public class AnyBixestCanalTeclat {
public static void main(String args[]) throws IOException {
// fem ús del modificador IOException per capturar i desprendre'ns
// de les "excepcions" (errors) que es puguin donar
int any;
boolean bixsest;
String text_any;

// El següent CONSTRUCTOR farà possible el "FLUX"
// de dades (l'objecte "System.in")
InputStreamReader flux = new InputStreamReader(System.in);
// I aquest altre CONSTRUCTOR habilita el teclat com a "CANAL" (l'objecte corresponent)
// per a les dades que s'entrin
BufferedReader teclat = new BufferedReader(flux);

// Es posa en pràctica l'entrada de la paraula "text_any"
// i que entrem en forma de guarisme (tot i que, en aquest moment, pel programa és
// una cadena de caràcters
System.out.println("Quin any voleu analitzar ?");
text_any = teclat.readLine();
// primer que res, cal convertir aquesta paraula a un enter
any = (Integer.valueOf(text_any)).intValue();

// I, a partir d'aquí, fem el càlcul convenient per analitzar si l'any és bixsest
bixsest = ((any % 4 == 0) && (any % 100 !=0)) || (any % 400 == 0);

// I, finalment, tenim la sortida del programa ... que fem efectiva amb
// el MÈTODE println de l'objecte System.out (el canal i el flux de sortida
// ja estan habilitats, per defecte)
if(bixsest){System.out.println("L'any " + any + " és bixest");}
else{System.out.println("L'any " + any + " no és bixest");};
}
}

Entrada de datos por teclado en lenguaje Java

import java.io.*;
public class ProvaEntradaTeclat{
public static void main (String args[] ) throws IOException {

// declaració de tipus de la variable corresponent al nomb que entrem
String nomQueEntrem;

// declaració de les variables referenciades flux i teclat(canal)
InputStreamReader flux; // flux
BufferedReader teclat; // canal

// construcció dels objectes flux i teclat(canal)
flux = new InputStreamReader(System.in); // System.in identifica el teclat com a prfr d'entrada
teclat = new BufferedReader(flux);

System.out.println("type your name, please: ");
nomQueEntrem = teclat.readLine();
System.out.println("Your name is: "+nomQueEntrem);
}
}

Elaboración de un paquete de código ( package ) en lenguaje Java

package ExempleCase; // Empaquetant

/**
Per engegar el programa empaquetat cal que feu
java -jar "camí_del_fitxer.exemple_case.jar"
*/

public class ExempleCase {

public static void main(String args[]) {int mes;
mes = 2; // és necessari donar un valor inicial a la variable
switch (mes) {
case 1:
System.out.println("El mes de gener té 31 dies");
break;
case 2:
System.out.println("El mes de febrer té 28 dies si l'any no és de traspàs; i 29, si l'és");
break;
case 3:
System.out.println("El mes de març té 31 dies");
break;
case 4:
System.out.println("El mes d'abril té 30 dies");
break;
case 5:
System.out.println("El mes de maig té 31 dies");
break;
case 6:
System.out.println("El mes de juny té 30 dies");
break;
case 7:
System.out.println("El mes de juliol té 31 dies");
break;
case 8:
System.out.println("El mes d'agost té 31 dies");
break;
case 9:
System.out.println("El mes de setembre té 30 dies");
break;
case 10:
System.out.println("El mes d'octubre té 31 dies");
break;
case 11:
System.out.println("El mes de novembre té 30 dies");
break;
case 12:
System.out.println("El mes de desembre té 31 dies");
break;
default:
}
}

}

Paso de parámetros por valor en el lenguaje Java

import java.io.*;

public class ProvaPrimeraClasse{
public static void main(String args[]) throws IOException {
double altura=0, radi=0;
String text;
InputStreamReader flux = new InputStreamReader(System.in);
BufferedReader teclat = new BufferedReader(flux);

System.out.println("height's value ?");
text=teclat.readLine();
altura=(Double.valueOf(text)).doubleValue();

System.out.println("value of radius ?");
text=teclat.readLine();
radi=(Double.valueOf(text)).doubleValue();

Volums v = new Volums();
System.out.println("Volum of cylinder ="+v.calculaVolum(altura,radi));
// Observem que els arguments del mètode calculaVolum (de l'objecte v)
// li són passats per valor (sempre es fa així en Java)
}
}

Otro ejercicio de programación en lenguaje Java sobre cálculos básicos

public class ProvaEnters{
public static void main (String args[] ){

// declaració de tipus de cada variable
byte e_1_byte; //rang: de -2^{-7} a 2^7-1 (de -128 a 127)
short e_2_byte; //rang: de -2^{-15} a 2^15-1 (de -32768 a 32767)
int e_4_byte; //rang: de -2^{-31} a 2^31-1 (de -2147483648 a 2147483647)
long e_8_byte; //rang: de -2^{-63} a 2^63-1 (de -92233720366854775808 a -92233720366854775807)

// assignació de valor
e_1_byte=-112;
e_2_byte=31245;
e_4_byte=42765;
e_8_byte=1589944592;

System.out.println("exemple de quantitat entera tipus 'byte' (1 byte)="+e_1_byte);
System.out.println("exemple de quantitat entera tipus 'short'(2 byte)="+e_2_byte);
System.out.println("exemple de quantitat entera tipus 'int' (4 byte) ="+e_4_byte);
System.out.println("exemple de quantitat entera tipus 'long' (8 byte)="+e_8_byte);

}
}

Un ejercicio sencillo de asignación de valor a una variable de tipo carácter

public class ProvaChar{
public static void main (String args[] ){

// declaració de tipus de cada variable
char c_1;

// assignació de valor
c_1='w';

System.out.println("lletra "+c_1);
}
}

Un ejercicio de cálculo básico en leguaje Java

public class ProvaReals{
public static void main (String args[] ){
float increment_percentual;
double q_final,q_inicial,increment;

q_final=4E+0D;
q_inicial=1.2E+0D;
increment=q_final-q_inicial;
increment_percentual = (float) (increment*100/q_inicial);

System.out.println("Q_f="+q_final);
System.out.println("Q_i="+q_inicial);
System.out.println("increment="+increment);
System.out.println("increment percentual="+increment_percentual+" %");
}
}

Un ejercicio de prueba con cadenas de caracteres en lenguaje Java

Un exercici senzill:
public class Cadena{
public static void main(String args[]){
String nom;
nom = "Miquel";
System.out.print("El nom" + nom + "té" + nom.length() + "caràcters\n");
System.out.print("El nom" + nom + "té la lletra 'q' en la posició" + nom.indexOf("q") + "\n");
}
}


Un exercici una mica més complet:

public class ProvaString{
public static void main (String args[] ){

// declaració de tipus de cada variable
String nom;
String nombre;
String name;
int position_letter_o;

// assignació de valor
nom="Joan";
nombre="Juan";
name="John";
position_letter_o=nom.indexOf("o")+ 1;
System.out.println("The word "+nom+" has "+nom.length()+" characters \n");
System.out.println("The first character of the word "+nom+" is "+nom.charAt(0)+"\n");
System.out.println("The word "+nom+" has the letter 'o' in "+position_letter_o+"nd position \n");
System.out.println("The word "+nom+" is the same as "+nom.toUpperCase()+" \n");
System.out.println("The first name "+nom+" (catalan) is the same as "+nom.replace(nom,nombre)+" in spanish \n");
System.out.println("The first name "+nom+" (catalan) is the same as "+nom.replace(nom,name)+" in english \n");
}
}

Redondeos en lenguaje Java

public class Arrodoniments {
public static void main(String[] arg) {

int i = 7;
int j = -9;
double x = 72.3543;
double y = 0.3498;

System.out.println("i es " + i);
System.out.println("j es " + j);
System.out.println("x es " + x);
System.out.println("y es " + y);

// Valor absoluto de un número
System.out.println("|" + i + "| es " + Math.abs(i));
System.out.println("|" + j + "| es " + Math.abs(j));
System.out.println("|" + x + "| es " + Math.abs(x));
System.out.println("|" + y + "| es " + Math.abs(y));

// aproximación decimal
//empleando (round)
System.out.println(x + " es " + Math.round(x));
System.out.println(y + " es " + Math.round(y));

System.out.println(x + " es aprox." + (double)Math.round(x*100)/100);
System.out.println(y + " es aprox." + (double)Math.round(y*100)/100);

//empleando floor
System.out.println("The floor of " + x + " es " + (100*Math.floor(x))/100);
System.out.println("The floor of " + y + " es " + (100*Math.floor(y))/100);

// para hallar el menor de dos números
System.out.println("min(" + i + "," + j + ") es " + Math.min(i,j));
System.out.println("min(" + x + "," + y + ") es " + Math.min(x,y));

// Para hallar el mayor de dos números
System.out.println("max(" + i + "," + j + ") es " + Math.max(i,j));
System.out.println("max(" + x + "," + y + ") es " + Math.max(x,y));

// las constantes PI y E
System.out.println("Pi es " + Math.PI);
System.out.println("e es " + Math.E);

//funciones trigonomètricas
double angulo = 45.0 * Math.PI/180.0;
System.out.println("cos(" + angulo + ") es " + Math.cos(angulo));
System.out.println("sin(" + angulo + ") es " + Math.sin(angulo));
System.out.println("tan(" + angulo + ") es " + Math.tan(angulo));

// Funciones trigonomètricas inversas
double valor = 0.707;
System.out.println("acos(" + valor + ") es " + Math.acos(valor));
System.out.println("asin(" + valor + ") es " + Math.asin(valor));
System.out.println("atan(" + valor + ") es " + Math.atan(valor));
y=6.2; //ordenada
x=-5.4; //abscisa
System.out.println("atan2(" + y+" , "+x + ") es " + Math.atan2(y, x));

//Funciones exponencial y logarítmica
System.out.println("exp(1.0) es " + Math.exp(1.0));
System.out.println("exp(10.0) es " + Math.exp(10.0));
System.out.println("exp(0.0) es " + Math.exp(0.0));

System.out.println("log(1.0) es " + Math.log(1.0));
System.out.println("log(10.0) es " + Math.log(10.0));
System.out.println("log(Math.E) es " + Math.log(Math.E));

// pow(x,y) devuelve x elevado a y.
System.out.println("pow(2.0, 2.0) es " + Math.pow(2.0,2.0));
System.out.println("pow(10.0, 3.5) es " + Math.pow(10.0,3.5));
System.out.println("pow(8, -1) es " + Math.pow(8,-1));

// sqrt(x) devuelve la raíz cuadrada de x.
System.out.println("La raíz cuadrada de " + y + " es " + Math.sqrt(y));

// Devuelve un número pseudo-aleatorio comprendido entre 0.0 y 1.0
System.out.println("Número aleatorio: " + Math.random());
System.out.println("Otro número aleatorio: " + Math.random());

try {
//espera la pulsación de una tecla y luego RETORNO
System.in.read();
}catch (Exception ex) { }
}
}

Procedimientos en el lenguaje Pascal ( paso de parámetros )

program compartimentacio_procedural_amb_pas_de_parametres;

(* els paràmetres, en Pascal, es poden passar per valor o per variable *)

uses crt;

var aa,bb,result:real;
var i:integer;

procedure suma(a,b:real;var r+esultat:real);
  begin
    resultat:=a+b;
  end;

procedure multiplica(a,b:real;var resultat:real);
  begin
    resultat:=a*b;
  end;

procedure divideix(a,b:real;var resultat:real);
  begin
    resultat:=a/b;
  end;

procedure resta(a,b:real;var resultat:real);
  begin
    resultat:=a-b;
  end;

begin
  clrscr;
  writeln('entra dos operands reals ');
  readln(aa);readln(bb);
  writeln;

  writeln('1:sumar');
  writeln('2:multiplica');
  writeln('3:divideix');
  writeln('4:resta');
  writeln('0:sortir');

  readln(i);

  case i of
    1:suma(aa,bb,result);
    2:multiplica(aa,bb,result);
    3:divideix(aa,bb,result);
    4:resta(aa,bb,result);
    0:halt;
  end;

  write('el resultat de l'operació és:',result:4:2);

  repeat until keypressed;

end.

Escritura y lectura de un archivo de texto en lenguaje Pascal

PROGRAM EscriuArxiuText(input,data,output);
(*--------------------------------------------------------------------*)
(* Crea un arxiu de text (ascii) a partir de l''entrada per teclat *)
(* Aquest programa est… relacionat amb el programa de lectura de *)
(* lectura LFTEXT.PAS *)
(*--------------------------------------------------------------------*)
uses Crt;
VAR
caracter:char;
NomArxiu:string[30];
arxiu:text;
BEGIN
clrscr;
write('cam¡ i nom de l''arxiu per guardar-lo al disc ? ');readln(NomArxiu);
assign(arxiu,NomArxiu);
rewrite(arxiu);
WHILE NOT eof(input) DO (* eof(INPUT) *)
BEGIN
WHILE NOT eoln(input) DO (* eoln(INPUT) *)
BEGIN
read(caracter);
write(arxiu,caracter);
END;
readln;
writeln(arxiu);
END;
reset(input); (* torna eof a false *)
close(arxiu);
readln;
END.

===

PROGRAM LlegeixArxiuText(data,output);
(*---------------------------------------------------------------------*)
(* Lectura d''un arxiu de disc creat previament amb un editor ascii *)
(* qualsevol *)
(*---------------------------------------------------------------------*)
uses Crt;
VAR
caracter:char;
NomArxiu:string[30];
arxiu:text;
BEGIN
clrscr;
write('cam¡ i nom de l''arxiu ? ');readln(NomArxiu);
assign(arxiu,NomArxiu);
reset(arxiu);
WHILE NOT eof(arxiu) DO
BEGIN
WHILE NOT eoln(arxiu) DO
BEGIN
read(arxiu,caracter);
write(caracter);
END;
write(chr(7)); (* senyal acústi de fi de línia *)
readln(arxiu);
writeln;
END;
close(arxiu);
readln;
END.

Una prueba con la instrucción "switch ... case" del lenguaje Java

public class ExempleCase {

public static void main(String args[]) {int mes;
mes = 2; // és necessari donar un valor inicial a la variable
switch (mes) {
case 1:
System.out.println("El mes de gener té 31 dies");
break;
case 2:
System.out.println("El mes de febrer té 28 dies si l'any no és de traspàs; i 29, si l'és");
break;
case 3:
System.out.println("El mes de març té 31 dies");
break;
case 4:
System.out.println("El mes d'abril té 30 dies");
break;
case 5:
System.out.println("El mes de maig té 31 dies");
break;
case 6:
System.out.println("El mes de juny té 30 dies");
break;
case 7:
System.out.println("El mes de juliol té 31 dies");
break;
case 8:
System.out.println("El mes d'agost té 31 dies");
break;
case 9:
System.out.println("El mes de setembre té 30 dies");
break;
case 10:
System.out.println("El mes d'octubre té 31 dies");
break;
case 11:
System.out.println("El mes de novembre té 30 dies");
break;
case 12:
System.out.println("El mes de desembre té 31 dies");
break;
default:
}
}
}

Entrando los valores de los coeficientes de una matriz

program prova;
type matriu = array [1..10,1..10] of integer;
var c:matriu;
var i,j,valor:integer;

procedure vegem;
begin
   for i:=1 to 4 do
   for j:=1 to 4 do
     begin
       writeln(i,',',j,':' ,c[i,j]);
     end;
end;

begin
 for i:=1 to 4 do
   for j:=1 to 4 do
     begin
       if (i<=2)  and (j<=2) then
         begin
           writeln(i,',',j);
           readln(valor);
           c[i,j]:=valor;
         end
       else c[i,j]:=0;
     end;

     vegem;
end.

Mínimo y máximo de un conjunto de números

program maxmin;
uses crt;

var x,xx: array [1..10] of real;
var max,min:real;
var i:integer;

begin
  clrscr;


  for i:=1 to 10 do
    begin
      write('x[',i,']=');readln(x[i]);
    end;



  max:=x[1];
  min:=x[1];
  for i:=1 to 10 do
    begin
      if x[i]>max then max:=x[i];
      if x[i]<min then min:=x[i];
    end;


  writeln('el valor m…xim ‚s: ',max);
  writeln('el valor m¡nim ‚s: ',min);

  repeat until keypressed;


end.

Cálculo del máximo común divisor de dos números enteros mediante las restas sucesivas ( un programa en lenguaje Pascal ) y cálculo del mínimo común múltiplo

program mcdmcm;

uses crt;

var a,b,mcd,mcm:integer;

function mmccdd(x,y:integer):integer;
  begin
    if (x=y) then
      mmccdd:=x
      else
      begin
        if (x>y) then
        mmccdd:=mmccdd(x-y,y)
        else
        mmccdd:=mmccdd(x,y-x)
      end;
  end;

function mmccmm(x,y:integer):integer;
  begin
    mmccmm:=(x div mmccdd(x,y))*y;
  end;

procedure espera;
var c:char;
  begin
    repeat
      c:=readkey;
    until c=chr(27);
  end;

begin
  clrscr;
  write('a? ');readln(a);
  write('b? ');readln(b);
  mcd:=mmccdd(a,b);
  mcm:=mmccmm(a,b);
  writeln(' mcd = ',mcd);
  writeln(' mcm = ',mcm);

  espera;

end.



martes, 4 de mayo de 2021

Mis herramientas favoritas

Mis herramientas favoritas

Lenguajes de programación y asistentes de cálculo:
    Hoja de cálculo ( herramienta generalista, muy versátil; está integrada en muchas otras herramientas )
    R ( estadística, probabilidad, y cálculo numérico )
    Octave ( cálculo y análisis numérico )
    Maxima ( Un clásico CAS - cálculo simbólico - )
    Python ( lenguaje de propósito general, orientación a objetos; muy adecuado para los trabajos de modelización )
    C/C++ ( lenguaje de propósito general, orientación a objetos; muy adecuado para los proyectos de ingeniería y electrónica )
    Processing ( lenguaje muy apropiado para la elaboración de gráficos, orientación a objetos. También sirve como interfaz para la adquisición de datos mediante la intervención de una placa microcontroladora )
    Julia ( cálculo numérico, cálculo paralelo, un proyecto interesantísimo )
    JavaScript ( lenguaje orientado a objetos muy adecuado para preparar utilidades interactivas en una página web )

Herramientas de mucha utilidad didáctica para las matemáticas en e. secundaria:
    GeoGebra ( multiherramienta: hoja de cálculo, cálculo simbólico, gráficos en 2 y 3 dimensiones, ...)
    Logo ( un clásico didáctico del MIT )
    Scratch ( programación visual, muy apropiada para aprender a programar, hereda la filosofía de los científicos que concibieron el lenguaje Logo, pero con eficaces interfaces gráficos que facilitan el aprendizaje )

Calculadoras científicas, gráficas, programables ...:
    Casio fx 82 MS ( humilde calculadora científica, pero muy buena )
    Casio fx 850P ( un clásico entrañable de los años 80 del siglo pasado, programable en Casio Basic, con una gran librería de utilidades )
    TI 92 ( un calculadora gráfica de los años 90 del siglo pasado, programable en TI Basic; entre otras herramientas, incorpora un módulo CAS con Derive, todo un clásico )
    TI Voyage 200 ( es la evolución de la TI92, con algunas mejoras, todo un clásico de la primera década del actual siglo )
    NumWorks ( calculadora gráfica y programable del siglo XXI, muy rápida y con mucha capacidad de memorai, con un enfoque novedoso por tratarse de un proyecto colaborativo: software y hardware abiertos. Es programable en Python. Presenta una buena conectividad a través de la página web del fabricante. El software se puede actualizar periódicamente y se desarrolla de forma continua, cada vez es mejor. )

Herramientas de edición:
    Bloc de notas ( por su sencillez y pocos recursos necesarios, cualquiera sirve. Una herramienta humilde, pero necesaria. Aparece integrada en todos los sitemas operativos )
    LaTeX ( sistema de composición de documentos, muy apropiado para la tipografía científica )
    LibreOffice ( conjunto de herramientas ofimáticas )
    LyX ( procesador de textos híbrido WYSIWYG - LaTex )
    HTML ( un lenguaje de marcas ( 'tags' ) para editar trabajos en la www: un clásico fundamental )

Electrónica:
    Fritzing ( programa para el diseño de circuitos electrónicos: por si os gusta experimentar en el taller, trastear y soldar )
nbsp   Scratch ( programación visual, puede utilizarse en proyectos de robótica )
    MBlock ( programación visual, similar a Scratch y especialmente útil en proyectos de robótica )

Astronomía: ( programas de ayuda para la localización de astros y para obtener sus coordenadas )
    Stellarium
    Cartes du Ciel

Introducción a la resolución de ecuaciones diofánticas

Antes de exponer la solución del ejercicio que resolveremos como ejemplo, vamos a decir algunas cosas sobre las ecuaciones con números enteros; en concreto, las que toman la forma $ax+by=c$ (que son las más sencillas), y que llamamos ecuaciones diofánticas lineales. Los coeficientes $a,b,c \in \mathbb{Z}$ vienen dados; y, de tener solución la ecuación, las incógnitas $x$ e $y$, que debemos determinar deben ser, también, números enteros.

Algo de teoría:
Veamos lo que nos dice la teoría: Una ecuación diofántica lineal del tipo $ax+by=c$ tiene solución si y sólo si $d\overset{.}{=}\text{m.c.d.}(a,b)$ es divisor del término independiente $c$, lo cual anotamos de la forma abreviada $d|c$; y, teniendo solución dicha ecuación, se demuestra que hay infinitos pares de valores $(x,y)$ que satisfacen dicha ecuación. Encontramos las infinitas soluciones ( solución general ) encontrando, primero, una solución particular $(x_1,y_1)$, y, a continuación, la solución general, que es de la forma

$$\left\{\begin{matrix}
x=x_1+\lambda\,\dfrac{b}{d} & \\
\\
y=y_1-\lambda\,\dfrac{a}{d} & \\
\end{matrix}\right. \forall \lambda \in \mathbb{Z}$$

Vamos, ahora, a exponer un ejemplo.

ENUNCIADO:
Sea la ecuación diofántica lineal $6x+50y=108$. ¿ Tiene solución ? En caso afirmativo, ¿ cómo son los infinitos pares de valores enteros $(x,y)$ ?

SOLUCIÓN:
Observemos que $a=6$, $b=50$ y $c=108$. Como el máximo común divisor de $a$ y $b$, $d:=\text{m.c.d.}(6,50)=2$, es divisor del término independiente $c=108$, esto es $2 | 108$, podemos afirmar que la ecuación tiene solución en $\mathbb{Z}$ y que ésta consta de infinitos pares de números enteros $(x,y)$, que vamos a ver cómo son a continuación.

Encontremos, para empezar, una solución particular de la ecuación dada. Para ello, determinaremos primero una solución particular de la ecuación $ax+by=d$ (identidad de Bézout) y, partiendo de ésta, encontraremos la solución general a una ecuación diofántica lineal.

La identidad de Bézout, $ax+by=d$ es, en el caso que nos ocupa, $6x+50y=2$. Y vemos fácilmente que, $-8$ y $1$ son dos números enteros que cumplen dicha igualdad; en efecto, $6(-8)+50\cdot 1 = 2$   (1). Y, como el término independiente, $108$, de la ecuación pedida se obtiene multiplicando el término independiente de la identidad de Bézout ( que es $2$ ) por $108/2=54$, mutiplicaremos pues ambos miembros de (1) por $54$ para obtener $$6\cdot (-8)\cdot 54+50\cdot 1 \cdot 54 = 2 \cdot 54$$ con lo cual $$6 \cdot \underset{x_1}{\underbrace{\left((-8)\cdot 54\right)}}+50 \cdot \underset{y_1}{\underbrace{\left( 1 \cdot 54 \right)}}= 108$$ es decir $$6 \cdot \underset{x_1}{\underbrace{(-432)}}+50 \cdot \underset{y_1}{\underbrace{54}}= 108$$ luego una solución particular es $$x_1=-432\,,\,y_1=54$$ Así pues, finalmente, construyendo la solución general, llegamos a $$\left\{\begin{matrix}
x=-432+\lambda\,\dfrac{50}{2} & \\
\\
y=54-\lambda\,\dfrac{6}{2} & \\
\end{matrix}\right. \forall \lambda \in \mathbb{Z}$$
es decir
$$\left\{\begin{matrix}
x=-432+25\,\lambda \\
\\
y=54-3\,\lambda \\
\end{matrix}\right. \quad \quad \forall \lambda \in \mathbb{Z}$$

Ahora, dando valores (enteros) arbitrarios al parámetro $\lambda$ podemos encontrar cualesquiera de los pares de números enteros $(x,y)$ - hay infinitos - que constituyen la solución general; así, por ejemplo, para $\lambda = 4$, encontramos $(-332,42)$, etcetera.


Referencias:
  [1] BUJALANCE, E.; et. al., Elementos de Matemática Discreta, Sanz y Torres, Madrid, 2005 ( tercera edición )
  [2] PARSONS, P.; DIXON, G.et. al., Matemáticas en segundos, Librero, Madrid, 2020 ( pp. 42-43 )
  [3] Wikipedia, https://es.wikipedia.org/wiki/Ecuación_diofántica

$\square$