Ejercicios Introducción a la Aritmética Computacional

Colección de ejercicios propuestos

Representación en Punto Flotante

Problema:

Calcular los números cuya representación en punto flotante de precisión doble es:

  1. \[0 \, 10000000100\] \[0111110000000000000000000000000000000000000000000000\]
  2. \[1\, 10000000010\] \[1011001000000000000000000000000000000000000000000000\]

Solución:

% Número 1
signo=0;
exponente=(2^2+2^10)-1023;
mantisa=2^-2+2^-3+2^-4+2^-5+2^-6;
numero=(-1)^signo*2^exponente*(1+mantisa)
%Número 2
signo=1;
exponente=(2+2^10)-1023;
mantisa=2^-1+2^-3+2^-4+2^-8;
numero=(-1)^signo*2^exponente*(1+mantisa)
	            

Resultado:

  • Primer número: 47.5
  • Segundo número: -13.53125

Representación IEEE de -17.25

Problema:

Escribe la representación normalizada en el estándar IEEE de doble precisión del número decimal -17.25.

Solución:

	              

Resultado:

La representación IEEE 754 de -17.25 es:

  • Signo: 1 (negativo)
  • Exponente: 10000000011 (4 + 1023 = 1027)
  • Mantisa: 0001010000000000000000000000000000000000000000000000

Verificación:

  • 17.25 = 1.000101 × 2⁴
  • El bit de signo 1 indica número negativo
  • El exponente 4 se representa como 1027 (4 + 1023)

Comparación de Precisión

Problema:

¿Qué estimación es más precisa?

\[\frac{9}{11} = 0.818 \,\,\,\,\,\, \sqrt{18} = 2.243\]

Solución:

	  % Calcular valores exactos
	  valor_exacto1 = 9/11;
	  valor_exacto2 = sqrt(18);

	  % Aproximaciones dadas
	  aprox1 = 0.818;
	  aprox2 = 2.243;

	  % Calcular errores relativos
	  error_rel1 = abs((valor_exacto1 - aprox1)/valor_exacto1)
	  error_rel2 = abs((valor_exacto2 - aprox2)/valor_exacto2)


	  % Escritura con formato
	  fprintf('9/11:\n');
	  fprintf('Valor exacto: %.15f\n', valor_exacto1);
	  fprintf('Aproximación: %.3f\n', aprox1);
	  fprintf('Error relativo: %.10f\n\n', error_rel1);

	  fprintf('sqrt(18):\n');
	  fprintf('Valor exacto: %.15f\n', valor_exacto2);
	  fprintf('Aproximación: %.3f\n', aprox2);
	  fprintf('Error relativo: %.10f\n', error_rel2);
	              

Resultado:

Análisis de precisión:

  • 9/11 ≈ 0.81818181818181...
  • Error relativo de 0.818: 2.22×10⁻⁴
  • √18 ≈ 2.24360635350064...
  • Error relativo de 2.243: 2.72×10⁻⁴

La aproximación de 9/11 = 0.818 es más precisa ya que tiene un error relativo menor.

Aproximaciones Históricas de π

Problema:

Calcular el error absoluto, el error relativo y el número de cifras significativas de las siguientes aproximaciones de π:

  • p* = 22/7 (Antiguo Egipto, siglo XXVI a.C)
  • p* = 223/71 (Arquímedes, Antigua Grecia, siglo III a.C.)
  • p* = 3.14159 (Liu Hui, China, año 265)
  • p* = 355/113 (Zu Chongzhi, China, año 480)

Solución:

	  % Definir las aproximaciones históricas
	  aprox = [22/7, 223/71, 3.14159, 355/113];
	  nombres = {'Egipto', 'Arquímedes', 'Liu Hui', 'Zu Chongzhi'};

	  % Calcular errores
	  for i = 1:length(aprox)
	      error_abs(i) = abs(pi - aprox(i));
	      error_rel(i) = error_abs(i)/pi;

          % Escritura con formato
	      %fprintf('\n%s:\n', nombres{i});
	      %fprintf('Aproximación: %.10f\n', aprox(i));
	      %fprintf('Error absoluto: %.10e\n', error_abs(i));
	      %fprintf('Error relativo: %.10e\n', error_rel(i));

	  end
	  % Escritura sin formato
	  [error_abs' error_rel']
	              

Resultado:

Aproximación Error Absoluto Error Relativo
22/7 1.2644×10⁻³ 4.0251×10⁻⁴
223/71 8.4897×10⁻⁵ 2.7019×10⁻⁵
3.14159 2.6535×10⁻⁶ 8.4445×10⁻⁷
355/113 2.6676×10⁻⁷ 8.4889×10⁻⁸

Intervalo de Aproximación para √2

Problema:

Determina el mayor intervalo en el cual debe estar p* para que aproxime a √2 con un error menor que 10⁻².

Solución:

	  % Valor exacto de sqrt(2)
	  valor_exacto = sqrt(2);

	  % Error máximo permitido
	  error_max = 1e-2;

	  % Calcular límites del intervalo
	  limite_inferior = valor_exacto * (1 - error_max);
	  limite_superior = valor_exacto * (1 + error_max);

	  % Visualización del intervalo
	  x = linspace(limite_inferior-0.1, limite_superior+0.1, 1000);
	  y = abs(x - valor_exacto)/valor_exacto;

	  fprintf('Intervalo de aproximación:\n');
	  fprintf('Límite inferior: %.6f\n', limite_inferior);
	  fprintf('Valor exacto: %.6f\n', valor_exacto);
	  fprintf('Límite superior: %.6f\n', limite_superior);

	  % Verificación
	  p_test = [1.4, 1.41, 1.415, 1.42];
	  for i = 1:length(p_test)
	      error_rel = abs(p_test(i) - valor_exacto)/valor_exacto;
	      fprintf('\nPrueba p* = %.3f\n', p_test(i));
	      fprintf('Error relativo: %.6f\n', error_rel);
	      fprintf('¿Dentro del intervalo? %s\n', ...
	          error_rel < error_max ? 'Sí' : 'No');
	  end
	              

Resultado:

Para un error relativo menor que 10⁻², p* debe estar en el intervalo:

\[1.3997 < p* < 1.4282\]

Este intervalo garantiza que:

\[\left|\frac{p* - \sqrt{2}}{\sqrt{2}}\right| < 10^{-2}\]

Sistema de Ecuaciones Lineales

Problema:

Resuelve el siguiente sistema con Matlab:

\[\left\{ \begin{array}{l} 17x_1 + 5x_2 = 22\\ 1.7x_1 + 0.5x_2 = 2.2 \end{array} \right.\]

Una solución obvia es x₁ = x₂ = 1 y el sistema tiene infinitas soluciones (la segunda ecuación es la primera dividida por 10). ¿Qué sucede?

Solución:

	  % Definir el sistema
	  A = [17 5; 1.7 0.5];
	  b = [22; 2.2];

	  % Mostrar la matriz aumentada
	  fprintf('Matriz aumentada:\n');
	  disp([A b]);

	  % Intentar resolver el sistema
	  warning('off', 'MATLAB:nearlySingularMatrix');
	  x = A\b;
	  fprintf('\nSolución computada:\n');
	  disp(x);

	  % Verificar la solución
	  difere = A*x - b;
	  disp(difere)

	              

Resultado:

El problema presenta inestabilidad numérica debido a:

  1. Las ecuaciones son linealmente dependientes (una es múltiplo de la otra)
  2. Pequeñas perturbaciones en los coeficientes producen grandes cambios en la solución

Aunque matemáticamente el sistema tiene infinitas soluciones, numéricamente es inestable debido a los errores de redondeo en la representación de punto flotante.

Estabilidad de Sucesiones Recursivas

Problema:

Se quiere calcular pₙ como (1/3)ⁿ para n>0 considerando p₀=1 usando dos métodos:

  1. Definiendo pₙ=(1/3)pₙ₋₁ para n>1
  2. Definiendo p₀=1 y p₁=1/3 y calculando para n>2:
    \[p_n = \frac{10}{3}p_{n-1} - p_{n-2}\]

¿Alguno de los dos métodos es inestable?

Solución:

	  % Comparar ambos métodos para varios valores de n
	  N = 20;  % Número de términos a calcular

	  % Método 1: pn = (1/3)pn-1
	  p1 = zeros(1,N);
	  p1(1) = 1;  % p0
	  for n = 2:N
	      p1(n) = (1/3) * p1(n-1);
	  end

	  % Método 2: pn = (10/3)pn-1 - pn-2
	  p2 = zeros(1,N);
	  p2(1) = 1;    % p0
	  p2(2) = 1/3;  % p1
	  for n = 3:N
	      p2(n) = (10/3)*p2(n-1) - p2(n-2);
	  end

	  % Valores exactos para comparación
	  p_exact = (1/3).^(0:N-1);

	  % Calcular errores relativos
	  err1 = abs((p1 - p_exact)./p_exact);
	  err2 = abs((p2 - p_exact)./p_exact);

	  % Mostrando resultados con formato
	  fprintf('n\tMétodo 1\tMétodo 2\tExacto\n');
	  fprintf('-------------------------------------------\n');
	  for n = 1:N
	      fprintf('%d\t%.2e\t%.2e\t%.2e\n', ...
	          n-1, p1(n), p2(n), p_exact(n));
	  end
	              

Resultado:

Análisis de estabilidad:

  • El primer método es estable: el error se mantiene acotado y los resultados son precisos
  • El segundo método es inestable:
    • Los errores crecen exponencialmente
    • La recursión amplifica los errores de redondeo
    • Los resultados se desvían significativamente de los valores exactos

Suma de Series en Diferentes Órdenes

Problema:

Evaluar la serie armónica hasta 10 millones de términos:

\[\sum\limits_{n = 1}^{10000000} {\frac{1}{n}}\]

Calcular primero en orden usual y luego en orden opuesto. Explicar las diferencias obtenidas e indicar cuál es el resultado más preciso.

Solución:

	  % Parámetros
	  N = 10000000;

	  % Suma en orden ascendente
	  suma_asc = 0;
	  for n = 1:N
	      suma_asc = suma_asc + 1/n;
	  end

	  % Suma en orden descendente
	  suma_desc = 0;
	  for n = N:-1:1
	      suma_desc = suma_desc + 1/n;
	  end

	  % Mostrar resultados
	  fprintf('Suma en orden ascendente: %.15f\n', suma_asc);
	  fprintf('Suma en orden descendente: %.15f\n', suma_desc);


	              

Resultado:

Análisis de los resultados:

  • La suma en orden descendente es más precisa porque:
    • Suma primero los términos más pequeños
    • Minimiza la pérdida de precisión por cancelación
    • Reduce el error de redondeo acumulado
  • Diferencias observadas:
    • La suma ascendente pierde precisión en términos pequeños
    • Los errores de redondeo se amplifican más en el orden ascendente
    • La diferencia entre ambos resultados es significativa

Estabilidad Numérica de Expresiones

Problema:

Indica cómo evaluar con Matlab la expresión:

\[\frac{1}{x + h} - \frac{1}{x}\]

para los valores de h siguientes: 10⁻², 10⁻⁴,..., 10⁻²². Escribe una reformulación de esta expresión de forma que no se produzca cancelación.

Solución:

	  % Definir valor de x
	  x = 1;  % Podemos usar cualquier valor no nulo

	  % Generar valores de h
	  h = 10.^(-2:-2:-22);

	  % Función original
	  f1 = @(x,h) 1./(x + h) - 1./x;

	  % Función reformulada
	  f2 = @(x,h) -h./(x.*(x + h));

	  % Configurar formato de salida
	  format longEng;

 	  % Resultados
 	  [h' f1(1,h)' f2(1,h)']

	  % Evaluación y escritura con formato
	  fprintf('    h       Original     Reformulada    Dif. Rel.\n');
	  fprintf('------------------------------------------------\n');
	  for i = 1:length(h)
	      v1 = f1(x,h(i));
	      v2 = f2(x,h(i));
	      dif_rel = abs(v1-v2)/abs(v2);
	      fprintf('%8.0e  %12.5e  %12.5e  %8.1e\n', ...
	          h(i), v1, v2, dif_rel);
	  end

	  % Visualización del error relativo
	  loglog(h, abs(f1(x,h) - f2(x,h))./abs(f2(x,h)), 'b-o');
	  grid on;
	  title('Error Relativo vs h');
	  xlabel('h');
	  ylabel('Error Relativo');
	              

Resultado:

La expresión original sufre de cancelación cuando h es pequeño porque:

  • Los términos 1/(x+h) y 1/x se vuelven muy similares
  • La resta de números casi iguales pierde dígitos significativos

La expresión reformulada:

\[-\frac{h}{x(x+h)}\]

Es más estable porque:

  • Evita la resta de términos casi iguales
  • Mantiene la precisión incluso para h muy pequeño
  • Es algebraicamente equivalente a la expresión original

Evaluación de Diferencias

Problema:

Indica cómo evaluar con Matlab la expresión:

\[\sqrt{{x^2} + 1} - 1\]

para los valores de x siguientes: 10⁻², 10⁻⁴,..., 10⁻²². Escribe una reformulación de esta expresión de forma que no se produzca cancelación.

Solución:

	  % Definir valor de x
	  x = 1;  % Podemos usar cualquier valor no nulo

	  % Generar valores de h
	  h = 10.^(-2:-2:-22);

	  % Función original
	  f1 = @(x) sqrt(x.^2+1)-1;

	  % Función reformulada
	  f2 = @(x) x.^2./(sqrt(x.^2+1)+1);

	  % Configurar formato de salida
	  format longEng;

	  % Resultados
 	  [h' f1(x,h)' f2(x,h)']

	  % Evaluar y escribir con formato
	  fprintf('    h       Original     Reformulada    Dif. Rel.\n');
	  fprintf('------------------------------------------------\n');
	  for i = 1:length(h)
	      v1 = f1(x,h(i));
	      v2 = f2(x,h(i));
	      dif_rel = abs(v1-v2)/abs(v2);
	      fprintf('%8.0e  %12.5e  %12.5e  %8.1e\n', ...
	          h(i), v1, v2, dif_rel);
	  end

	  % Visualización del error relativo
	  loglog(h, abs(f1(x,h) - f2(x,h))./abs(f2(x,h)), 'b-o');
	  grid on;
	  title('Error Relativo vs h');
	  xlabel('h');
	  ylabel('Error Relativo');
	              

Resultado:

La expresión original sufre de cancelación cuando x es pequeño porque:

  • Los términos √(x² + 1) y 1 se vuelven muy similares
  • La resta de números casi iguales pierde dígitos significativos

La expresión reformulada:

\[\frac{x^2}{\sqrt{x^2 + 1} + 1}\]

Es más estable porque:

  • Evita la resta de términos casi iguales
  • Mantiene la precisión incluso para x muy pequeño
  • Es algebraicamente equivalente a la expresión original

Ejemplo 3.8: Reformulación Logarítmica

Problema:

Calcular una fórmula alternativa para la expresión:

\[ - \log \left( {x - \sqrt {{x^2} - 1} } \right)\]

que no sea susceptible de pérdida de cifras significativas cuando x es grande y positiva.

Solución:

	  % Definir función original
	  f1 = @(x) -log(x - sqrt(x.^2 - 1));

	  % Función reformulada usando identidad logarítmica
	  f2 = @(x) log(x + sqrt(x.^2 - 1));

	  % Comparar para valores grandes de x
	  x = 10.^(5:5:30)';

	  %Valores
	  [x f1(x) f2(x)]

	  % Escritura con formato
	  fprintf('     x      Original    Reformulada   Dif. Rel.\n');
	  fprintf('-------------------------------------------\n');
	  for i = 1:length(x)
	      v1 = f1(x(i));
	      v2 = f2(x(i));
	      fprintf('%8.1e  %10.4f  %10.4f  %8.1e\n', ...
	          x(i), v1, v2, abs(v1-v2)/abs(v2));
	  end
	              

Resultado:

La expresión original pierde precisión porque:

  • Para x grande, x y √(x² - 1) son casi iguales
  • La resta causa pérdida de precisión
  • El logaritmo amplifica los errores

La expresión reformulada:

\[\log(x + \sqrt{x^2 - 1})\]

Es más estable porque:

  • Usa suma en lugar de resta
  • No hay pérdida de cifras significativas
  • Es algebraicamente equivalente (son expresiones inversas)

Sucesión Convergente a π

Problema:

Considera la sucesión recurrente:

\[{x_1} = 2\] \[{x_{n + 1}} = {2^{n - \frac{1}{2}}}\sqrt {1 - \sqrt {1 - {4^{1 - n}}x_n^2} }\]

que converge a π. Explica el comportamiento para x₅₅₀

Solución:

	  % Implementar la sucesión
	  x = zeros(1, 550);
	  x(1) = 2;

	  % Calcular términos
	  for n = 1:549
	      factor = 2^(n - 0.5);
	      inner = 1 - 4^(1-n)*x(n)^2;
	      if inner < 0
	          fprintf('Error: Raíz de número negativo en n=%d\n', n);
	          break;
	      end
	      x(n+1) = factor * sqrt(1 - sqrt(inner));
	  end

	  % Analizar convergencia
	  for i = [1:5, 10:10:50, 100:100:550]
	      if i <= length(x)
	          fprintf('x_%d = %.15f\n', i, x(i));
	          fprintf('Error vs pi: %.2e\n\n', abs(x(i)-pi));
	      end
	  end

	  % Visualizar convergencia
	  semilogy(abs(x(1:end) - pi));
	  title('Convergencia a π');
	  xlabel('n');
	  ylabel('|x_n - π|');
	              

Resultado:

La sucesión presenta problemas numéricos porque:

  • Involucra potencias grandes (2^n) que crecen rápidamente
  • La resta 1 - 4^(1-n)x_n² causa pérdida de precisión
  • Los errores de redondeo se amplifican en cada iteración

Para n = 550:

  • Los términos pierden precisión significativa
  • La convergencia a π se ve afectada por inestabilidad numérica
  • El método no es práctico para n grande

Integral Recursiva

Problema:

Considera la integral:

\[{y_n} = \int\limits_0^1 {{x^n}{e^x}dx} \,\,\,\,\,\,\left( {n \ge 0} \right)\]

que satisface la relación de recurrencia:

\[{y_{n + 1}} = e - \left( {n + 1} \right){y_n}\]

Calcula y₂₀ utilizando esta recurrencia. ¿Qué ocurre si utilizas la ley de recurrencia inversa?

\[{y_n} = \frac{{e - {y_{n + 1}}}}{{n + 1}}\]

Solución:

	  % Calcular usando recurrencia hacia adelante
	  y_forward = zeros(1, 21);
	  y_forward(1) = exp(1) - 1;  % y₀ = e - 1

	  for n = 1:20
	      y_forward(n+1) = exp(1) - n*y_forward(n);
	  end

	  % Calcular usando recurrencia hacia atrás
	  y_backward = zeros(1, 21);
	  y_backward(21) = 0;  % Aproximación inicial

	  for n = 20:-1:1
	      y_backward(n) = (exp(1) - y_backward(n+1))/n;
	  end

	  % Comparar resultados
	  fprintf('n      Forward         Backward        Diferencia\n');
	  fprintf('-------------------------------------------------\n');
	  for n = 1:21
	      fprintf('%2d  %14.6e  %14.6e  %14.6e\n', ...
	          n-1, y_forward(n), y_backward(n), ...
	          abs(y_forward(n) - y_backward(n)));
	  end

	  % Visualizar diferencias
	  semilogy(0:20, abs(y_forward - y_backward), '-o');
	  title('Diferencia entre métodos');
	  xlabel('n');
	  ylabel('|y_{forward} - y_{backward}|');
	              

Resultado:

Análisis de los métodos:

  1. Recurrencia hacia adelante (yₙ₊₁ = e - (n+1)yₙ):
    • Es numéricamente inestable
    • Los errores se amplifican en cada paso
    • Pierde precisión rápidamente
  2. Recurrencia hacia atrás (yₙ = (e - yₙ₊₁)/(n+1)):
    • Es numéricamente estable
    • Mantiene la precisión
    • Proporciona resultados más confiables

La diferencia en el comportamiento se debe a que:

  • La recurrencia hacia adelante amplifica los errores por un factor n
  • La recurrencia hacia atrás los reduce por un factor 1/n
  • Para integrales de este tipo, es preferible usar la recurrencia hacia atrás