Resolviendo una ecuación lineal

votos
35

Necesito resolver programáticamente un sistema de ecuaciones lineales en C, objetivo C o (si es necesario) C ++.

Aquí hay un ejemplo de las ecuaciones:

-44.3940 = a * 50.0 + b * 37.0 + tx
-45.3049 = a * 43.0 + b * 39.0 + tx
-44.9594 = a * 52.0 + b * 41.0 + tx

A partir de esto, me gustaría obtener la mejor aproximación para a, by tx.

Publicado el 03/08/2008 a las 19:14
fuente por usuario
En otros idiomas...                            


10 respuestas

votos
3

¿Está buscando un paquete de software que haga el trabajo o que realmente haga las operaciones matriciales y demás y realice cada paso?

El primero, un compañero de trabajo acaba de usar Ocaml GLPK . Es solo un envoltorio para el GLPK , pero elimina muchos de los pasos de configuración. Sin embargo, parece que tendrás que quedarte con el GLPK, en C. Para este último, gracias a Delicious por guardar un artículo antiguo solía aprender LP hace un tiempo, PDF . Si necesita ayuda específica para la configuración, infórmenos y estoy seguro de que yo o alguien volveremos y lo ayudaremos, pero creo que es bastante directo desde aquí. ¡Buena suerte!

Respondida el 03/08/2008 a las 19:27
fuente por usuario

votos
17

La Regla de Cramer y la Eliminación Gaussiana son dos buenos algoritmos de propósito general (ver también Ecuaciones Lineales Simultáneas ). Si está buscando código, consulte GiNaC , Maxima y SymbolicC ++ (según sus requisitos de licencia, por supuesto).

EDITAR: Sé que estás trabajando en C land, pero también tengo que poner una buena palabra para SymPy (un sistema de álgebra computacional en Python). Puedes aprender mucho de sus algoritmos (si puedes leer un poco de Python). Además, está bajo la nueva licencia BSD, mientras que la mayoría de los paquetes matemáticos gratuitos son GPL.

Respondida el 03/08/2008 a las 19:37
fuente por usuario

votos
7

Para un sistema 3x3 de ecuaciones lineales, supongo que estaría bien desplegar tus propios algoritmos.

Sin embargo, puede que tenga que preocuparse por la precisión, la división por cero o los números realmente pequeños y qué hacer con infinitas soluciones. Mi sugerencia es ir con un paquete de álgebra lineal numérico estándar como LAPACK .

Respondida el 08/08/2008 a las 18:59
fuente por usuario

votos
1

Personalmente, soy parcial a los algoritmos de Recetas Numéricas . (Soy aficionado a la edición en C ++)

Este libro le enseñará por qué funcionan los algoritmos, y le mostrará algunas implementaciones bastante depuradas de esos algoritmos.

Por supuesto, podrías usar CLAPACK a ciegas (lo he usado con gran éxito), pero primero escribiría manualmente un algoritmo de eliminación gaussiana para al menos tener una ligera idea del tipo de trabajo que se ha realizado al hacer estos algoritmos. estable.

Más tarde, si estás haciendo un álgebra lineal más interesante, mirar alrededor del código fuente de Octave responderá muchas preguntas.

Respondida el 25/08/2008 a las 19:22
fuente por usuario

votos
3

El paquete de herramientas numéricas de plantilla de NIST tiene herramientas para hacerlo.

Una de las formas más confiables es usar una descomposición QR .

Aquí hay un ejemplo de un contenedor para que pueda llamar a "GetInverse (A, InvA)" en mi código y colocará el inverso en InvA.

void GetInverse(const Array2D<double>& A, Array2D<double>& invA)
   {
   QR<double> qr(A);  
   invA = qr.solve(I); 
   }

Array2D se define en la biblioteca.

Respondida el 25/08/2008 a las 19:53
fuente por usuario

votos
2

Según la redacción de su pregunta, parece que tiene más ecuaciones que incógnitas y desea minimizar las incoherencias. Esto se hace típicamente con regresión lineal, que minimiza la suma de los cuadrados de las inconsistencias. Dependiendo del tamaño de los datos, puede hacer esto en una hoja de cálculo o en un paquete estadístico. R es un paquete gratuito de alta calidad que realiza regresiones lineales, entre muchas otras cosas. Hay mucho en la regresión lineal (y muchos errores), pero es fácil de hacer en casos simples. Aquí hay un ejemplo R usando sus datos. Tenga en cuenta que el "tx" es la intersección de su modelo.

> y <- c(-44.394, -45.3049, -44.9594)
> a <- c(50.0, 43.0, 52.0)
> b <- c(37.0, 39.0, 41.0)
> regression = lm(y ~ a + b)
> regression

Call:
lm(formula = y ~ a + b)

Coefficients:
(Intercept)            a            b  
  -41.63759      0.07852     -0.18061  
Respondida el 17/09/2008 a las 15:22
fuente por usuario

votos
2

En términos de eficiencia en tiempo de ejecución, otros han respondido mejor que yo. Si siempre tendrá el mismo número de ecuaciones que variables, me gusta la regla de Cramer, ya que es fácil de implementar. Simplemente escriba una función para calcular el determinante de una matriz (o use una que ya esté escrita, estoy seguro de que puede encontrar una) y divida los determinantes de dos matrices.

Respondida el 19/09/2008 a las 04:36
fuente por usuario

votos
15

Puedes resolver esto con un programa exactamente de la misma manera que lo resuelves a mano (con multiplicación y sustracción, y luego devolviendo los resultados a las ecuaciones). Esto es bastante estándar en matemáticas de nivel secundario.

-44.3940 = 50a + 37b + c (A)
-45.3049 = 43a + 39b + c (B)
-44.9594 = 52a + 41b + c (C)

(A-B): 0.9109 =  7a -  2b (D)
(B-C): 0.3455 = -9a -  2b (E)

(D-E): 1.2564 = 16a (F)

(F/16):  a = 0.078525 (G)

Feed G into D:
       0.9109 = 7a - 2b
    => 0.9109 = 0.549675 - 2b (substitute a)
    => 0.361225 = -2b (subtract 0.549675 from both sides)
    => -0.1806125 = b (divide both sides by -2) (H)

Feed H/G into A:
       -44.3940 = 50a + 37b + c
    => -44.3940 = 3.92625 - 6.6826625 + c (substitute a/b)
    => -41.6375875 = c (subtract 3.92625 - 6.6826625 from both sides)

Entonces terminas con:

a =   0.0785250
b =  -0.1806125
c = -41.6375875

Si vuelve a conectar estos valores en A, B y C, verá que son correctos.

El truco es usar una matriz 4x3 simple que a su vez se reduce a una matriz de 3x2, luego un 2x1 que es "a = n", siendo n un número real. Una vez que tienes eso, lo pasas a la siguiente matriz para obtener otro valor, luego esos dos valores en la siguiente matriz hasta que hayas resuelto todas las variables.

Siempre que tenga N ecuaciones distintas, siempre puede resolver para N variables. Digo distinto porque estos dos no son:

 7a + 2b =  50
14a + 4b = 100

Son la misma ecuación multiplicada por dos, por lo que no puedes obtener una solución de ellos: multiplicar el primero por dos y restar te deja con la afirmación verdadera pero inútil:

0 = 0 + 0

A modo de ejemplo, aquí hay un código C que resuelve las ecuaciones simultáneas que se le hacen en su pregunta. Primero algunos tipos necesarios, variables, una función de soporte para imprimir una ecuación y el comienzo de main:

#include <stdio.h>

typedef struct { double r, a, b, c; } tEquation;
tEquation equ1[] = {
    { -44.3940,  50, 37, 1 },      // -44.3940 = 50a + 37b + c (A)
    { -45.3049,  43, 39, 1 },      // -45.3049 = 43a + 39b + c (B)
    { -44.9594,  52, 41, 1 },      // -44.9594 = 52a + 41b + c (C)
};
tEquation equ2[2], equ3[1];

static void dumpEqu (char *desc, tEquation *e, char *post) {
    printf ("%10s: %12.8lf = %12.8lfa + %12.8lfb + %12.8lfc (%s)\n",
        desc, e->r, e->a, e->b, e->c, post);
}

int main (void) {
    double a, b, c;

Luego, la reducción de las tres ecuaciones con tres incógnitas a dos ecuaciones con dos incógnitas:

    // First step, populate equ2 based on removing c from equ.

    dumpEqu (">", &(equ1[0]), "A");
    dumpEqu (">", &(equ1[1]), "B");
    dumpEqu (">", &(equ1[2]), "C");
    puts ("");

    // A - B
    equ2[0].r = equ1[0].r * equ1[1].c - equ1[1].r * equ1[0].c;
    equ2[0].a = equ1[0].a * equ1[1].c - equ1[1].a * equ1[0].c;
    equ2[0].b = equ1[0].b * equ1[1].c - equ1[1].b * equ1[0].c;
    equ2[0].c = 0;

    // B - C
    equ2[1].r = equ1[1].r * equ1[2].c - equ1[2].r * equ1[1].c;
    equ2[1].a = equ1[1].a * equ1[2].c - equ1[2].a * equ1[1].c;
    equ2[1].b = equ1[1].b * equ1[2].c - equ1[2].b * equ1[1].c;
    equ2[1].c = 0;

    dumpEqu ("A-B", &(equ2[0]), "D");
    dumpEqu ("B-C", &(equ2[1]), "E");
    puts ("");

Luego, la reducción de las dos ecuaciones con dos incógnitas a una ecuación con una desconocida:

    // Next step, populate equ3 based on removing b from equ2.

    // D - E
    equ3[0].r = equ2[0].r * equ2[1].b - equ2[1].r * equ2[0].b;
    equ3[0].a = equ2[0].a * equ2[1].b - equ2[1].a * equ2[0].b;
    equ3[0].b = 0;
    equ3[0].c = 0;

    dumpEqu ("D-E", &(equ3[0]), "F");
    puts ("");

Ahora que tenemos una fórmula del tipo number1 = unknown * number2, simplemente podemos calcular el valor desconocido con unknown <- number1 / number2. Luego, una vez que hayas calculado ese valor, sustitúyelo en una de las ecuaciones con dos incógnitas y resuelve el segundo valor. Luego sustituya ambas incógnitas (ahora conocidas) en una de las ecuaciones originales y ahora tiene los valores para las tres incógnitas:

    // Finally, substitute values back into equations.

    a = equ3[0].r / equ3[0].a;
    printf ("From (F    ), a = %12.8lf (G)\n", a);

    b = (equ2[0].r - equ2[0].a * a) / equ2[0].b;
    printf ("From (D,G  ), b = %12.8lf (H)\n", b);

    c = (equ1[0].r - equ1[0].a * a - equ1[0].b * b) / equ1[0].c;
    printf ("From (A,G,H), c = %12.8lf (I)\n", c);

    return 0;
}

La salida de ese código coincide con los cálculos anteriores en esta respuesta:

         >: -44.39400000 =  50.00000000a +  37.00000000b +   1.00000000c (A)
         >: -45.30490000 =  43.00000000a +  39.00000000b +   1.00000000c (B)
         >: -44.95940000 =  52.00000000a +  41.00000000b +   1.00000000c (C)

       A-B:   0.91090000 =   7.00000000a +  -2.00000000b +   0.00000000c (D)
       B-C:  -0.34550000 =  -9.00000000a +  -2.00000000b +   0.00000000c (E)

       D-E:  -2.51280000 = -32.00000000a +   0.00000000b +   0.00000000c (F)

From (F    ), a =   0.07852500 (G)
From (D,G  ), b =  -0.18061250 (H)
From (A,G,H), c = -41.63758750 (I)
Respondida el 26/02/2009 a las 11:59
fuente por usuario

votos
6

Eche un vistazo a la Fundación Microsoft Solver .

Con él podrías escribir código como este:

  SolverContext context = SolverContext.GetContext();
  Model model = context.CreateModel();

  Decision a = new Decision(Domain.Real, "a");
  Decision b = new Decision(Domain.Real, "b");
  Decision c = new Decision(Domain.Real, "c");
  model.AddDecisions(a,b,c);
  model.AddConstraint("eqA", -44.3940 == 50*a + 37*b + c);
  model.AddConstraint("eqB", -45.3049 == 43*a + 39*b + c);
  model.AddConstraint("eqC", -44.9594 == 52*a + 41*b + c);
  Solution solution = context.Solve();
  string results = solution.GetReport().ToString();
  Console.WriteLine(results); 

Aquí está el resultado:
=== Informe de servicio de Solver Foundation ===
Fecha y hora: 20/04/2009 23:29:55
Nombre del modelo:
Capacidades predeterminadas solicitadas: LP
Tiempo de resolución (ms): 1027
Tiempo total (ms): 1414
Resolver Estado de finalización: Optimal
Solver Seleccionado: Microsoft.SolverFoundation.Solvers.SimplexSolver
Directivas:
Microsoft.SolverFoundation.Services.Directive
Algorithm: Primal
Arithmetic: Hybrid
Pricing (exact): Default
Pricing (double): SteepestEdge
Basis: Slack
Pivot Count: 3
== = Detalles de la solución ===
Metas:

Decisiones:
a: 0.0785250000000004
b: -0.180612500000001
c: -41.6375875

Respondida el 21/04/2009 a las 04:33
fuente por usuario

votos
1
function x = LinSolve(A,y)
%
% Recursive Solution of Linear System Ax=y
% matlab equivalent: x = A\y 
% x = n x 1
% A = n x n
% y = n x 1
% Uses stack space extensively. Not efficient.
% C allows recursion, so convert it into C. 
% ----------------------------------------------
n=length(y);
x=zeros(n,1);
if(n>1)
    x(1:n-1,1) = LinSolve( A(1:n-1,1:n-1) - (A(1:n-1,n)*A(n,1:n-1))./A(n,n) , ...
                           y(1:n-1,1) - A(1:n-1,n).*(y(n,1)/A(n,n))); 
    x(n,1) = (y(n,1) - A(n,1:n-1)*x(1:n-1,1))./A(n,n); 
else
    x = y(1,1) / A(1,1);
end
Respondida el 30/12/2014 a las 15:24
fuente por usuario

Cookies help us deliver our services. By using our services, you agree to our use of cookies. Learn more