C/C++ Math Library - 5 - More Efficient Matrix Determinants
By Michael Grieco
Summary
Topics Covered
- Old Determinant Creates n! Matrices
- Exclusion Lists Avoid Matrix Copies
- Exclusion Stack Tracks Skipped Columns
- Base Case Finds Last Non-Excluded Column
Full Transcript
all right welcome back everyone um I Pro I said I would continue with the big integer stuff but I I I was just
so deep in uh I I was actually balls deep in looking at uh making the determinant algorithm more efficient so I figured it out somehow and um today
we're going to look at that um and then we'll continue on with IG integers next time I believe um so I guess we'll just hop
right into it um okay so if we look at our uh existing um our existing um algorithm
that we have for the determinant what we'll find is that it's super inefficient in terms of runtime
and memory it actually allocates both actually um because what we have is this Matrix uh let's say we have it's a
4x4 2 three 2 4 3 1 32 3 3 3 4 and then last
row so for each determinant that we're finding we're essentially going to be we splice The Matrix but we're not keeping the same Matrix we are copying it over so we're
creating how many how many matrices well we have a 3X3 the first one would be 2 two
2 3 2 4 and then 32 three or sorry 32 4 two I'll just do
those and then this also generates three more matrices because we have to get the 2x two matrices there so this we get um 3
three and then this will generate even more matrices the 43 and then the
44 so for each Matrix you're getting the how many of so this is this is a 4x4 here and you're going to get four
different matrices four new matrices here and then we have a 3 Y3 and that gives us three more and
then holy [ __ ] goddamn um so we're essentially calling n factorial we're creating n factorial matrices where n is the number of rows or the number of
columns um and how many operations this is taking well it's taking uh oh what is that taking actually that's a wonderful
question um well it does we have uh it's a great question actually um it's just but it's en it's enormous and the amount of memory this takes is is
pretty stupid um so it turns out that there is a better a much better way to do this without splicing matrices and that involves using excl something called exclusion lists it's it's it's
not like a computer science concept per se um but I just call it an exclusion list because
um because um let me just make sure I'm trans yeah um I call it an exclusion list because it it it we're telling the
compiler or the program which columns to ignore so let's we'll kind of walk through this in the way that I kind of piece this together but let's say we
have the same Matrix this determinant of the Matrix m is going to
equal the element at 1 one times the determinant um expanding across row two and we're
going to cancel or cut off Row one um so that's Matrix this element right here 1 one and then we cut
off the first row so we expand on the second row now um and then we cut off the First Column um so let me just move this over here
actually um so this is that's that then we'll have M * 1 2 and this is time the determinant will expand across row two
and cut off row or Cut Off column two then same thing plus M13 times the determinant expanding
across row two and cutting off Row three or column 3 and then minus M14 times the determinant expanding
across row two and uh cutting off column 4 so we're essentially cutting off the column of the element that we're accessing
um now however you can't just it's not this simple because um let's say that it would be hard just to do this
without having a list of excluded columns because if you remember when we spliced matrices if we spliced this first one here um we would get the that the First
Column is gone we can't access it anymore in the copy however in if we're we're just passing in m to all these determinant functions we're still going to have access to that First Column
which may complicate things um so we're going to have to have a list of columns to exclude so rather than taking up n factorial matrices has n factorial
matrices um we're now going to have um with the exclusion list we're going to have one
Matrix plus one list of size n um and the reason why it's going to be size n is because you can only exclude all the columns you can't exclude any more well it's actually going to be n
plus one for us just because of one thing that I'm going to add in the code but doesn't make too much of a difference um so rather than
having we essentially have um n by n + n + 1 as opposed to n n factorial
elements or and I don't even know how many elements that is because it's so many um but regardless it's going to be a lot um so if we follow this path
further we're going to go into determinant uh 21 and we're going to exclude column 1 here and this is going to equal
to expand across row two Cut Off column one M22 times the determinant expand across Row 3 now since we're cutting off the
Matrix even further uh and this time we're we're canceling out row two again then minus M23 times the determinant expand across
the next row row three and cut off the current column which is column 3 and then plus M uh 24 times the
determinant expanding cross uh row row three and cutting off column 4 um so now we are going in further oops
what just happened then determinant of 32 is going to equal to m33 but first let's do the exclusion
columns um so if we were just to take the second column as something to exclude we would not be excluding the first one however however we have to carry that exclusion all the way down
otherwise we're not calculating it properly so we have an exclusion of one and two here um so now uh the determinant here
it's going to be m33 times the determinant of three of 44 excuse
me um minus or 43 because we're cutting off column 3 sorry then minus uh M at
34 times the determinant expanding across row four and cutting off column 4 now let's go one level down again
determinant of 43 is going to equal m at 44 and that's it because that's the last element and this time our exclusion list looks like 1 2 and three because we're
cutting off column 3 and we're left with column 4 and then similarly determinant of column or 44 is going to equal m of
four three because we are excluding 1 two and four this time so the last remaining one is three um you can keep this going for you can do this whole
process out but you'll get the final answer after some point and this exclusion column list will kind of act like a stack in some ways um and we're
going to somewhat treat it like that uh the the differen is in the code will actually have a a fixed size so we'll have n plus one elements and we will just modify how many elements we want to
read from that list all right um yeah um so just one more quick note uh
or just notice how the parameters here uh I kept stating expanding across this row uh the parameter that you pass in here will be the row that you expand ac
across um and then the second parameter will be the next column to skip um from
y um all right so let's get right into the code I guess um let me find this all right we're going to have uh
add a few utility methods first um just for array a or array accessors I'm I'm going to Define them in C
mathematics. um it will return Boolean
mathematics. um it will return Boolean the first one and it's going to be contains uint and we're going to pass in unsigned integer pointer array and
uh an unsigned int number of elements and then the Target and then also just want to print
so void print uint array um pass an unsigned integer pointer array and the unsigned integer number of
elements um so you can just copy these definitions and then in C mathematics. C
create that file if you you don't have it just paste the definitions in and um these will be pretty simple we will go
linearly through the list um since you saw that sometimes it wasn't sorted or you will you will get that it's not sorted all the time uh like you might
have when we specifically when we call determinant 42 you'll have a list that looks like 132 which is not sorted so we cannot assume that this list is sorted
um okay um so for contains uint we're just going to iterate through the elements for unsigned int I is equal to z i is less than number of elements I
++ oops there we go um if array at I is equal to Target return true and then outside of the loop we're going to return false because we have not found
the elements then for print um we'll print it like a vector where we have the square brackets at in the
beginning uh then for the unassigned integer I is equal to zero I is less than number of elements I ++ uh we
will print F uh percent D for an integer and then a space after and then pass in the array at I as the argument and then at the end
just print uh closing square bracket and a new line just like that um and then now that we have actually written source code in the c
mathematics. c we have to add it to the
mathematics. c we have to add it to the Run path um so you can just type in C
mathematics. C uh when we type in all
mathematics. C uh when we type in all the the source file in um source file names in the batch files
nice okay um so the way we're going to implement this in code is going to be um I'm just going to go find the method here um we are going to have a new method and I'm going to call it it will
return float and I'll call it determinant exclusion we're going to pass in four different parameters I believe yeah or yeah four parameters we'll have the
Matrix pointer M uh then we will have an unsigned int row or this is actually going to be five
parameters so the row to expand upon then unsigned in call the new column to skip and then we're going to passing the array and the number of elements so
unsigned in pointer skip calls so this is just the array and then an unsigned int pointer um number of skip calls and the reason why it's a pointer
is because uh we want to modify it directly um like I said this is going to be a stack um and we want to we want to keep
track of it throughout um as we go back and forth so just write it like that and then in the determinant method you can just update the parameter to be
a pointer to M um so once again expand expansion row column to skip then the array to that we of columns that we're going to skip and
then the actual number of skipped columns all right you can copy this uh signature into matrix.
c um and then in um in the current Matrix uh method that we
have um you can just comment everything out because we're not going to use it um all right you know we'll actually just write the the new determinant
method right now so in determinant this will be the first call to determinant exclusion we're going to say if m. rows
does not equal m do calls or m. rows is
equal to Z we're going to return 0.0 float and then apart from that outside of the statement we're going to say return determinant exclusion pass in M
the pointer the row will be zero the column will be zero skip calls will be null and then the number of columns to skip will be also will be zero all right so that's what it should
look like make sure that m is a pointer now on the parameter list for determinant okay now in the actual determinant exclusion
method uh we're going to have a few things here we we will start with the array initialization uh so notice here how we pass in null and if that is the
case then we want to initialize AR the array um we can just do this by saying if not skip calls that means if the pointer is
null um we will initialize the array um so skip calls is equal to the malok of and we're going to say m. calls plus
one and once again the Plus One will be uh for the zero for the initial zero be passing because in the first round we have four columns and we don't want to skip any of them when we're when we're
expanding so we're just going to pass into zero and have that kind of as a ghost column to skip um but we won't actually be skipping a column
um so M do calls plus one and then we will multiply this by the size of unsigned int actually I don't think we
need let's see one two four oh we actually don't need to have that plus one in there because we don't and actually skip every single
column right yeah we don't skip every single column we only skip every column but one and that's that's the base case where we
return the single element okay well yeah so you only need the number of columns um then we also have to initialize the counter so number of skip
calls is equal to malok since it's a pointer we have to allocate memory then that's just a size of unsigned in uh and then we will initialize number of skip columns to zero to do this you
have to dreference number of skip skip calls and then just set it equal to zero uh now we're going to insert the current column into the
list um we do this we will say skip calls and then in square brackets for the index D reference of number of skip calls make sure you D reference it because we want the integer value not
the pointer value um is equal to call that's going to be the the the next element in the array and then pointer of number skip calls so we dreference
number skip calls and then you put the dreference in parenthesis and then just say plus plus um so let me actually diagram why this works this
way um so let's uh is there a way to there we go all right let me zoom out and I'm going to move everything
over okay um so the way we're going to keep track of the exclusion columns is like I said through an array um so if we have
four r four four columns we are going to have four spots here 1 2 3 four and then the
number uh here will it will be one I believe because we'll have the zero and then when we go to 32 oh this will be actually two when we
call it um you know I I need to move this down one more time so we have one when we call the the determinant function the first
time that'll be zero then we'll have a Z and a one then a 0 1 2 when we call it the here there are
three skip columns and then when we get the last element we have four skip columns 0 1 2 3 and
then we have four now now when we go back to determinant 32 and call determinant 44 we're going to have to remove this last element or not remove
it but we're going to have to update it um because we don't want to skip the third column in this last method and all we can all we actually have to do is just decrease the number of skip columns by one because if we just want to read
the first three elements we don't have to we don't really care what the last element is we can just leave it as is because the only thing that we care about is that the first three elements stay the same so when we call
determinant 44 it will be 1 0 1 2 3 at the beginning but then we will replace the three at the end with four because the number of skip calls going into this
method is three um and at index 3 it was three but now we're making it four I'm stumbling over my words but or I wasn't stumbling but I hope that made some
muchat sense this this will actually help more um if we do determinant um now
[Music] 33 this is going to equal so determinant 33 is we're essentially
taking um we're in this Matrix here how do I what is the here let's so we essentially by the time we get back to
to determinant 21 after these calls have finished or back to an array that has 0124 but the number number of skip
columns is two again because we subtracted it and after determinate 32 it's subtracted back to two so now when we call determinate
33 we have now 0o 1 and then three is the third column and then we now three skip columns again and this will expand the same
way um but if now if we go back to calling determinant 22 once again when we call this we have the same array that we finished
determinate 33 or 34 with um but this time we only start with we only care about uh the zero at the beginning that was called that we
started with the initial call so there's one skip column and then now that we call it with 2 two we insert the two into the col into the skip column list
and we have two skip columns now so this is like a stack like I said um but yeah so that that's pretty much how we're going to iterate through this and once again these blank elements are not
really blank the entire time we don't we just don't care what they say um so for example when we call determinant 33 there will be a four in that last spot
but we only care about the first three elements which is denoted by the number of skip columns um which tells us okay we only care about the first three elements
all right back to the code so this is just inserting that that current column into there um and then if this is just going
to be a the initial call so if row is equal to zero just row Plus+ you know actually I'm going to remove that and you know what I will change the call in
determinant in the determinant method to be expand across Row one this time because we'll be expanding across Row one all
right now we'll do the base case um if row is equal to m. rows that
means that we're on the last row so we just want to return single element we're going to have an unsigned int C is equal to m. calls so essentially we're going
to m. calls so essentially we're going to go from from the right side to the left side to to find which of the the
the First Column that is not excluded so unsigned in C is equal to M calls um yep and then so we're going to find
the last column not in the exclusion list so if the D reference of number skip calls does not equal zero there are skip columns to look through and then we
also want to say we we want to avoid an unnecessary check so if skip calls at the D reference of number of skip calls
minus one so the last element or the previous element um is equal does not equal zero then we have have a valid
column to check actually um so while contains uint this is just the function from the C mathematics. we're going to pass in skip
mathematics. we're going to pass in skip calls uh the number Valance will be the de reference of number skip calls and then the target will be C and while that's true we're going to say C minus
minus so we will go from the right to the left um to figure out which is which of the which column is not contained um once again this check here
is just because if we had a 1ex one array uh there would be a zero in the skip column list but we wouldn't want to check that um just because it's it's it's a useless
check it's just another function and call so we can just avoid that by uh just not calling the function and then outside of both if statements um but still inside the base case if statement
we're going to say return uh m. elements
at row minus one because we're working with array indes not right now not Matrix indices and then C minus one all right after the base case we're
going to have some placeholders float return is equal to 0.0 float and then Char co-actor sign is equal to negative 1 this is pretty much
or is equal to one sorry and this is similar to uh oh this is similar to what we had um in the in the previous
implementation and then once again we're going to co-actor expand um so for unsigned int C is equal to 1 C
is less than or equal to m. calls
C++ we're going to skip the excluded columns so if contains uh uint skip
calls the dfference of number of skip calls and then for the Target we want to determine if the current column is excluded then just
continue and then outside of the if statement we're going to have a result uh Place alter float res is equal to 0.0
float uh if M do elements at row minus one at C minus one does not equal 0.0 floats this is
because if we if the element is 0.0 float we don't want to we don't really care we if we multiply it will just get to zero so we're just going to avoid um
calling them recursively uh so do the recursive call if it's a if it's a value not if it's nonzero and just say re is
equal to co-actor sign times m. elements
the element so row minus one C minus one and then that times the determinant exclusion um passing in the matx
this we're going to go to the next row um the current column to skip in the recursive call would be this column then we'll just pass in skip calls and the
number of skip calls because remember we are going to keep track of these um we're going to keep track of them through the recursive calls um and then outside of that if
statement we just uh say R plus equals res and then we will say co-actor sign is equal to negative co-actor sign okay um so now to remove the
current column from the list after we've expanded we want to remove the current column from the list and all we can say there is just the D reference of number
skip calls minus minus and once again make sure that the D reference is in parentheses and then the minus minus is outside of the
parentheses and then um return rent so this this uh decrement here all it does it just tells the compiler that
we don't care about that last or the the that element anymore and we can overwrite it whenever um so yeah that's all that
is now we can go into main. C and we'll actually test this by printing out the the uh exclusion column or the exclusion list uh every time so I will print F
here um percent D percent [Music] D um percent
D yeah and then I'll print out row call and then D reference of number skip calls and then print you to write pass
in skip calls and then the this is not necessary if if you but this is just for demonstration purposes and then the number of skip
calls all right if we go into main.c let's get a nice 5x5 um
main.c let's get a nice 5x5 um determinant um okay there we go
nice okay um so I will create a matrix here mat m is equal to Matrix five rows five columns and then for the
arguments uh three 0 03 0 -3
0 -2 0 0 01 0 03 this is this is going to be an easy
Matrix just because there's so many zeros and then 0 -1 2 0 and 1 so we'll print out the Matrix and then um we will print out the
determinant uh passing in a reference to M all right so CD test run um that is not right
did it not it didn't like something8 is that right hey it worked um but yeah so you see how many how like the number of zeros here just
obliterated the number of calls um so we didn't need many calls here um but you see the initial call will give us just an array with zero in it um and then
we'll have the 01 then the 013 but you see how this is progressing in order um and then it it drops back um let me actually I I want to find a better
example because this isn't really too helpful no same thing um I'll do this
one oh you can't even see it God damn it okay um one two three four
5 4 619
8 12 10 2 7
0 4 6 1 9 8 and then 1 5
2 4 3 now if we run this um that is definitely not right
that seems more right but why was it screwing up give me one sec just going to fill my water okay that looks or you know let me put
this in my calculator just to see what the actual answer is it's 324 looks right second determinant 5x five oops
oops it is zero oh I guess this is God damn it you gave us a [ __ ] ass [ __ ] thing come on
buddy just give me a better array please what exam did I use an ex oh I use a 4x4 up here so that might be better just use a 4x4 cuz I actually
have a 4x4 that works so let me just try this out four and four 1 three God this is
tedious one 4 3 9 55 6
21 1 12 4 2 3 God damn it why
is I'm getting different results this is definitely this definitely has something to do with memory do I have like a leak or something or
um you know let me actually do something here in matrix. C I will initialize the values for the
array um so I'll skip the initializing array to putting that in the determinant function and then I will have a float R
is equal to this and then in determinant exclusion I will pass in skip calls and number of skip calls
all right let me see if this works now I mean the stack seems to be going right son of a [ __ ] oh and then I didn't return
anything then I also can go free skip calls 27 is that I don't think that's right it's not right because I'm just getting
the same [ __ ] errors um [ __ ]
[ __ ] I mean I guess we can just look through the list though um where is this so we call determinant M which is
determinant 1 0 where we have the list is just zero then 0 1 012 01 2 3 0 oh
they're that's not right oh I get I know why now because it's doing something with [ __ ] it's getting five elements but
that's you know maybe I should just have M calls plus one just to be safe but it shouldn't be doing that 27 no that didn't fix
it okay so I guess keep the M calls that that's number should be the same um
but let me debug and see when it's returning an element here I do want to see that so debug um break matrix. C line
1028 God damn Run Okay so we're 43 the 0123 that's right and we're calling it at 43
good um so step yep good okay yeah this isn't right why is why is it going 44 oh that's not right all right I'm going to have a
break point in and I'll say if row is equal to m. rows minus
one quit debug um break matrix.
c1015 run no I don't want 1019 break matrix.
c115 continue what the [ __ ] okay so it's just infinitely adding elements to the [ __ ] list but why what the
heck all right you know let me let me just walk through this we will break at line 19 run all right step
in one zero good all right number of skip calls should be uh just a zero so print skip calls at zero good
okay um step into here we're going 21 which is the next call good oops [ __ ] back into
two1 good so we're skipping the First Column good then we don't want to skip the second
one step in there 32 is the next call good next
next 012 that is is right good skipping columns one and two this time step in so
43 we should just be returning uh the elements step okay so we're going in the if statement that's good then unsigned in
C okay so it does not contain the fourth so we're going to print m. elements
or just print row minus or yeah row print C all right so 44 perfect then step step [ __ ] God
damn step in we're calling determinant 44 now oh no stepped into the function by accident
whoa whoa whoa whoaa why is it skipping what the hell what why is it skipping should not be skipping that this is this doesn't look
right skip calls at number of skip calls is equal to column number of skip calls plus plus God damn it all right I will debug again this
time I'll break at um 1026 um so we're calling it 43 that is
the first good so while it does not contain we have a zero one
two three and then we should stop good return false continue step into the function number
of element is equal to five no that's not right oh okay all right yeah okay I I forgot the crucial thing I said it in my demo and I forgot
to do it but you need to reset or subtract one from the skip columns we do it over here but the thing is we don't do it over when we return down here um
so we're going to say if we're going to skip the or subtract the skip columns after the Y Loop um so what happen what was happening was it
was returning over here properly at the end of the method after doing the expansion and subtracting one but when we returned the single element we didn't
actually decrement the skip columns so now if we run this we should get the proper answer 32 which is
Perfecto and there's no memory [ __ ] um and you can we can verify that this all works
um you have the one Zer the zero 0 1 012 012 3 012 4 and then it pops the four and then
replaces the two with a three or pops the two and then ads a three um keeps going 0 134 blah blah blah blah blah and then we
get to determinant 22 which is over why am I not seeing it 2 three should be here 21 22 here we go let we
get to determinate 22 and we call a z or we pass in 02 okay yeah so that was good wonderful error um all right so yeah that was kind
of the main thing that I wanted to talk about um there was another method but it it would take a while to explain it and we've already been live for just about
an hour and it's not I I'll put it in the code but um it just has to do with uh doing a modified row Echelon form of the uh of the Matrix which isn't too
impressive but um there's a lot of conceptual stuff there but yeah so this is a more efficient recursive method rather than
then cutting out all of these columns and [ __ ] or and making all these copies
um so uh yeah it's pretty much it I hope you guys learned something this was I I actually almost creamed my pants last night when I when I figured this out um
because it was so cool I was so proud of myself um but uh yeah so sounds uh pretty
good um all I got to go do some work so uh I'll see you guys maybe on the weekend I doubt it though because I I think I'm pretty or maybe Saturday might
be busy though um but until the next stream hope you guys enjoy yourselves protect yourselves eat well drink well
um and uh yeah keep yourself safe and uh I'll see you then for
Loading video analysis...