Вычисление определителя матрицы любой размерности
У меня просьба–проверить код сначала визуально(если кому-то не лень:)),а затем в деле.Мои тесты(с матрицами размера 0..5) он прошёл без проблем(сверялся с MathCAD'ом)
Синтаксис процедуры прост:1й параметр–указатель на матрицу двойных слов(матрица располагается в памяти линейно по строкам),2й–размерность матрицы.На выходе–определитель заданной матрицы
В аттаче приведены коды для .exe и .dll,а также сама библиотека и файл .lib(если вы линковать вдруг будете;например,у вас нет асма и вы будете тестировать на C++)
Также буду рад замечаниям по улучшению кода,вашим кодам вычисления определителя(если вы делали);желательно на асме,конечно,C/Delphi/VB в крайнем случае.Если есть какие-то более лёгкие и быстрые методы–с радостью рассмотрю
Если возникнут какие-то вопросы типа как пользоваться,объяснить структуру или зачем ТС это понадобилось–милости просим (= Постараюсь всё разъяснить
Не знаю,куда он подевался,вроде цеплял.Но теперь он точно есть,поэтому прошу по возможности(по желанию:)) почистить свои мессаги и приступить к делу
Proger_XP :)[/COLOR]
Upd:вот и отлично.Буду рад содействию в тестировании и совершенствовании.Просто это вроде как 1й мой проект,который можно совместить с учёбой(предыдущее всё было только для себя,в основном;в смысле,с учёбой не связанное)
(честно сказать, с ассемблером еще не сталкивался, сейчас скачал masm (надеюсь я угадал что скачать?), пока что остановился на ошибке
fatal error RC1110 : could not open rsrc.rc
завтра продолжу бороться с ним))
ну не в этом суть.
я недавно писал подсчет определителя, алгоритм такой:
Приводим матрицу к единичному виду (главная диагональ состоит из 1, под ней нули, над ней (в этом случае) неважно что)
[COLOR="DimGray"]Как приводить: берем элемент [1][1], если он не равен нулю, то делим строку с этим элементом на него же, и сохраняем этот элемент в отдельной переменной(т.е выносим как бы за скобку), от остальных строк(можно от строк, стоящих "ниже" текущей) отнимаем эту строку умноженную на элемент [1]. получаем столбец (1,0,0...0). идем на [2][2] и повторяем действия.... Если, например, перебором по [1] не нашли элемента, отличного от нуля, то определитель соотв. равен нулю. [/COLOR]
"Вынесенные элементы" потом перемножаем и получаем определитель матрицы.
p.s. ну и не забываем, если элемент отрицателен, то можно умножить строку на -1, и вынесенный коэфициент умножить на -1.
код: (с++, признаюсь он не совершенен)
{
j1=0;j=0;
for(i=0;i<n;i++)
{
if(str==0)
{
if(matr[j] != 0)
{
if(matr[j] < 0)
{
for(j1=0;j1<n;j1++)
matr[j1]=matr[j1]*(-1);
koef=koef*(-1);
}
str=i;
koef=koef*matr[j];
k=matr[j];
for(j1=0;j1<n;j1++)
matr[j1]=matr[j1]/k;
j1=j;i1=i+1;
while(i1<n)
{
k=matr[i1][j1];
for(;j1<n;j1++)
matr[i1][j1]=matr[i1][j1]-k*matr[j1];
i1++;j1=j;
}
str=0;j=i+1;flag=false;
}
else
{
for(j1=0;j1<n;j1++)
swap(matr[j1],matr[n-1][j1]);
i--;koef=koef*(-1);
if(flag)
{
i++;koef=0;
}
flag=true;
}
}
}
return koef;
};
забыл добавить: чем этот алгоритм плох - я постоянно делю дробное число на дробное, отсюда неточность, если она принципиально важна - мой алгоритм не катит
fatal error RC1110 : could not open rsrc.rc
Угадал-угадал (=
Это при компиляции ошибка?Ну так у меня нет файла ресурсов,а в командной строке у тебя наверняка указана эта опция;)
Вполне могу.Считаю я его стандартным алгоритмом,через миноры.Т.е. в лоб с рекурсией;насколько я понимаю,это долго…но до других методов я пока не дошёл:)
Дело в том,что работать с вещественными числами на асме я пока не умею.Да и код твой,ИМХО,великоват для меня(нет,не в смысле он у тебя плохой…просто на асме это будет что-то=)).Поэтому он мне пока не подходит…но в будущем надеюсь его рассмотреть,особенно если он будет быстрее моего
В любом случае,спасибо за ответ
да, скорее всего ты прав =)
Ну да, я представляю...))
он будет быстрее если я сразу же найду столбец в нулями, тогда цикл завершится и ответ - 0. А в других случаях врядли он будет быстрее, мне понравился просто "необычный, нестандартный я б сказал" подход =)
Это откуда такая фраза?:) Про твой метод,я так понимаю
Кстати,в аттаче есть ещё DLL(ну ты видел:)),так вот,чтобы не заморачиваться с компиляцией кода,просто воспользуйся DLL.Синтаксис я написал
Для начала проверь на простых матрицах–от 1 до 3,а уж потом можно и посложнее.Я вот проверял на 8x8,пока работает:rolleyes:
ну да, я непонятно выразился :) просто наблюдал, кто как пишет(даже просто как считают в тетрадке), большинство людей идут простым разложением по первой(чаще всего) строчке, и не заморачиваются.
а тут идея интересная - выносить множитель и приводить к единичной матрице...
Для начала проверь на простых матрицах–от 1 до 3,а уж потом можно и посложнее.Я вот проверял на 8x8,пока работает:rolleyes:
окей, посмотрю, потом отпишусь =)
Т.к. автор сей программы(т.е. я) поместил код и данные в одну секцию(кода;в самом деле,зачем под небольшое количество данных новую секцию?),при линковке следует установить атрибут секции .text,разрешающий запись в неё,так-то.А то мы на пару с z0rch'ем долго думали,почему он ловит AW,в то время как у меня всё отлично (=
[COLOR="SlateGray"]подскажите, пожалуйста, в чем здесь ошибка?
( уже сил нет, замучал и себя, и топикстартера :D )[/COLOR]
после долгих мучений, благодаря @pixo $oft, решение было найдено.
{
[COLOR="Red"] int d=0;//а не глобально!!![/COLOR]
int b[10][10];
if(n==2)
{
return (aa[0][0]*aa[1][1]-aa[0][1]*aa[1][0]);
}
for(int i=0;i<n;i++)
{
for(int y=1; y<n; y++)
for(int x=0; x<n; x++)
{
if(x==i) continue;
if(x<i)
b[y-1][x] = aa[y][x];
else
b[y-1][x-1] = aa[y][x];
}
d+=pow(-1,(double)(2+i))*aa[0]*det(b,n-1);
}
return d;
}
Но недавно как раз писал библиотеку для матричных вычислений
(быстрых, а не просто, чтоб ситалось)
Вообщем пришел к выводу что самый быстрый(по скорости) способ работы с матрицами, это хранение матрицы в непрерывном массиве T*, а не T** и работа с ней через указатели.
Определитель расчитывается методом Гаусса с выбором ведущего по столбцу.
И вот результат:
inline double det(CMatrix<double> mtx)
{
SafeRankVec(mtx.rows,mtx.cols); //Проверка квадратности
bool sgn = true;
double *_IT_begin = mtx._begin(), *_IT_end = _IT_begin + mtx.cols*mtx.cols;
double *_IT_ii = _IT_begin, *_IT_ii_end = _IT_ii+mtx.cols;
double determ = 1.0;
for(int d = mtx.cols;_IT_ii<_IT_end; determ*=*_IT_ii, _IT_ii+=mtx.cols+1, _IT_ii_end+=mtx.cols, d--)
{
double *_mx = mem::nmax_abs(_IT_ii,mtx.cols,d); //ищем максимальный элемент в столбце
if(abs(*_mx) < dEps) return 0.0;
else
if(_mx!=_IT_ii)
{
mem::nmemswap(_IT_ii,_mx,d); //замена строк
sgn = !sgn;
}
for(double *_IT_i=_IT_ii+mtx.cols;_IT_i<_IT_end;_IT_i+=mtx.cols)
for(register double a = *_IT_i / *_IT_ii, *_IT_ii_j = _IT_ii + 1, *_IT_i_j = _IT_i + 1; _IT_ii_j<_IT_ii_end; *(_IT_i_j++)-= *(_IT_ii_j++) * a);
}
if(sgn) return determ;
else return -determ;
}
К сожалению у данного кода существенный недостаток: он просто жуть как нечитаем ))). Даже мне чтоб вспомнить что там за что отвечает - понадобиться много времени.
Зато он даже LAPAC обгоняет по скорости.
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace lab1
{
class Matrix
{
public int row, col;
public int[,] p;
double det;
public Matrix(int n, int m)
{
row = n;
col = m;
p = new int[n, m];
}
public void InitMartix() // Ввод матрицы
{
Random aa = new Random();
for (int i = 0; i < row; i++)
for (int j = 0; j < row; j++)
{
p[i, j] = aa.Next(5);
}
}
public double Det2x2()
{
double det;
det = p[0, 0] * p[1, 1] - p[0, 1] * p[1, 0];
return det;
} //Det 2x2
public void ShowMartix() //Вывод матрицы
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < row; j++)
{
Console.Write(p[i, j]);
Console.Write(" ");
}
Console.WriteLine();
}
}
//Минор
public Matrix Menor(int a, int b)
{
int i, j, p, q;
Matrix MEN = new Matrix(row - 1, col - 1);
for (j = 0, q = 0; q < MEN.col; j++, q++)
for (i = 0, p = 0; p < MEN.row; i++, p++)
{
if (i == a) i++;
if (j == b) j++;
MEN.p[p, q] = this.p[i, j];
}
return MEN;
}
//Определитель матрицы
public static double Det(Matrix B)
{
int n;
int signo;
double det = 0;
if (B.row != B.col)
{
Console.WriteLine("Matritsa dolgna biti kvadratnoi");
return 0;
}
else
if (B.row == 1)
return B.p[0, 0];
else
if (B.row == 2)
return B.Det2x2();
else
for (n = 0; n < B.col; n++)
{ //Проверка на знак
if ((n & 1) == 0)
{
signo = 1;
}
else
{
signo = -1;
}
//(n&1)==0 ? (signo=1):(signo=-1);
det = det + signo * B.p[0, n] * Det(B.Menor(0, n));
}
return det;
}
}
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Введите размерность первой матрицы: ");
int[,] A = new int[Convert.ToInt32(Console.ReadLine()), Convert.ToInt32(Console.ReadLine())];
Matrix m = new Matrix(A.GetLength(0), A.GetLength(0));
m.InitMartix();
Console.WriteLine("Matrix");
m.ShowMartix();
Console.WriteLine();
Console.Write("Det = ");
Console.Write(Matrix.Det(m));
Console.WriteLine();
Console.ReadKey();
}
}
}
На сишарпе-вычислит любой тебе определитель-главное не тупить))
[/QUOTE]
Это очень круто, особенно когда речь как раз идет о скорости расчета... Ну а если на даты посмотреть, можно вообще порадоваться. :)
[QUOTE=АллаКо]
главное не тупить))
[/QUOTE]
Эх... Золотые слова! :)
В свете текущих событий я вовсе не против возрождения данной темы ☺
arrjj,какие будут варианты?;)