Решение линейного уравнения

голоса
35

Мне нужно программно решить систему линейных уравнений в 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

Исходя из этого, я хотел бы получить наилучшее приближение a, bи tx.

Задан 03/08/2008 в 19:14
источник пользователем
На других языках...                            


10 ответов

голоса
17

Правило Крамера и исключения Гаусса два алгоритма хорошо, общего назначения (также см линейных уравнений ). Если вы ищете код, проверить GiNaC , Maxima и SymbolicC ++ ( в зависимости от ваших требований к лицензированию, конечно).

EDIT: Я знаю , что вы работаете в C земли, но я также должен положить в хорошее слово для SymPy (система компьютерной алгебры в Python). Вы можете многому научиться у своих алгоритмов (если вы можете прочитать немного питона). Кроме того , это в соответствии с новой лицензией BSD, в то время как большинство бесплатных математических пакетов являются GPL.

Ответил 03/08/2008 d 19:37
источник пользователем

голоса
15

Вы можете решить эту проблему с помощью программы точно так же, как решить эту проблему вручную (с умножением и вычитанием, а затем кормления результатов обратно в уравнение). Это довольно стандартные математики средней школы на уровне.

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

Таким образом, вы в конечном итоге с:

a =   0.0785250
b =  -0.1806125
c = -41.6375875

Если вы подключаете эти значения обратно в A, B и C, вы обнаружите, что они правы.

Фокус в том, чтобы использовать простую матрицу 4x3, который в свою очередь, уменьшает к матрице 3x2, то 2x1, который является «а = п», п быть фактическим числом. Если у вас есть, что вы кормите его в следующую матрицу, чтобы получить другое значение, а затем эти два значения в следующей матрице вверх до тех пор, как вы решили все переменные.

При условии, у вас есть N различных уравнений, вы всегда можете решить для N переменных. Я говорю отчетливым, потому что эти два не являются:

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

Они же уравнение , умноженное на два , так что вы не можете получить разрешение от них - умножения первого на два затем вычитая оставляет вас с истинными , но бесполезными заявлениями:

0 = 0 + 0

В качестве примера, вот некоторый код C , который работает одновременные уравнения , которые вы помещены в вашем вопросе. Первый некоторые необходимые типы, переменные, поддержка функция для распечатки уравнения, и начало 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;

Далее, снижение трех уравнений с тремя неизвестными до двух уравнений с двумя неизвестными:

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

Теперь, когда мы имеем формулу типа number1 = unknown * number2, мы можем просто работать неизвестное значение с unknown <- number1 / number2. Тогда, как только вы поняли , что значение из, подставить его в одно из уравнений с двумя неизвестными и выработать второе значение. Затем заменить оба эти (ныне известные) неизвестные в одном из исходных уравнений и теперь имеет значение для всех трех неизвестных:

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

Выход этого кода соответствует выданному ранее расчеты в этом ответе:

         >: -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)
Ответил 26/02/2009 d 11:59
источник пользователем

голоса
7

Для 3х3 системы линейных уравнений, я думаю, было бы хорошо, чтобы выкатить свои собственные алгоритмы.

Тем не менее, вы , возможно , придется беспокоиться о точности, деление на ноль или действительно небольших количествах и , что делать бесконечно много решений. Мое предложение пойти со стандартной числовой линейной алгебры пакета , такие как LAPACK .

Ответил 08/08/2008 d 18:59
источник пользователем

голоса
6

Посмотрите на Solver Foundation Microsoft .

С его помощью вы можете написать код, как это:

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

Вот результат:
=== Solver Service Foundation Report ===
Datetime: 04/20/2009 23:29:55
Название модели: По умолчанию
возможности запрошенный: LP
Решить Время (мс): 1027
Общее время (мс): 1414
Решить Статус завершения: Оптимальная
Solver Выбрано: Microsoft.SolverFoundation.Solvers.SimplexSolver
Директива:
Microsoft.SolverFoundation.Services.Directive
Алгоритм: Primal
арифметика: Гибридная
Цена (точно): По умолчанию
Цены (двойной): SteepestEdge
Основа: натяжной
Pivot Count: 3
== = Подробности Решение ===
Цели:

Решения:
а: 0,0785250000000004
б: -0,180612500000001
с: -41.6375875

Ответил 21/04/2009 d 04:33
источник пользователем

голоса
3

Шаблон Численный Toolkit из NIST имеет инструменты для делать это.

Одним из наиболее надежных способов является использование QR - разложения .

Вот пример обертки, так что я могу назвать «GetInverse (A, INVA)» в моем коде, и это поставит обратное в INVA.

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

Array2D определяется в библиотеке.

Ответил 25/08/2008 d 19:53
источник пользователем

голоса
3

Вы ищете пакет программного обеспечения, который будет делать работу или на самом деле делать матричные операции и такие, и делать каждый шаг?

Первый, коллега моего просто использовал OCAML GLPK . Это просто оболочка для GLPK , но она удаляет много шагов настройки вещи. Похоже , что вы собираетесь должны придерживаться GLPK, в C, хотя. Для последнего, благодаря вкусным для сохранения старой статьи я использовал , чтобы узнать LP некоторое время назад, PDF . Если вам нужна настройка далее конкретная помощь, дайте нам знать , и я уверен , что я или кто - то будет бродить назад и помочь, но, я думаю , что это довольно прямо вперед отсюда. Удачи!

Ответил 03/08/2008 d 19:27
источник пользователем

голоса
2

С точки зрения эффективности времени выполнения, другие ответили лучше , чем I. Если вы всегда будете иметь такое же количество уравнений в качестве переменных, мне нравится Крамера , как это легко осуществить. Просто написать функцию для вычисления определителя матрицы (или использовать тот , который уже написано, я уверен , что вы можете найти один из там), и разделить детерминанты двух матриц.

Ответил 19/09/2008 d 04:36
источник пользователем

голоса
2

Из формулировки вашего вопроса, похоже, у вас есть больше уравнений, чем неизвестные, и вы хотите, чтобы свести к минимуму противоречия. Обычно это делается с линейной регрессии, которая минимизирует сумму квадратов несоответствий. В зависимости от размера данных, вы можете сделать это в таблице или в статистическом пакете. R представляет собой высококачественный, бесплатный пакет, который делает линейной регрессии, среди многих других вещей. Существует много линейной регрессии (и много-х Gotcha), но, как это просто сделать для простых случаев. Вот пример R, используя данные. Обратите внимание, что «ТХ» ​​является перехват вашей модели.

> 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  
Ответил 17/09/2008 d 15:22
источник пользователем

голоса
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
Ответил 30/12/2014 d 15:24
источник пользователем

голоса
1

Лично я неравнодушен к алгоритмам Numerical Recipes . (Я люблю издание C ++) .

Эта книга научит вас, почему алгоритмы работы, а также показать вам некоторые довольно-а отлажена реализации этих алгоритмов.

Конечно, вы можете просто слепо использовать CLAPACK (я использовал его с большим успехом), но я бы первым вручную ввести алгоритм исключения Гаусса , по крайней мере , имеют слабое представление о том , какой работе , которая прошла в создании этих алгоритмов стабильный.

Позже, если вы делаете более интересную линейную алгебру, оглядывая исходный код Октава будет ответить на множество вопросов.

Ответил 25/08/2008 d 19:22
источник пользователем

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