Mnożenie macierzy, nieprawidłowy wynik

0

Witam,
piszę tu, bo mam kłopot ze zrozumieniem kodu z zakresu algebry macierzy.
Ogólnie, to jest to kod napisany przez mojego wykladowce, niestety moim skromnym zdaniem zle liczy.
Wiec usprawnilbym to, by dzialalo, bo zapewne jak przyjdzie do zaliczenia, to sie okaze, ze mimo iz sam popelnil blad to nas za to ukarze.

inne procedury z tego zadania takie jak dodawanie, wypisywanie czy czytanie macierzy ogarnąłem, także nie jestem jakimś kosmicznym darmozjadem :)
wycinek programu z którym mam kłopoty:

const max=10;
type mac=array[1..max,1..max] Of real;
var n:integer; {wymiar macierzy}
a,b,d,m:mac; 
procedure mno_mac (var x:mac; y,z:mac; m,l,l1:integer);
var i,j,k:integer;
begin
for i:=1 to m do
for j:=1 to l do begin
x[i,j]:=0;
for k:=1 to l1 do
x[i,j]:=x[i,j]+y[i,j]*z[k,j];
end;
end; 
mno_mac(M,D,A,n,n,n); 

Gwoli wyjaśnienia: D to macierz powstala z dodania macierzy A i B co miało miejsce w procedurze powtarzajacej mno_mac, M natomiast docelowo miałoby być macierzą po pomnożeniu.
no i wedle mnie źle liczy.
Każda pomoc, porada, poradnik są mile widziane, dzięki!

2

Przede wszystkim ten kod:

const max=10;
type mac=array[1..max,1..max] Of real;
var n:integer; {wymiar macierzy}
a,b,d,m:mac; 

{...}

procedure mno_mac (var x:mac; y,z:mac; m,l,l1:integer);
var i,j,k:integer;
begin
for i:=1 to m do
for j:=1 to l do begin
x[i,j]:=0;
for k:=1 to l1 do
x[i,j]:=x[i,j]+y[i,j]*z[k,j];
end;
end; 

mno_mac(M,D,A,n,n,n); 

powinien wyglądać co najmniej tak:

const
  Max = 10;

type
  Mac = array [1 .. Max, 1 .. Max] of Real;

var
  N: Integer;
  A, B, D, M: Mac;

{...}

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      begin
        X[I, J] := 0;

        for K := 1 to L1 do
          X[I, J] := X[I, J] + Y[I, J] * Z[K, J];
      end;
end;

{...}

Mno_Mac(M, D, A, N, N, N);

żeby człowiek mógł go w miarę szybko przeczytać i zrozumieć... Poza tym identyfikatory to masz tak zajebiste, że trzeba w kółko patrzyć góra, dół, góra, dół żeby cokolwiek z tego "kodu" zrozumieć... Nie dziw się, jak ten wątek będzie kwitnął tu jeszcze dwa tygodnie i nikt Ci nie odpowie...

Poza tym, co to za mania:

const
  Max = 10;

type
  Mac = array [1 .. Max, 1 .. Max] of Real;

??? To już nie można normalnie napisać:

type
  Mac = array [1 .. 10, 1 .. 10] of Real;

zważywszy na to, że używasz tej stałej tylko w deklaracji macierzy i nigdzie indziej, więc to sensu większego nie ma;

Jeszcze jedno - macierze indeksuje się od zera, a nie od jedynki; Nie sprzedajesz jabłek na bazarze tylko tworzysz kod programu, więc potraktuj go poważnie;

Nawiasem pisząc - masz tyle materiału na sieci o operacjach na macierzach (choćby tu czy tu), że naprawdę masz się z czego uczyć;

Jeżeli Twój szanowny "profesor" popełnił błąd - olej to i napisz swój kod, ale o wiele łatwiej jest napisać go od nowa (szczególnie w tym przypadku, gdzie masz do napisania raptem kilkanaście linii) niż poprawiać po kimś; Więc zabierz się za naukę (jeżeli oczywiście nie rozumiesz), a potem napisz kod po swojemu; Jak Ci nie będzie wychodzić - poproś nas o pomoc; Ale zrób coś samemu i nie patrz na tamten kod, bo można bólu głowy dostać...

0

dzięki,
powiem Ci tak: z jednej strony Twoje słowa bardzo mnie cieszą, bo to oznacza, że nie jestem w błędzie.
To znaczy, mój wykładowca, doktor habilitowany z politechniki śląskiej, wielkie G z delphi potrafi. Ten kod to 100% jego "praca", a że jest on człowiekiem oceniającym czy to dokładnie to co on nam podawał, więc bez sensu się teraz uczyć chyba tego serio tak jak to być powinno.

Masz zdecydowaną rację, lepiej by się było tego nauczyć samemu od zera tak jak to powinno działać, tak bym to rozumiał, program działał, a kod spełniał pewne standardy, natomiast nie wiem czy taka "niesubordynacja" nie utrudni mi- paradoksalnie- zaliczenia.

ale dzięki, poruszam się naprzód :P

a co do matematycznej strony działań na macierzach, to sprawę ogarniam, przerabiałem już ten materiał. Jednakże ciężej mi to przedstawić już w poziomie programistycznym ;)

1

Z tego co napisałeś wynika w pewnej mierze, że się poddajesz, a to bardzo duży błąd; Każdy się myli, nawet profesor rehabilitowany czy jak to nazwałeś; Jeżeli napiszesz algorytm poprawnie w odróżnieniu od tego co podałeś (jeżeli ten faktycznie krzywo liczy) i nie będziesz cwaniakował to myślę, że nikt Ci nie utrudni zaliczenia szczególnie dlatego, że masz rację; Napisz kod i pokaż profesorowi, że potrafisz i zasługujesz na zaliczenie; Poza tym nie wiadomo, czy ten kod celowo nie jest spartolony; Może tak być, a profesor tylko czeka kto pierwszy na to wpadnie; Jak wpadnie to udowodni, że materiał rozumie; To tylko spekulacje, ale za mienie racji nikt Ci krzywdy nie powinien zrobić;

Zachęcam do przestudiowania podanych przeze mnie materiałów (z resztą wybierz sobie artykuł taki, jaki Ci pasuje i jaki szybko połkniesz) i napisania poprawnego algorytmu; Nie tylko zrozumiesz matematyczną problematykę zagadnienia, ale także poćwiczysz zdolności programistyczne; To zagadnienie nie jest trudne, więc powinieneś je zrozumieć bez problemów; Implementacja to inna sprawa, ale programista może wszystko, więc do roboty :)

@ Przy okazji - sposobów mnożenia macierzy jest kilka, więc napisz przynajmniej z jakiego korzystasz; No i użyj macierzy dynamicznych :)

0

hmm szczerze to znam jedynie z algebry, czyli kartka i dlugopis, tylko jeden sposób mnożenia macierzy :P
a jeśli chodzi o sposoby przedstawienia jakiegoś algorytmu który by się tym zajął w kodzie, to pomysłu nie mam, choć niestrudzenie poszukuję!

Jeden podany przez profesora tak jakby... nie działa ;)

co do macierzy dynamicznych, pojęcia nie mam czym są, ale już uruchamiam google i pewnie zaraz się dowiem!

P.S.
dzięki za dotychczasową pomoc :)

1
paro napisał(a)

hmm szczerze to znam jedynie z algebry, czyli kartka i dlugopis, tylko jeden sposób mnożenia macierzy

Czyli nie czytałeś artykułów na wikipedii, które Ci podsunąłem...

paro napisał(a)

a jeśli chodzi o sposoby przedstawienia jakiegoś algorytmu który by się tym zajął w kodzie, to pomysłu nie mam, choć niestrudzenie poszukuję!

To nie poszukuj, tylko włącz kompilator i pisz kod! Marnujesz czas na szukanie gotowców, których pewnie nie zrozumiesz, bo Ci wystarczy, że działają... Jak napiszesz sam, to będziesz wiedział do czego służy każda instrukcja i jak potrzebna będzie modyfikacja - spędzisz nad tym kilka minut, a nie godzinę próbując najpierw kod zrozumieć, później przerobić;

paro napisał(a)

co do macierzy dynamicznych, pojęcia nie mam czym są, ale już uruchamiam google i pewnie zaraz się dowiem!

A na cholere głowę zawracać dolinie krzemowej... Na tym portalu masz nie tylko forum, ale także kompendium wiedzy z kilku języków, więc tam poczytaj przede wszystkim; Tu masz link;

0

ooook, wracam do szeregu ;)

wiesz, przede wszystkim jestem niedokładny i jak widzę nie dodałem, że użytkownik sam zdefiniuje rozmar tej macierzy

 WRITE('Podaj wymiar maciery:');READLN(n);

,
a macierze będą kwadratowe.

pozostanę chyba przy takich macierzach jakie tam mam, muszę tylko napisać coś co zrozumiem a dzięki czemu istotnie zostanie policzony iloczyn :)

P.S.
poczytalem wlasnie z wiki o macierzach i jestem w szoku ile jest rodzajów iloczynów :P!

1
paro napisał(a)

przede wszystkim jestem niedokładny i jak widzę nie dodałem, że użytkownik sam zdefiniuje rozmar tej macierzy

Więc czas ruszyć dupsko i poczytać o macierzach dynamicznych, bo innego wyjścia nie masz :P Jeżeli macierze mają być kwadratowe, to w sumie użytkownik musi podać tylko jedną wartość, bo wszystkie trzy macierze będą miały ten sam rozmiar i będziesz mógł go ustawić bez obliczeń; Jeśli zaś wykorzystasz iloczyn Kroneckera, będziesz musiał wykonać jedno obliczenie dla macierzy wyjściowej (tej z wynikami);

paro napisał(a)

poczytalem wlasnie z wiki o macierzach i jestem w szoku ile jest rodzajów iloczynów

A widzisz ;)

1

Powinno być x[i,j]:=x[i,j]+y[i,k]*z[k,j]; //pewnie popełniłeś błąd przepisując kod wykładowcy :)

0

Dzięki Wam obu chłopaki.

Furious Programming przywrócił mi wiarę w programowanie i z chęcią nauczę się tego o czym pisałeś,
natomiast dzięki Pelscie liczy tym sposobem tak jak powinien (domyślam się, że sam sposób jest równie marny co mój prof.).

a z FP jest lepszy pedagog niż z tego dr. habilitowanego :P

wiesz, to chyba kwestia tego, ze nikt u nas za bardzo tego nie ogarnia, a sam prof ogranicza sie jedynie to przepisywania na tablice kodów, a wychodzi na to ze on sam tam taki blad popelnil.
no ale, jestem duzy krok do przodu :P

widzisz, tylko omawia to chyba zbyt obszerne słowo.
póki wygląda i zachowuje się jak darth vader...

no nic, ale dzięki ;)

0
paro napisał(a)

domyślam się, że sam sposób jest równie marny co mój prof.

Dlaczego? Nie dość, że działa to jeszcze jest efektywny; Nie wiem czy istnieje możliwość zoptymalizowania go jeszcze bardziej ;)

pelsta napisał(a)

Napisz lepszy kod od Twojego wykładowcy a potem go krytykuj!

Dokładnie; Ale jest jedno ale - algorytm ten wykonuje iloczyn macierzy jedynie w przypadku, gdy obie wejściowe są kwadratowe; Stąd wniosek - napisz algorytm uniwersalny dla dowolnych dwóch macierzy wejściowych (oczywiście spełniających podstawowy warunek co do ich rozmiarów), przy czym będzie jak najbardziej efektywny; Nie dość, że nauczysz się myśleć jak programista, to jeszcze pozytywnie zaskoczysz profesora; I nauczysz się posługiwać macierzami dynamicznymi :)

Warto rozszerzać możliwości programów - to dużo uczy;

@ Przy okazji - nie wiem, dlaczego w tym kodzie:

for I := 1 to M do
  for J := 1 to L do
    begin
      X[I, J] := 0;

      for K := 1 to L1 do
        X[I, J] := X[I, J] + Y[I, K] * Z[K, J];
    end;

w bloku grupującym jest wykonywane podwójne wpisanie informacji do komórki X[I, J]; Niestey nie mam możliwości skompilować tego kodu, ale sądzę, że blok begin .. end jest w tym momencie zbędny... @paro, zobacz, czy poniższy algorytm zostanie poprawnie wykonany:

Pelsta zwrócił mi uwagę, że trzeba mieć pewność, że macierz jest wyzerowana; Sądzę, że nie musi być pod warunkiem, że tą instrukcję przypisania wartości do komórki:

X[I, J] := X[I, J] + Y[I, K] * Z[K, J];

zamienimy na taką:

X[I, J] := 0 + Y[I, K] * Z[K, J];

Czy to nie będzie zarowanie i wpisanie poprawnej wartości do komórki jednocześnie? Całość:

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      for K := 1 to L1 do
        X[I, J] := 0 + Y[I, K] * Z[K, J];
end;

Jak to według Was wygląda?

0
Furious Programming napisał(a)

Ale jest jedno ale - algorytm ten wykonuje iloczyn macierzy jedynie w przypadku, gdy obie wejściowe są kwadratowe;

Nie masz racji! Mnoży on macierze o wymiarach A[n,m] x B[m,k] = C[n,k]
Nie muszą być kwadratowe.
Właśnie dlatego wymaga tylu parametrów.

0

Ooops, no przecież... Mogłem poznać właśnie po ilości argumentów... :P

Swoją drogą, jeżeli wykonuje iloczyn dowolnych rozmiarów macierzy, to jest on o wiele krótszy, niż podany w artykule Mnożenie macierzy w dziale gotowce:

i := 0;
k := 0;
l := 0;
j := 0;

for i := Low(A) to High(A) do
  begin
    while l <= High(B[0]) do
      begin
        suma := 0;
        k := 0;

        for j := Low(A[0]) to High(A[0]) do
          begin
            suma := suma + A[i, j] * B[k, l];
            inc(k);
          end;

        C[i, l] := suma;
        inc(l);
      end;

    l := 0;
  end;

I co sądzicie o skróconej wersji:

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      for K := 1 to L1 do
        X[I, J] := 0 + Y[I, K] * Z[K, J];
end;

Ma to prawo działać?

0

słusznie prawisz, dalej działa.

Nie chciałem objeżdzać tegoż profesora, mimo że swoją frustrację kieruję w jego stronę, ale specjalnie zaznaczyłem, że jedynie domyślam się że ten algorytm jest średni.
Odniosłem takie wrażenie, po Twojej reakcji na niego :)
Da się przecież wyczuć, że rozmawiacie z człowiekiem zielonym jak choinka.

Co do samej nauki, o masz rację, to z pewnością wiele by mi dało, jednak w tym momencie raczej patrzę w stronę sesji i widzę tam fizykę, a informatyka jedynie jest bolączką jeśli chodzi o zaliczenie, takoż chętnie, ale potem :)

ale może spróbuję, czemu nie. Będę miał na przyszłość.

0

@Furious Programming

No wiesz. Jeżeli gościu od gotowca pisze coś takiego

i:=0;k:=0;l:=0;j:=0;
        for i := Low(A) to High(A) do

to bym się nim za bardzo nie nakręcał :)

A Twój "zoptymalizowany" kod na mnożenie działa poprawnie tyko przy pierwszym wywołaniu procedury mnożenia, bo kompilator zeruje tablice statyczne w momencie ich tworzenia.

0

Swoja drogą teraz to jakoś bardziej ma ręce i nogi, nie za bardzo łapałem sens tego definiowania x[i, j] jako 0.

To mialo jakis logiczny sens przy powiązaniu z begin i end? Bo dla mnie to był najbardziej bełkotliwy fragment i tak niezrozumiałego kodu.

a i jeszcze widzisz mam teraz nast. pytanie :P!
Po co właściwie pisać

0 + Y[I, K] * Z[K, J] 

?
W sensie: niczego nie podważam, bo pewnie o zgrozo przestanie prawidłowo działać po wyrzuceniu zera, ale z matematycznego punktu widzenia, to kuriozum.
Czyż nie?

0
pelsta napisał(a)

to bym się nim za bardzo nie nakręcał

No właśnie mnie ten algorytm zmylił, bo jest dłuższy i wykonuje iloczyn dowolnego rozmiaru macierzy; Ten, który podał @paro jest trzykrotnie krótszy i dlatego mylnie założyłem, że nie spełnia tego warunku ;)

Co sądzicie o tej wersji algorytmu:

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      for K := 1 to L1 do
        X[I, J] := 0 + Y[I, K] * Z[K, J];
end;

O cholera, jasne, że nie zadziała, nie popatrzyłem dokładnie, że pętla for K := 1 to L1 do przestanie działać prawidłowo... Wybaczcie, nie zauważyłem tego, zwracam honor... :P

Poprawny i koniec jest ten algorytm:

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      begin
        X[I, J] := 0;

        for K := 1 to L1 do
          X[I, J] := X[I, J] + Y[I, K] * Z[K, J];
      end;
end;

i koniec :P

0

Myślicie to w tej dziedzinie słowo na wyrost jeśli kierowane do mnie, aczkolwiek muszę stwierdzić, że wynik podaje prawidłowy.

a dorzucając, jeśli mogę do puli pytań, to miałbym jeszcze takie:
robi róznicę czy podamy x oraz y,z razem bądź osobno

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer) 

?

w końcu to ten sam typ, prawda?

żebym Ci ja takie miał, to bym nie zawracał gitary o 23,40 na forum :P
wykłady po przepisaniu kilkunastu programów bez zbędnych objaśnień przeszły na inne dziedziny informatyki, ćwiczenia nie otarły się o jakiekolwiek wyjaśnienia co i dlaczego, natomiast informatyka nie jest u mnie przedmiotem wodzącym więc...
;)

0
paro napisał(a)

robi róznicę czy podamy x oraz y,z razem bądź osobno [...] w końcu to ten sam typ, prawda?

To nie chodzi o typy argumentów, ale o ich widoczność; Coś słabo u Ciebie z teorią... Tutaj masz artykuł do poczytania o procedurach i funkcjach;

Deklarować argumenty możesz na cztery sposoby, Ciebie powinny jak na razie interesować trzy podstawowe:

  • ze słowem const - parametr jest rozpoznawany jako stała i nie można jej modyfikować
  • ze słowem var - jako zmienna, modyfikacji dokonuje się na zmiennej na zewnątrz procedury / funkcji jak i wewnątrz
  • bez żadnego słowa - jako zmienna, ale modyfikacji dokonuje się tylko wewnątrz procedury / funkcji
    Teraz zastanów się, dlaczego macierz X ma słowo var, a pozostałe nie mają, a także czy wszystkie poza var X: Mac mogły by być zadeklarowane ze słowem const;
0
Furious Programming napisał(a)

Poprawny i koniec jest ten algorytm:

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
begin
  for I := 1 to M do
    for J := 1 to L do
      begin
        X[I, J] := 0;

        for K := 1 to L1 do
          X[I, J] := X[I, J] + Y[I, K] * Z[K, J];
      end;
end;

i koniec :P

Tak jest!

Na koniec śmiem jeszcze twierdzić, że taki oto kod będzie cokolwiek szybszy.

procedure Mno_Mac(var X: Mac; Y, Z: Mac; M, L, L1: Integer);
var
  I, J, K: Integer;
  suma:Real;
begin
  for I := 1 to M do
    for J := 1 to L do
      begin
        suma := 0;
        for K := 1 to L1 do
          suma := suma + Y[I, K] * Z[K, J];
        X[I, J] := suma;
      end;
end;

1 użytkowników online, w tym zalogowanych: 0, gości: 1