LUP_decomposition.pl 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. #!/usr/bin/perl
  2. # Simple implementation of the LU decomposition.
  3. # See also:
  4. # https://en.wikipedia.org/wiki/LU_decomposition
  5. use 5.014;
  6. use warnings;
  7. use Math::AnyNum qw(:overload);
  8. # Code translated from Wikipedia (+ minor tweaks):
  9. # https://en.wikipedia.org/wiki/LU_decomposition#C_code_examples
  10. sub _LUP_decompose {
  11. my ($matrix) = @_;
  12. my @A = map { [@$_] } @$matrix;
  13. my $N = $#A;
  14. my @P = (0 .. $N + 1);
  15. foreach my $i (0 .. $N) {
  16. my $maxA = 0;
  17. my $imax = $i;
  18. foreach my $k ($i .. $N) {
  19. my $absA = abs($A[$k][$i] // return ($N, \@A, \@P));
  20. if ($absA > $maxA) {
  21. $maxA = $absA;
  22. $imax = $k;
  23. }
  24. }
  25. if ($imax != $i) {
  26. @P[$i, $imax] = @P[$imax, $i];
  27. @A[$i, $imax] = @A[$imax, $i];
  28. ++$P[$N + 1];
  29. }
  30. foreach my $j ($i + 1 .. $N) {
  31. if ($A[$i][$i] == 0) {
  32. return ($N, \@A, \@P);
  33. }
  34. $A[$j][$i] /= $A[$i][$i];
  35. foreach my $k ($i + 1 .. $N) {
  36. $A[$j][$k] -= $A[$j][$i] * $A[$i][$k];
  37. }
  38. }
  39. }
  40. return ($N, \@A, \@P);
  41. }
  42. sub solve {
  43. my ($matrix, $vector) = @_;
  44. my ($N, $A, $P) = _LUP_decompose($matrix);
  45. my @x = map { $vector->[$P->[$_]] } 0 .. $N;
  46. foreach my $i (1 .. $N) {
  47. foreach my $k (0 .. $i - 1) {
  48. $x[$i] -= $A->[$i][$k] * $x[$k];
  49. }
  50. }
  51. for (my $i = $N ; $i >= 0 ; --$i) {
  52. foreach my $k ($i + 1 .. $N) {
  53. $x[$i] -= $A->[$i][$k] * $x[$k];
  54. }
  55. $x[$i] /= $A->[$i][$i];
  56. }
  57. return \@x;
  58. }
  59. sub invert {
  60. my ($matrix) = @_;
  61. my ($N, $A, $P) = _LUP_decompose($matrix);
  62. my @I;
  63. foreach my $j (0 .. $N) {
  64. foreach my $i (0 .. $N) {
  65. $I[$i][$j] = ($P->[$i] == $j) ? 1 : 0;
  66. foreach my $k (0 .. $i - 1) {
  67. $I[$i][$j] -= $A->[$i][$k] * $I[$k][$j];
  68. }
  69. }
  70. for (my $i = $N ; $i >= 0 ; --$i) {
  71. foreach my $k ($i + 1 .. $N) {
  72. $I[$i][$j] -= $A->[$i][$k] * $I[$k][$j];
  73. }
  74. $I[$i][$j] /= $A->[$i][$i] // return [[]];
  75. }
  76. }
  77. return \@I;
  78. }
  79. sub determinant {
  80. my ($matrix) = @_;
  81. my ($N, $A, $P) = _LUP_decompose($matrix);
  82. my $det = $A->[0][0] // return 1;
  83. foreach my $i (1 .. $N) {
  84. $det *= $A->[$i][$i];
  85. }
  86. if (($P->[$N + 1] - $N) % 2 == 0) {
  87. $det *= -1;
  88. }
  89. return $det;
  90. }
  91. #
  92. ## Examples
  93. #
  94. # Defining a matrix
  95. my $A = [
  96. [2, -1, 5, 1],
  97. [3, 2, 2, -6],
  98. [1, 3, 3, -1],
  99. [5, -2, -3, 3],
  100. ];
  101. # Determinant of a matrix
  102. say "det(A) = ", determinant($A);
  103. # Solve a system of linear equations
  104. my $v = [-3, -32, -47, 49];
  105. say '(', join(', ', @{solve($A, $v)}), ')';
  106. # Invert a matrix
  107. my $inv = invert($A);
  108. say join(",\n", map { '[' . join(', ', map { sprintf('%8s', $_) } @$_) . ']' } @$inv);
  109. __END__
  110. det(A) = 684
  111. (2, -12, -4, 1)
  112. [ 4/171, 11/171, 10/171, 8/57],
  113. [ -55/342, -23/342, 119/342, 2/57],
  114. [ 107/684, -5/684, 11/684, -7/114],
  115. [ 7/684, -109/684, 103/684, 7/114]