Решение практических задач на Паскале. Выпуск 6
На нашем форуме (http://www.yourpascal.com/viewforum.php?f=6) был задан вопрос о возведении матрицы в степень в неподходящем для этого месте "нужно напечатать период дроби (на паскале)". Отвечу в рассылке, в которой давно уже ничего не писал. Увы! Времени очень мало
В этом выпуске я представлю способ решения задачи с использованием рекурсии, так как давно искал повод для этого :)). Вот и пришло время ...
Сейчас будет предложен вариант решения, в котором ранг матрицы фиксирован. Он задается константой max в модуле uMatrics. Если Вам захочется изменить ранг матрицы, то нужно изменить значение константы и заново откомпилировать программу. Это не очень удобно и в Delphi для решения такой проблемы существуют динамические массивы (которых в Паскале нет :(( ).
Но если нельзя, но хочется, то добрый старый Паскаль позволяет сделать все ... Правда, для этого матрицу следует расположить в динамической памяти компьютера и работать с ней с помощью указателя. Чтобы было легче понять более сложный вариант, я и в этот раз буду размещать матрицу в динамической памяти. Это вовсе не обязательно. Можно обойтись "традиционными" двумерными массивами, объявленными в разделе VAR программы. Но используя указатели на массивы, размещенные в динамической памяти, позволяет использовать функции, а не процедуры. А это тоже плюс
Если описанный здесь способ покажется сложным, то напишите мне - я опишу и тот, более простой, способ решения с "обычными" массивами-матрицами.
Метод рекурсий заключается в том, что в программе вызывается подпрограмма. А она вызывает саму себя до тех пор, пока не будет выполнена какая-то группа действий. Например, в данном случае, подпрограмма вычисления степени PowMatrix будет выполнять исходной умножение матрицы на нее же и вызывать самую себя с показателем степени на единицу меньше. Так будет происходить до тех пор, пока показатель степени, передаваемый подпрограмме в виде параметра не станет меньше нуля.
Достоинство метода рекурсий в том, что код получается маленький (и, вернее, он гусарский). Недостаток в том, что неэффективно используется память и при возведении в большую степень может не хватить стека. Тогда стек нужно увеличить за счет кучи. Как это делается, можно посмотреть в стандартной справке, если набрать $M и вызвать контекстную помощь, нажав сочетание Ctrl+F1.
Замечу, что испытания моей программы показали: переполнение стека наблюдается при возведении матрицы в степень больше 650. А для меньших степеней гораздо вероятнее получить ошибку переполнения. Она возникает, когда в переменную типа Real делается попытка записать значение больше допустимого.
Сначала приведу исходный код программы, а потом дам пояснения. Хотите - читайте ...
Все основное находится в модуле uMatrics. Он показан в листинге 1.
Для тестирования используется программа Solve006.pas, текст которой приведен в листинге 2.
{Листинг 1}
unit uMatrix;
interface
CONST
max = 2;
TYPE
PMatrix = ^TMatrix;
TMatrix = array [1..max, 1..max] of Real;
function NewMatrix: PMatrix;
function PowMatrix(Source: PMatrix; pow: Integer): PMatrix;
procedure ShowMatrix(matrix: PMatrix; Dest: String; Size, Digs: Byte; mes: String);
procedure AutoFillMatrix(var Matrix: PMatrix);
function MulMatrix(source1, source2: PMatrix): PMatrix;
function UnitaryMatrix: PMatrix;
implementation
{Возведение матрицы в степень pow}
function PowMatrix(Source: PMatrix; pow: Integer): PMatrix;
var p: PMatrix;
begin
p:=NewMatrix;
case pow of
-MaxInt..-1: Exit;
0: p:=UnitaryMatrix;
else
p:=MulMatrix(Source, PowMatrix(Source, Pow-1));
end;
PowMatrix:=p
end;
{Локальная подпрограмма для форматированного вывода числа типа Real
При Size <= 0 вывод не форматируется
При Dig < 0 используется только Size}
function RealToStrFmt(Value: Real; Size, Digs: Byte): String;
var s: String;
begin
if Size <= 0 then Str(Value, s) else
if Digs < 0 then Str(Value:Size, s) else Str(Value:Size:Digs, s);
RealToStrFmt:=s
end;
procedure ShowMatrix(matrix: PMatrix; Dest: String;
Size, Digs: Byte; mes: String);
var i, j: Integer;
f: Text;
begin
if Matrix = nil then Exit;
if Dest <> '' then
begin
Assign(f, Dest);
{$I-} Reset(f); {$I+}
if IOResult <> 0 then begin
{$I-} Rewrite(f); {$I+}
if IOResult <> 0 then
begin
WriteLn('Невозможно создать файл ', DEST);
WriteLn('Вывод будет происходить на экран');
Dest:='';
end;
end
else
begin
Close(f);
Append(f);
WriteLn(f);
WriteLn('Результаты будут дописаны в конец файла ', Dest)
end;
end;
if Dest = '' then WriteLn(mes) else WriteLn(f, mes);
for i:=1 to max do begin
for j:=1 to max do
if Dest = '' then
Write(RealToStrFmt(Matrix^[i,j],Size,Digs),' ')
else
Write(f, RealToStrFmt(Matrix^[i,j],Size,Digs),' ');
if Dest = '' then
WriteLn
else
WriteLn(f);
end;
if Dest <> '' then
begin
Flush(f);
Close(f);
end;
end;
procedure AutoFillMatrix(var Matrix: PMatrix);
var i, j: Integer; k: Integer;
begin
if Matrix = nil then Matrix := NewMatrix;
k:=1;
for i:=1 to max do
for j:=1 to max do
begin Matrix^[i,j]:= -k; inc(k) end;
end;
{Перемножение матриц}
function MulMatrix(source1, source2: PMatrix): PMatrix;
var i, j, k: Integer;
Dest: PMatrix;
begin
New(Dest);
for i:=1 to max do
for j:=1 to max do begin
Dest^[i, j]:=0;
for k:=1 to max do
Dest^[i, j] := Dest^[i, j] + Source1^[i,k]*Source2^[k,j]
end;
MulMatrix := Dest;
{ ShowMatrix(Dest,'');}
end;
function UnitaryMatrix: PMatrix;
var i, j: Integer;
p: PMatrix;
begin
New(p);
for i:=1 to max do
for j:=1 to max do
if i=j then p^[i,j]:=1 else p^[i,j]:=0;
UnitaryMatrix:=p
end;
function NewMatrix: PMatrix;
var p: PMatrix;
begin New(p); NewMatrix:=p end;
{"Служебная" часть модуля для обеспечения
правильной работы с динамической памятью}
function HeapFunc(Size: Word): Integer; far;
begin HeapFunc:=1 end;
VAR
varForHeapRelease: Pointer;
SaveExit: Pointer;
procedure FreeAllMemory; far;
begin
Release(varForHeapRelease);
ExitProc:=SaveExit;
end;
BEGIN
Mark(varForHeapRelease);
HeapError:=@HeapFunc;
SaveExit:=ExitProc;
ExitProc:=@FreeAllMemory;
END.
{Листинг 2}
uses uMatrix;
VAR
m: PMatrix;
BEGIN
WriteLn;
AutoFillMatrix(m);
ShowMatrix(m, 'test.txt',12, 2, 'Исходная матрица');
m:=PowMatrix(m, 2);
ShowMatrix(m,'test.txt', 12, 2, 'Матрица после возведения в степень 2');
END.
Как отмечено вначале, здесь использован самый дубовый прямой метод: матрица каждый раз умножается сама на себя столько раз, в какую степень хотим возвести ее. По определению: A0 = E; A1 = А; AN = AN-1·A. Здесь E - единичная матрица.
Расчет производится по формуле:
В модуле определены шесть подпрограмм для непосредственного решения задачи и две вспомогательные для обеспечения правильной работы с динамической памятью.
В разделе объявлений модуля "объявляются" новые типы переменных:
В модуле созданы следующие подпрограммы:
else
p:=MulMatrix(Source, PowMatrix(Source, Pow-1));
По-моему, подпрограмма простая
При работе с динамической памятью могут возникнуть две неприятности:
Программа простая. Сначала определена переменная типа PMatrix, указатель на двумерный массив. Затем вызывается процедура автоматического заполнения матрицы. Если хотите, можете написать другую процедуру, которая значение элементов будет считывать с клавиатуры. Затем все просто:
В результате должен получиться файл с содержимым, показанным ниже
Это результаты работы программы: содержимое файла test.txt
Исходная матрица
-1.00 -2.00
-3.00 -4.00
Матрица после возведения в степень 2
7.00 10.00
15.00 22.00
Недостатком предложенного метода является то, что пользователь может вызвать некоторые из подпрограмм с неверными параметрами. В худшем случае ошибок времени выполнения может и не произойти. Тогда человек будет думать, что расчет выполнен правильно. Избавиться от этого недостатка можно только с помощью объектов. Там можно и удобный интерфейс предоставить и ошибки легко отслеживать. Добавьте еще отмеченные проблемы с использованием памяти
Исходный текст программы и модуля можно скачать в виде архива по адресу http://www.borlpasc.narod.ru/Boris/Solve006.zip
Мы приглашаем Вас и Ваших друзей к сотрудничеству. Напишите, какая проблема Вас лично интересует - и мы постараемся помочь Вам. Поделитесь со всеми, если Вам удастся найти красивое решение. Присылайте свои программы, и если они хороши, то опубликуем их с обязательным указанием Вашего авторства.
По всем вопросам можно писать либо в Гостевую книгу нашего сайта на www.turbopascal.tk, либо
мне,
Постараюсь ответить на все вопросы и учесть все разумные предложения
Рассылка поддерживается сайтом www.turbopascal.tk. При перепечатке ссылка на сайт обязательна
Обращаем внимание: наш форум размещается на www.yourpascal.com !!!
Внимание: сессия и экзамены еще не начались - самое время подписаться на нашу рассылку:
|
|