(A) 덧셈과 뺄셈

 두 행렬의 덧셈/뺄셈은 크기(행수와 열수)가 같아야 수행되며 다르면 에러가 발생한다. 한 가지 주의할 것은 <스칼라±행렬> 의 연산인데 수학적으로는 정의되지 않지만 Scilab 에서는  (Matlab 와 마찬가지로) 스칼라를 모든 요소와 각각 더하는 연산을 한다는 것이다.

>> A=[1 2;3 4]; B=[5*%i %pi;%e %e^2]; C=[1 2 3]; d=-2;
>> A+B
ans  =
   1. + 5.i     5.1415927  
   5.7182818    11.389056  
>> B-C
    !--error 9
 Inconsistent subtraction.
>> C+d
ans  =
 - 1.    0.    1.

위 의 예에서 보듯이 행렬 A와 B는 크기가 둘 다 2x2로 같으므로 덧셈/뺄셈이 가능하지만 B와 C는 크기가 다르다. 따라서 B와 C의 덧셈/뺄셈은 허용되지 않는다. 다만 d는 스칼라이므로 이것과 행렬과의 연산은 가능하며 모든 요소에 이 스칼라를 더하거나 빼는 연산을 수행한다.

(B) 곱셈

 행렬의 곱셈에 사용되는  연산자들은 다음 표와 같다.

[표 1]  행렬의 곱셈

A*B

일반적인 곱셈. A의 행과 B의 열의 개수는 같아야 함.

A.*B

요소간 곱셈. A와 B는 같은 크기여야 함.

A.*.B

Kronecker 곱셈.

A^n, A**n

A행렬의 n승. 즉, A*A*A*...*A. A는 정방행렬이어야 함.

A.^B

요소간 거듭제곱. A와 B는 같은 크기여야 함.

단순한 A*B 연산은 행렬의 곱셈으로서 피연산자의 한 쪽이 스칼라 이거나 아니면 A의 행 수와 B의 열의 개수는 같아야 계산이 성립한다.

>> A=[1,2+3*%i;%pi %e^2]
A  =
   1.           2. + 3.i  
   3.1415927    7.3890561

>> %i*A
ans  =
    i           - 3. + 2.i    
  3.1415927i    7.3890561i  

>> A*[1 2 3;4 5 6]
ans  =
   9. + 12.i    12. + 15.i    15. + 18.i  
   32.697817    43.228466     53.759115

>> [1 2 3]*A
         !--error 10
Inconsistent multiplication.

위의 예들에서 마지막 것은 행렬의 차수가 맞지 않아서 에러가 발생하는 것이다.


 이에 반해서 A.*B 연산은 행렬의 같은 위치에 있는 요소끼리 곱하는 연산으로서 A행렬과 B행렬의 크기가 같아야 한다.


>> P=[1 2 ; 3 4]
P  =
    1.    2.  
    3.    4.  

>> Q= [%i %pi; %e %T]
Q  =
    i            3.1415927  
    2.7182818    1.        

>> P.*Q
ans  =
    i            6.2831853  
    8.1548455    4.        

이 연산의 결과도 같은 크기의 행렬이다. 만약 크기가 서로 다른 행렬에 대해서 이 연산을 수행하려고 한다면 에러를 발생할 것이다.


>> [1 2].*[3;4]
         !--error 9999
inconsistent element-wise operation

 

그리고 A.*.B 연산은 Kronecker 곱셈으로서 수학기호로는 ⊗로 표시 한다. 즉 A⊗B는 다음과 같은 연산을 수행한다.



따라서 행렬 A와 B에 대한 크기 제약 조건은 없으며 예를 들면 다음과 같다.


>> [1 2].*.[10 20;30 40]
ans  =
   10.    20.    20.    40.  
   30.    40.    60.    80.

 

 행렬의 거듭제곱에는 ^, **, .^ 연산자가 있다. A^n, 또는 A**n 은 행렬의 거듭제곱 연산자로서 예를 들어서 A^3 또는 A**3은 A*A*A 와 결과가 같다. 따라서 이 연산이 수행되려면 A행렬은 반드시 정방행렬이어야 한다. 이에 반해서 A.^n 은 행렬의 각각의 요소를 n승 하는 것이다. 따라서 A행렬의 크기에 대한 제한 조건은 없다.

>> A=[2,3;4,5];
>> A^3
ans  =
    116.    153.  
    204.    269.  
>>A.^3
ans  =
    8.     27.  
   64.    125.  

행렬의 거듭 제곱과 요소간 거듭 제곱은 반드시 구분해야 한다. 한 가지 특이한 (혼동하기 쉬운)점은 만약 A가 벡터라면 A^n 이나 A.^n 이나 결과가 같다는 것이다. (개인적인 생각으로는 A가 벡터일 때 A^n 연산은 에러를 뱉는 것이 더 일관성이 있는 것 아닌가 하는 아쉬움은 있는데 어쨌든 Scilab은 그렇게 동작한다.(

(C) 나눗셈

 행렬의 나눗셈에 대해서 다음 표에 정리하였다. 나눗셈은 보통의 나눗셈과 좌나눗셈으로 나뉜다는 점이 특이하다.

[표 2]  행렬의 나눗셈

A/B

행렬의 나눗셈. A*inv(B)와 같다.

한 쪽이 스칼라이거나 크기가 같아야 한다.

A./B

요소간 나눗셈. A와 B는 같은 크기여야 한다..

A\B

행렬의 좌나눗셈 inv(A)*B와 같다.

한 쪽이 스칼라이거나 크기가 같아야 한다.

A.\B

요소간 좌나눗셈. A와 B는 같은 크기여야 한다.

이 경우에도 요소간 연산자가 따로 정의되어 있으며 dot(.)으로 시작한다. 또한 행렬의 나눗셈의 경우는 역행렬과 관계되어 있으며 A/B는 A*inv(B)와, A\B는 inv(A)*B와 동일한 연산을 수행한다. inv()함수는 정방행렬의 역행렬을 구하는 함수이다.

(D) 기타 연산자

  행렬의 복소전치행렬은 작은 따옴표(‘)를 행렬의 끝에 붙여주면 구할 수 있고 단순 전치 행렬은 점 작은 따옴표 (.’)를 붙이면 된다. 복소전치행렬은 행과 열의 위치를 바꾸고 켤레복소수로 변환된 행렬을 의미하며 단순 전치 행렬은 그냥 위치만 바꾼 행렬은 의미한다.

>> A=[1, 2*%i; 3+4*%i, 5]
 A  =
    1.          2.i  
    3. + 4.i    5.  

>> B=A'
B  =
    1.     3. - 4.i  
 - 2.i    5.        

>> C=A.'
C  =
   1.     3. + 4.i  
   2.i    5.      

 

이 결과를 잘 살펴보면 B행렬은 A행렬의 복소 전치 행렬이고 C행렬은 단순 전치 행렬이라는 것을 알 수 있다.


 정방행렬의 역행렬을 구하는 함수는 inv()이고 행렬식(determinant)을 구하는 함수는 det()이다. 직전의 A행렬에 대해서 역행렬과 행렬식을 구하는 예는 다음과 같다.


>> inv(A)
 ans  =
    0.3170732 + 0.1463415i    0.0585366 - 0.1268293i  
   - 0.0731707 - 0.3414634i    0.0634146 + 0.0292683i  

>> det(A)
   ans  =
      13. - 6.i  



Posted by 살레시오
,

 심파이에서 행렬객체에 수행할 수 있는 선형대수 관련 연산을 다음 표에 정리하엿다.


[표 1] 선형대수 연산

연산

기능

A.T

A.H

A.D

전치행렬(transposition)

복소전치행렬(hermite conjugation)

Dirac transposition

A.rank()

행렬의 랭크(rank)

A.det()

행렬식 (determinant)

A.inv()

역행렬 (inverse matrix)

A.LUsolve(b)

행렬방정식 Ax=b 를 푼다.

A.norm()

norm을 구한다.

A.eigenvals(**flags)

A.eigenvects(**flag)

행렬의 고유값을 구한다.

행렬의 고유값과 고유벡터를 구한다.

A.evalf()

행렬 각 요소의 실수 근사값을 구한다.

A.applyfunc(f)

행렬 각 요소에 함수 f를 적용한다.


숫자로만 이루어진 행렬뿐만 아니라 대수 기호가 포함된 행렬에 대한 연산도 수행 가능하다.


>>> x=symbols('x') # x 를 기호로 설정
>>> C=Matrix([[x,2],[1,x]])
>>> D=ones(2)*x

>>> C
[x  2]
[1  x]

>>> D
[x  x]
[x  x]

>>> C.det()
2    
x  - 2

>>> C*D
[  2             2   ]
[x  + 2*x  x  + 2*x  ]
[   2           2    ]
[ x  + x    x  + x   ]


만약 대수 기호 대신에 숫자를 입력하고 싶다면 subs() 메쏘드를 이용한다.


>>> C.subs(x,11)
[11  2 ]
[1   11]

>>> y=symbols('y')

>>> D.subs(x,y**2+1)
[ 2       2    ]
[y  + 1  y  + 1]
[ 2       2    ]
[y  + 1  y  + 1]

>>> D.subs(x,sqrt(y))
[  ___    ___     ]
[\/ y     \/ y    ]
[  ___    ___     ]
[\/ y     \/ y    ]




Posted by 살레시오
,

 행렬 간 산술 연산은 +, -, *, ** 연산자로 수행할 수 있다. *은 행렬 간 곱셈을, **은 거듭제곱을 수행한다.


[표 1] 행렬의 기본 연산

연산

기능

A+B, A-B

행렬간 덧셈, 뺄셈

A*B

행렬간 곱셈 (A의 열 수와 B의 행 수가 같아야 한다.0

A**k

행렬 A의 k 거듭 제곱

v1.dot(v2)

벡터 v1과 v2의 내적(dot product)


덧셈과 곱셈은 두 행렬의 크기가 같아야 하고 행렬 간 곱셈은 차수 조건에 맞아야 한다. 즉, 첫 번째 행렬의 열 수와 두 번재 행렬의 행 수가 같아야 곱셈이 성립한다.


>>> A=randMatrix(3,4)
>>> B=randMatrix(4,2)

>>> A
[2   48  28  60]
[2   9   18  88]
[79  14  17  25]

>>> B
[10  49]
[47  95]
[18  67]
[60  59]

>>> A*B   
[6380  10074]
[6047  7351 ]
[3254  7815 ]

>>> B*A # 오류발생


 파이썬에서 **가 거듭제곱 연산자이므로 심파이에서도 행렬의 거듭제곱은 ** 연산자로 수행한다. 거듭제곱을 수행하려면 행렬이 정방행렬이어야 한다.


>>>  A=Matrix([[1,2],[2,3]])
⎡1  2⎤
⎣2  3⎦

>>> A**10
⎡514229  832040 ⎤
⎣832040  1346269⎦



Posted by 살레시오
,

6.3 포인터 연산     [doc]     [smts]

 포인터 변수에 대해서 더하기와 빼기 연산이 가능하다. 먼저 다음 예제를 살펴보자.


0602-01.c
#include <stdio.h>
int main()
{
  short sa=10, sb=11, *spa=&sa, *spb=&sb;
  long la=20, lb=21, *lpa=&la, *lpb=&lb;
  printf("%p, %p\n", spa, spb);
  printf("%p, %p\n", lpa, lpb);
}

printf()함수에서 %p 형식지정자는 포인터를 출력할 때 사용되며 주소를 16진수로 표시해준다. 실행 결과는 다음과 같다. (주소는 PC마다 다를 수 있다.)


ffffff12, ffffff10
0028ff0c, 0028ff08

변수 sa와 sb는 인접한 메모리에 저장되는데 주소값의 차이가 2가 난다. short형이 2바이트 자료형이기 때문이다. 마찬가지로 long형은 4바이트 자료형이므로 인접한  la와 lb는 주소가 4가 차이가 난다.


 이와 같이 포인터에 정수를 더하거나 빼기도 하고 포인터끼리 뺄셈을 하는 등 연산 기능을 제공한다. 이러한 연산을 통해 포인터가 가리키는 주소를 변화시키거나 포인터들이 가리키는 주소 간 거리를 계산할 수 있다. 이러한 기능은 특히 배열을 다룰 때 유용하게 사용된다.


포인터에 정수를 더하거나 뺄 수 있는데 이 경우 (주소의) 변량은  다음과 같다.


  • 더하거나 빼는 정수×자료형의 크기


다음 예를 보자.


0602-02.c
#include <stdio.h>
int main()
{
  short sa = 10;
  short *spa = &sa;
  printf("%p\n", spa++);
  printf("%p\n", spa);
  printf("%p\n", spa+2);
}

실행결과는 다음과 같다.(주소값은 PC마다 다를 수 있다.)


0028ff1a
0028ff1c
0028ff20

포인터 spa를 1 증가시켰는데 주소값은 2가 증가되었다. 이는 spa가 short형 포인터이고 short는 2바이트를 차지하는 자료형이기 때문이다. 그리고 spa+2는 주소값이 4가 증가되었는데 (정수)*(바이트수) 만큼 증가되기 때문이다.


증감연산자와 포인터 연산자를 조합한 몇 가지 혼동할 여지가 있는 예를 들어보자.


*(++ipA)  // 포인터값을 먼저 증가시킨 해당 변수의 값을 참조
*(ipA++)  // 해당 변수값을 참조한 다음 포인터값을 증가
++(*ipA)  // *ipA 변수값 1 증가후 그 값 참조
(*ipA)++  // *ipA 값 참조 후 변수값 1 증가

이 예제들의 경우 괄호를 생략하여 가독성을 떨어뜨리는 것은 바람직하지 않다.


Posted by 살레시오
,