求解線性方程 (Solving a linear equation)


問題描述

求解線性方程 (Solving a linear equation)

我需要以編程方式求解 C、Objective C 或(如果需要)C++ 中的線性方程組。

這是方程的一個示例:

‑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

來自此,我想獲得 abtx 的最佳近似值。


參考解法

方法 1:

Cramer's Rule and Gaussian Elimination are two good, general‑purpose algorithms (also see Simultaneous Linear Equations). If you're looking for code, check out GiNaC, Maxima, and SymbolicC++ (depending on your licensing requirements, of course).

EDIT: I know you're working in C land, but I also have to put in a good word for SymPy (a computer algebra system in Python). You can learn a lot from its algorithms (if you can read a bit of python). Also, it's under the new BSD license, while most of the free math packages are GPL.

方法 2:

You can solve this with a program exactly the same way you solve it by hand (with multiplication and subtraction, then feeding results back into the equations). This is pretty standard secondary‑school‑level mathematics.

‑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)

So you end up with:

a =   0.0785250
b =  ‑0.1806125
c = ‑41.6375875

If you plug these values back into A, B and C, you'll find they're correct.

The trick is to use a simple 4x3 matrix which reduces in turn to a 3x2 matrix, then a 2x1 which is "a = n", n being an actual number. Once you have that, you feed it into the next matrix up to get another value, then those two values into the next matrix up until you've solved all variables.

Provided you have N distinct equations, you can always solve for N variables. I say distinct because these two are not:

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

They are the same equation multiplied by two so you cannot get a solution from them ‑ multiplying the first by two then subtracting leaves you with the true but useless statement:

0 = 0 + 0

By way of example, here's some C code that works out the simultaneous equations that you're placed in your question. First some necessary types, variables, a support function for printing out an equation, and the start of 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;

Next, the reduction of the three equations with three unknowns to two equations with two unknowns:

    // 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 ("");

Next, the reduction of the two equations with two unknowns to one equation with one unknown:

    // 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 ("");

Now that we have a formula of the type number1 = unknown * number2, we can simply work out the unknown value with unknown <‑ number1 / number2. Then, once you've figured that value out, substitute it into one of the equations with two unknowns and work out the second value. Then substitute both those (now‑known) unknowns into one of the original equations and you now have the values for all three unknowns:

    // 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;
}

The output of that code matches the earlier calculations in this answer:

         >: ‑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)

方法 3:

For a 3x3 system of linear equations I guess it would be okay to roll out your own algorithms.

However, you might have to worry about accuracy, division by zero or really small numbers and what to do about infinitely many solutions. My suggestion is to go with a standard numerical linear algebra package such as LAPACK.

方法 4:

Take a look at the Microsoft Solver Foundation.

With it you could write code like this:

  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); 

Here is the output:
===Solver Foundation Service Report===
Datetime: 04/20/2009 23:29:55
Model Name: Default
Capabilities requested: LP
Solve Time (ms): 1027
Total Time (ms): 1414
Solve Completion Status: Optimal
Solver Selected: Microsoft.SolverFoundation.Solvers.SimplexSolver
Directives:
Microsoft.SolverFoundation.Services.Directive
Algorithm: Primal
Arithmetic: Hybrid
Pricing (exact): Default
Pricing (double): SteepestEdge
Basis: Slack
Pivot Count: 3
===Solution Details===
Goals:

Decisions:
a: 0.0785250000000004
b: ‑0.180612500000001
c: ‑41.6375875

方法 5:

Are you looking for a software package that'll do the work or actually doing the matrix operations and such and do each step?

The the first, a coworker of mine just used Ocaml GLPK. It is just a wrapper for the GLPK, but it removes a lot of the steps of setting things up. It looks like you're going to have to stick with the GLPK, in C, though. For the latter, thanks to delicious for saving an old article I used to learn LP awhile back, PDF. If you need specific help setting up further, let us know and I'm sure, me or someone will wander back in and help, but, I think it's fairly straight forward from here. Good Luck!

(by Adam ErnstBrian JorgensenpaxdiabloPall MelstedBobby Ortiznlucaroni)

參考文件

  1. Solving a linear equation (CC BY‑SA 2.5/3.0/4.0)

#system #linear-algebra #math #linear-equation






相關問題

asp.net c# sistem login (asp.net c# login system)

系統函數的彙編代碼(iPhone) (Assembly code to system functions (iPhone))

如何跟踪系統依賴關係? (How to track System Dependencies?)

使用 system() 甚至 passthru() 從 php 運行 python 腳本不會產生任何輸出。為什麼? (Running a python script from php with system( ) or even passthru( ) produces no output. Why?)

求解線性方程 (Solving a linear equation)

C++ 中的 System() 調用及其在編程中的作用 (System() calls in C++ and their roles in programming)

找不到傳遞給使用 Perl“系統”命令調用的程序的參數 (Cannot find argument passed to program called using Perl "system" command)

如何顯示所有用戶表中的所有列/字段? (How to display all columns/fields in all user tables?)

系統文件夾中的庫未加載正確的語言 (Libraries from system folder does not load correct language)

為什麼系統不返回主值? (Why system doesn't return main's value?)

通知未顯示,因為服務或警報管理器已被終止 (Notification not showing because service or alarm manager is killed off)

通過 C++ 調用系統不一致失敗 (Calling the system through C++ inconsistently fails)







留言討論