수치 해석(numerical analysis)분야에서 독보적인 위치를 차지하고 있는 툴은 단연 MATLAB이다. 수치 해석에 특화된 독자적인 스크립트 언어를 가지고 있고 (문법 구조가 robust하지 않다는 평이 있긴 하지만) 다양한 분야에 적용가능한 toolbox와 폭넓은 이용자층, 개발사의 적극적 지원 등 장점이 많은 툴이다. java로 개발되어서 실행하려면 jvm을 먼저 설치해야 한다. (물론 jvm의 성능이 비약적으로 향상되었긴 하지만 속도면에서 불리한 면이 있지 않을까하는 짐작이 든다.)



 단점은 역시 꽤 고가의 툴이라는 점이며 이 점때문에 학부생들이나 가볍게 배워보려는 이들에게는 실습 도구로 사용하기에 적합하지 않다. 개발사인 mathwork사에서 라이센스를 꽤 엄격하게 관리하고 있기도 하다. (라즈베리파이에는 무료로 제공된다는 얘기도 있는데 아직 확인을 못 해 봤다. 그리고 그 사양 위에서 matlab이 원활하게 실행될 것 같지도 않다.) 국내에서 출판된 공학 수학 교재들 중에 MATLAB을 보조적으로 사용하는 것들이 있는데 이런 점에서 적절치 않다고 생각한다.


 다른 대안이 없는 것은 아니다.일단 Octave라는 걸출한 오픈 소스 프로그램이 있는데 Matlab과의 문법 호환성이 거의 100%라서 기본적인 코드는 거의 변환 없이 실행이 가능할 정도이므로 학부생들의 기초 교육은 이것을 사용하면 된다. 그동안 gui 환경이 갖춰지지 않아서 불편했는데 (베타 버전이긴 하지만) 최근에는 gui환경도 지원하기 시작해서 더욱 고무적이다.


 그 외에도 scilab이 있는데 scilab은 유럽 등지에서는 꽤 유명하고 널리 쓰이는 수치해석 툴로써 무료로 사용할 수 있는 프리웨어이다. 하지만 개인적으로는 matlab과는 많이 동떨어진 문법을 가지고 있고 익히기도 꽤 어려웠다는  경험이 있다. 그리고 freemat 이라는 것도 있는데 matlab과 100%호환되는 툴을 지향하고 개발되기 시작했으나 2013년 이후로 개발이 중지된 것 같다. 최근에는 python+numpy 라는 (역시 오픈소스) 떠오르는 방법도 있다. (필자는 이것을 가장 추천하는 편이다.)


 모든 개발 환경이 클라우드로 옮겨가고 있는 추세이다. matlab은 아니지만 가장 간편하게 (무료로) matlab 코드를 실행시킬 수 있는 방법은 온라인 옥타브이다. 접속하면 바로 코딩이 가능하고 그 결과를 즉시로 확인해 볼 수 있다.



위 그림에서와 같이 plot()명령의 결과도 바로 확인할 수 있다. 단, scrip파일을 작성하려면 로그인을 해야 한다.


mathclouds 라는 사이트도 있다. 여기에서도 matlab 문법을 지원한다.


matlab 실습을 가장 간편하게 할 수 있는 방법은 이러한 서비스들을 사용하는 것이다.



Posted by 살레시오
,

 MATLAB에서 취급되는 모든 데이터형은 기본적으로 행렬(matrix)이다. 스칼라(scalar)값은 1☓1차원의 행렬이고 벡터는 n☓1차원의 행렬이다. 일반적으로 수학 교재에서 그냥 벡터라 하면 열벡터(column vector)를 의미하는 경우가 많다. 이 책에서도 앞으로 그냥 벡터라고 하면 열벡터를 의미하는 것으로 한다. 심지어 문자열도 그 문자 각각의 아스키(ASCII)코드값의 행렬(벡터)로 표시하며 다항식은 계수들의 행렬(벡터)로 표시한다. 함수에 넘겨주는 입력 파라메터도 행렬이고 함수의 수행 결과 생성되는 출력값도 역시 행렬이다. 앞으로 다음과 같이 구분하도록 하겠다. (m,n은 양의 정수)

       ① 스칼라 : 1x1차원의 행렬

       ② 벡터 : nx1차원의 행렬 (행벡터 : 1xn 차원 행렬)

       ③ 행렬 : mxn 차원의 행렬

 이제 행렬을 저장하는 변수를 만드는 기본적인 방법을 설명한다. MATLAB에 행렬을 입력하는 방법의 하나는 원소들을 대괄호 안에 입력하는 것으로 각 원소들은 공란 또는 콤마로 분리되어야 한다. 예를 들어 다음과 같은 행렬은

아래와 같이 한 줄에 입력할 수 있다.


>> A = [1.2 10 15; 3 5.5 2; 4 6.8 7] 󰎠
A =
1.2000 10.0000 15.0000
3.0000 5.5000 2.0000
4.0000 6.8000 7.0000


위에서 보듯 행렬의 원소들을 대괄호 안에 입력해야한다. 모든 행의 원소들은 공란 또는 ‘,’로 분리되어야 하며, 마지막 행을 제외한 각 행의 끝은 세미콜론으로 구분한다.


 크기가 큰 행렬은 몇 개의 입력 줄로 펼쳐서 나타낼 수 있다. 예를 들어 다음과 같은 행렬 B를 생각해보자.

이 행렬은 다음과 같은 네 줄로 나눠서 입력할 수 있다.

>> B = [1.5630 2.4572 3.1113 4.1051;
3.2311 1.0000 2.5000 3.2601;
1.0000 2.0000 0.6667 0.0555;
0.2345 0.9090 1.0000 0.3333];


여기서 ‘]’뒤에 세미콜론 ‘;’를 사용하였는데 끝에 세미콜론을 할 경우 처리 결과를 출력하지 않는다. 또한 각행의 마지막에 세미콜론 ‘;’을 사용하였는데, 이것을 생략하여도 같은 행렬로 입력된다.

※ 다른 언어(C/C++/JAVA/C# 등등)와 달리 MATLAB에서는 하나의 명령어 끝에 반드시 세미콜론 ‘;’을 붙여야 하는 것은 아니다. MATLAB의 명령 줄의 끝에 세미콜론 ‘;’ 을 붙이면 명령을 수행한 결과 값을 화면에 표시하지 않으며 세미콜론을 붙이지 않으면 명령을 수행한 결과 값을 화면에 표시한다. 명령어들 간에 구분을 하기 위해서는 콤마‘,’를 사용하며 세미콜론으로도 명령어들을 구분하나 바로 앞의 명령어의 출력을 억제한다. 다음 예를 보자.

>> a=1, b=2; c=3 󰎠
a =
1
c =
3


 이미 만들어진 행렬을 이용하여 새로운 행렬을 만들 수도 있다.


>> A1=[A; 11 22 33]
A1 =
   1.2000   10.0000   15.0000
   3.0000    5.5000    2.0000
   4.0000    6.8000    7.0000
  11.0000   22.0000   33.0000


이 예에서는 이미 만들어진 행렬 A를 이용하여 A행 밑에 한 행을 추가하여 새로운 행렬 A1을 생성하였다.


[그림 1]  행렬 A1의 구조


새로운 열을 A행렬 오른편에 추가하고 싶다면 다음과 같이 하면 된다.


>> B=[0.5; 2; 100] 󰎠
B =
0.5000
2.0000
100.0000
>> A2=[A B] 󰎠
A2 =
1.2000 10.0000 15.0000 0.5000
3.0000 5.5000 2.0000 2.0000
4.0000 6.8000 7.0000 100.0000


또는 다음과 같이 한 번에 같은 일을 수행할 수도 있다.


>> A2 = [A [0.5; 2; 100] ]
A2 =
1.2000 10.0000 15.0000 0.5000
3.0000 5.5000 2.0000 2.0000
4.0000 6.8000 7.0000 100.0000


 다른 예로 A행렬을 두 번 이용하여 다음과 같이 새로운 AA라는 행렬을 만들 수도 있다.

>> AA=[A A]
AA =
   1.2000   10.0000   15.0000    1.2000   10.0000   15.0000
   3.0000    5.5000    2.0000    3.0000    5.5000    2.0000
   4.0000    6.8000    7.0000    4.0000    6.8000    7.0000

 

위에서 행렬 A를 옆으로 나란히 붙어서 새로운 행렬 AA를 만들었다. 또는


>> AA2=[A; A]
AA2 =
   1.2000   10.0000   15.0000
   3.0000    5.5000    2.0000
   4.0000    6.8000    7.0000
   1.2000   10.0000   15.0000
   3.0000    5.5000    2.0000
   4.0000    6.800      7.0000


위에서는 행렬 A를 밑으로 나란히 붙어서 새로운 행렬 AA2를 만들었다. 위와 같이 기존에 생성된 행렬들을 합성하여 새로운 행렬을 만들 때는 그 행렬들의 차원에 주의해야 한다. 다음의 예를 보자.


>> A=[1 2; 3 4] 󰎠
A =
1 2
3 4
>> B=[5 6 7;8 9 10; 10 11 12] 󰎠
B =
5 6 7
8 9 10
10 11 12
>> AB=[A B] 󰎠
오류 내용 : 행렬에서 dimension이나 type이 일치하지 않습니다.
>> AB_=[A; B] 󰎠
오류 내용 : 행렬에서 dimension이나 type이 일치하지 않습니다.

 

두 행렬을 옆으로 연결할 때는 행수가 같아야 하고 밑으로 붙일 때는 열수가 같아야 한다. 그렇지 않으면 위와 같이 오류가 발생한다.



Posted by 살레시오
,

 프로그램을 작성하다 보면 빈행렬(empty matrix)을 생성할 필요가 가끔씩 생긴다. 즉, 요소가  없는 0x0크기의 빈행렬를 생성하려면 다음과 같이 하면 된다.


>> A = []

 

만약 행렬 A가 이전에 어떤 요소들을 가지고 있었다면 빈행렬을 대입하는 순간 그것들은 지워지고  그 요소를 저장하기 위해 할당되었던 메모리는 반환된다.

특수한 행렬을 생성하는 기본적인 함수들은 다음 표와 같다.


[표 1] 특수 행렬을 생성하는 함수들

eye(), eye(r, c), eye(M)

주대각 요소가 1인 행렬을 생성한다.

zeros(), zeros(r, c), zeros(M)

모든 요소가 0인 행렬 생성

ones(), ones(r, c), ones(M)

모든 요소가 1인 행렬 생성

linspace(c1, c2 [, n])

c1부터 c2까지 n개의 등간격 행벡터 생성. (n이 생략되면 기본적으로 100임) c1, c2 : 복소수 가능

testmatrix(‘magi’, n)

testmatrix(‘frk’, n)

testmatrix(‘hilb’, n)

nxn 매직행렬 생성

nxn Franck행렬 생성

nxn Hilbert행렬의 역행렬 생성

rand(“seed”), rand(“seed”, s)

rand(r, c [ , strKey] )

rand(M [ , strKey] )

seed값을 얻거나 설정한다. (초기에 s=0임)

r x c 크기 혹은 M행렬과 같은 크기의 난수 행렬 생성

strKey : “uniform”, “normal”, “info”

grand

더 다양한 옵션으로 난수행렬 생성

eye(M)은 M행렬과 같은 차수이고 주대각 요소가 1인 행렬을 생성하며 행렬M이 꼭 정방행렬일 필요는 없다. 주의할 점은 eye(10)이라고 입력하면 10x10 행렬이 생성되는 것이 아니라 1x1행렬이 생성된다는 것이다. (10이라는 숫자가 1x1행렬이기 때문) 그리고 덧셈과 뺄셈에서 사용될 때는 더해지는 행렬과 자동으로 같은 차수의 행렬을 생성한다. ones()와 zeros()도 같은 특성을 가진다.

 linspace(c1, c2 [,n])함수는 c1부터 c2까지 등간격으로 n개의 요소를 가지를 행벡터를 생성하는 자주 사용되는 함수이다. 예를 들어서 0부터 2π 까지 100개의 요소를 가지는 행벡터를 생성헤서 vX변수에 대입하려면 다음과 같이 하면 된다.

>> vX =  linspace(0, 2*%pi)

1000개로 개수를 늘리려면 다음과 같이 한다.


>> vX =  linspace(0, 2*%pi, 1000)

 

 한 가지 특이한 것은 c1, c2는 복소수도 가능하다는 것이다. 예를 들어서 c1=1+%i, c2=2+3*%i, n=5라면 간격을 나눌 때 실부부는 실수부끼리, 허수부는 허수부끼리 나눈다. 즉 다음 두 명령은 같은 것이다.

>> linspace(1+%i, 2+3*%i, 5)
>> linspace(1, 2, 5) + linspace(1, 3, 5)*%i


후자 보다는 전자가 더 간단하므로 이것을 사용하면 된다

.

 함수 rand()는 난수행렬을 생성시킨다. 이때 균일분포/정규분포를 strKey로 지정할 수 있다.


위 예제에서 histplot()은 히스토그램을 그리는 함수이다.



Posted by 살레시오
,

(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 살레시오
,

 생성된 행렬의 개별 요소를 접근하려면 행렬에 괄호 안에 행과 열의 번호를 콤마(,)로 구분하여 써주면 되는데 예를 들어 A행렬의 r행 c열 요소는 다음과 같이 접근한다.


 A(r, c)

 

여기서 r과 c를 인덱스(index)라고 한다. 사용 예를 들면 다음과 같다.

>> A = [ 1 2 3; 4 5 6]
>> b = A(1, 2) // 2가 변수 b에 저장된다.
>> A(2,3) = 3*%i // 2행 3열의 요소가 6에서 3i 로 변경된다.

만약  B가 벡터라면 다음과 같이 두 가지 방법으로 인덱싱을 할 수 있다.


>> B = [2 3 5 7 11 13 17 19]
>> c = B(1, 5)  // 1행 5열 요소이므로 11이 변수 c에 저장된다.
>> d = B(5) // 위와 같다.
>> E = (1:100)’
>> f = E(5,1) // 5행 1열의 요소이므로 5가 변수 f에 저장된다.
>> g = E(5) // 위와 같다.

 

이 예제와 같이 벡터의 인덱싱은 하나만 지정할 수 있다.


 사실 행렬은 내부적으로 1차원 배열로 관리되며 따라서 인덱스를 하나만 지정할 수는 있다. 예를 들어 다음과 같이 H행렬이 있다고 가정할 때


>> H = [ 10, 20, 30; -40, -50, -60,]

 

H(1)은 H(1,1)과 같이 10이고 H(2)는 H(2,1)의 요소인 -40이다. H(3)은 H(2,1)과 같다.

H(1) == H(1,1)

H(3) == H(1,2)

H(5) == H(1,3)

H(2) == H(2,1)

H(4) == H(2,2)

H(6) == H(2,3)

[ 그림 1] H행렬의 인덱싱

하지만 행렬의 경우 이런 방식으로 인덱싱하는 것을 추천하지 않으며 사용 빈도도 낮다.

 하나의 행 또는 하나의 열 전체를 선택하고자 할 때는 콜론(:)을 인덱스로 사용한다. 예를 들어서 H행렬의 1행 전체와 2열 전체를 선택해서 J1과 J2에 대입하고자 하면 다음과 같이 한다.


>> J1=H(1,:) // 1행, 전체 (즉 그냥 1행)
J1  =
    10.    20.    30.  
>> J2=H(:,2) // 전체. 2열 (즉 그냥 2열)
J2  =
    20.  
  - 50.  

이와 같이 콜론(:)이 인덱싱에서 단독으로 쓰이면 전체(all)의 의미를 갖는다. 혼동의 여지가 있는 것은 이전 포스트에서 설명한 콜론(:)연산자의 경우이다. 예를 들어서 2x100 행렬 K를 다음과 같이 생성했다고 가정해 보자.

>> K = [1:100; 100:-1:1]

이 행렬의 1열부터 10열까지 동시에 선택해서 변수 L에대입하려면 다음과 같이 하면 된다.


>> L = K(:, 1:10)

 

두 번째 열인덱스에 1:10 이라고 콜론 연산자를 사용했다. 따라서 이 명령은 다음과 완전히 동일하다.

>> L = K(:, [1 2 3 4 5 6 7 8 0 10])


이렇게 하는 것 보다는 전자가 훨씬 간단하므로 전자를 사용할 것이다. 하지만 콜론 연산자를 사용하는 것은 연속되거나 등간격의  행이나 열을 선택하는 경우에만 사용할 수 있다. 예를 들어서 2열, 4열, 6열… 20열을 동시에 취하려면 다음과 같이 하면 된다..

>> M = K(:, 2:2:20)


하지만 등간격이 아닌 경우에는 행벡터로 각각을 직접 지정해 주어야 한다. 예를 들어서 1열, 2열, 3열 5열 7열 11열 을 동시에 취하려면 다음과 같이 하면 된다.


>> M = K(:, [1 2 3 4 5 11])

등간격이 아니므로 이렇게 직접 지정해 주어야 한다.


 만약 어떤 행렬의 가장 마지막 행(또는 열)을 선택하고 싶다면 $지정자를 이용하면 된다.(MATLAB의 경우에는 end 지정자가 있다.) 예를 들어 K행렬의 가장 마지막 열을 N변수에 대입하고 싶다면다음과  같이 하면 된다.


>>N=K(:,$)
N  =
   100.  
   1.  

 

다른 예로 P라는 100x100는 행렬이 있다고 하고 이 행렬의 마지막 행의 1열부터 50열까지를 선택하고 싶다면

>> P($, 1:50)

라고 지정하면 된다. 마지막행 전체를 선택하려면 P($ , :)라고 하면 될 것이다.

 한 가지 특이한 점은 이 $ 지정자는 특정한 값을 가지지 않는데도 불구하고  마치 특수 상수처럼 사용된다는 점이다. 이 말은 이 지정자가 자유롭게 변수에 저장될 수 있다는 말이며 다음과 같은 명령이 가능하다.


>> x = $
>> y = [ 3 5 7 $]
>> z = 5:$
>> Q = P(:, z)

이 예의 마지막 명령은 P행렬의 5열부터 마지막 열까지를 취해서 Q변수에 대입하는 것이다.




Posted by 살레시오
,

 Scilab의 기본 데이타형은 행렬(matrix)이며 스칼라도 내부적으로는 1x1행렬로 취급된다. 벡터도 마찬가지로 nx1 행렬 (열벡터) 혹은 1xn 행렬 (행벡터) 로 취급된다. 행렬의 요소는 숫자, 불리언, 문자열, 다항식 등 일반적인 Scilab 객체가 될 수 있다. 일 반적으로 그냥 벡터라 하면 열벡터(column vector)를 의미하는 경우가 많으므로 앞으로 그냥 벡터라고 하면 열벡터를 지칭하며 행벡터는 명시적으로 표기하도록 하겠다.


 함수에 넘겨주는 입력 파라메터도 행렬이고 함수의 수행 결과 생성되는 출력값도 역시 행렬이다. 편의상  다음과 같이 구분한다.

❶ 스칼라 : 1x1 차원의 행렬

❷ 벡터 : nx1 차원의 행렬 (행벡터는 1xn 행렬), n ≥ 1 인 정수(스칼라 포함)

❸ 행렬 : mxn 차원의 행렬, m,n ≥ 1 인 정수 (스칼라, 벡터포험)

 행렬을 입력할 때는 대괄호 [ ] 를 사용하며 콤마(,)나 공백을 이용하여 요소 간을 구별한다. 세미콜론(;) 혹은 다음 줄이 행들을 구분한다. (이것은 Matlab 이나 Octave와 동일하다.) 따라서 다음 네 가지가 완전히 동일한 2x3 행렬을 생성한다.

>> A = [1, 2, 3; 4, 5, 6]
>> A = [1 2 3; 4 5 6]
>> A = [1,2,3
4,5,6]
>> A = [1 2 3
4 5 6]

행이 두 개 이상일 때는 반드시 행들의 요소 갯수가 같아야 함에 유의해야 한다. 예를 들어서 다음과 같은 입력은 에러를 발생시킬 것이다. 왜냐하면 1행의 크기는 3인데 2행의 크기는 2이기 때문이다.


>> B = [ 1 2 3; 4 5]

행렬의 요소는 복소수도 가능하고 문자열도 가능하다. 예를 들어 다음과 같다.

복소행렬을 생성할 때 주의할 점은 실수부와 허수부사이에 공백문자가 있으면 안된다는 것이다. 다음의 C행렬과 D행렬은 크기가 다른 행렬이 되버린다.(왜?)


>> C = [2+3*%i, 4]
>> D = [2 +3*%i, 4]

 

문자열을 요소로 하는 행렬도 가능하다. 예를 들면 다음과 같다.

>> E = [“abc” “def”]
>> F = [“hello”, “world”; “My name is”, ‘Salecio’]    

그러나 한 행렬의 요소들이 숫자나 문자열이 섞일 수는 없다. 또한 정수형과 실수형은 데이터형이 다르기 때문에 한 행렬에 섞일 수도 없다. 따라서 다음과 같은 입력은 에러를 발생시킨다.


>> G = [ 1 2; ‘abc’, 4]
>> K = [int8(1) 2; 3 4]

 

첫 번째 G는 숫자와 문자열을 한 행렬에 섞어서 입력했기 때문에 에러를 발생하며 두 번째 K는 정수형과 실수형이 섞여있기 때문에 에러를 발생한다.

 반면에 진리값을 나타내는 %T(혹은 %t) 와 %F( 혹은 %f)는 특수한 상수이지만 내부적으로 (더블형) 실수 1과 0으로 취급된다. 따라서 이들 값을 숫자형과 섞어서 사용할 수 있다.

이미 생성된 행렬을 이용하여 새로운 행렬을 생성할 수도 있다.

>> A = [ 1 2; 3 4]
>> B = [ A; 5 6]
>> C = [ A [5 6; 7 8] ]
>> D = [A A]
>> E = [A; A]

두 행렬을 옆으로 연결할 때에는 행의 수가 같아야하고 위아래로 연결할 때는 열의 수가 같아야 에러가 발생하지 않는다.

콜론(:) 연산자

 행벡터 혹은 열벡터를 입력하거나, 생성시켜야 하는 경우에 각 벡터 요소들의 증가분이나  감소분이 일정한 경우가 있다. 이 경우에는 콜론(:) 연산자를 이용하면 된다. 이 연산자는 사용 빈도가 높으므로 잘 숙지해 두어야 한다. 입력형식은 아래와 같다.


>> 변수 = 초기값:증감값:최종


이 명령은 초기값으로부터 최종값까지 증감값만큼 증감시킨 수열을 요소로 갖는 행벡터를 생성하여 변수에 저장하게 된다. (행벡터를 생성한다는데 주의하자!) 예를 들어, 변수 a에 0부터 100까지 10의 간격으로 행벡터를 만들고자 한다면 다음과 같이 입력하여 실행한다.



만약 증가분이 1이라면 이것은 생략할 수 있다. 만약 0부터 20까지 1씩 증가시킨 값들을 요소로 갖는 행벡터를 만들고 싶다면 다음과 같이 한다.


>> b=1:20

 

10부터 1씩 감소시켜서 0까지의 값을 요소로 갖는 행벡터는 다음과 같이 생성할 수 있다.

>> c = 10:-1:0

콜론 연산자는 사용 빈도가 굉장히 높으므로 잘 숙지하고 있어야 한다.



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 살레시오
,

  행렬 객체에 대해서 파이썬 인덱싱과 슬라이싱도 사용할 수 있다. 주의할 점은 수학의 행렬은 1행, 1열부터 시작하지만 Matrix객체는 인덱스가 0으로 부터 시작한다는 점이다. 따라서 첫 번째 행(열)의 인덱스는 0이고 두 번째 행(열)의 인덱스는 1이다.


>>> M = Matrix(2, 3, [1,2,3,4,5,6])
>>> M
[1 2 3]
[4 5 6]

>>> M[4] # 5
>>> M[1,2] # 6
>>> M[0,0] # 1
>>> M[1,1] # 5

>>> M[0:2, 0:2] # 2x2 부분 행렬
>>> M[1:2, 2]
>>> M[:,2] # 3열 전체


특정한 행이나 열을 뽑아내고 싶다면 row(n), col(n) 멤버 함수를 이용한다.


>>> M.row(0)
>>> M.col(2)
>>> M.row(-1) # 마지막 행을 뽑아낸다.


행렬 M의 복사본을 생성하고 싶다면 다음과 같이 하면 된다.


>>> M2 = M[:,:]
>>> M2[0,0]=100
>>> M


이와 같이 M2를 변경해도 M에는 영향을 미치지 않는다.


>>> M = Matrix(4,4,range(1,17))
⎡1   2   3   4 ⎤
⎢5   6   7   8 ⎥
⎢9   10  11  12⎥
⎣13  14  15  16⎦

>>> M[2,2] = M[0,3] = 0
>>> M
⎡1   2   3   0 ⎤
⎢5   6   7   8 ⎥
⎢9   10  0   12⎥
⎣13  14  15  16⎦

>>> M[2:, 2:] = zeros(2,2)
>>> M
⎡1   2   3  4⎤
⎢5   6   7  8⎥
⎢9   10  0  0⎥
⎣13  14  0  0⎦


행렬의 특정 행이나 열을 삭제하고 싶을 경우는 row_del(), col_del() 멤버함수를 이용하면 된다.


>>> M.row_del(0) # 첫 번째 행을 삭제한다.
>>> M.col_del(1) # 첫 번째 열을 삭제한다.


반대로 특정한 행이나 열을 끼워 넣고 싶다면 row_insert(n), col_insert(n) 을 이용한다. 이 경우 기존 행렬의 요소들을 밀어내고 그 위치에 입력한 행이나 열을 끼워 넣게 된다.


 그리고 A.row_join(B) 는 A행렬의 오른편에 B행렬을 병합시키고 A.col_join(B)는 A행렬의 하단에 B행렬을 병합시킨다.


>>> A=eye(3)
>>> A
⎡1  0  0⎤
⎢0  1  0⎥
⎣0  0  1⎦

>>> B=Matrix(3,2,range(11,17))
>>> B
⎡11  12⎤
⎢13  14⎥
⎣15  16⎦

>>> A.row_join(B)
⎡1  0  0  11  12⎤
⎢0  1  0  13  14⎥
⎣0  0  1  15  16⎦

>>> A.col_join(B.T)
⎡1   0   0 ⎤
⎢0   1   0 ⎥
⎢0   0   1 ⎥
⎢11  13  15⎥
⎣12  14  16⎦


또한 행렬객체의 메쏘드 중에 extract()라는 함수가 있다. 이것을 이용해서 원하는 부분을 뽑아낼 수 있다.


>>> A.extract(rowsList, colsList)


예를 들면 다음과 같다.


>>> M=Matrix(4,3,range(12))
>>> M
⎡0  1   2 ⎤
⎢3  4   5 ⎥
⎢6  7   8 ⎥
⎣9  10  11⎦

>>> M.extract([0,1,3],[0,1])
⎡0  1 ⎤
⎢3  4 ⎥
⎣9  10⎦


이 예를 보면 [0행, 1행, 3행] 과 [0열, 1열]이 겹치는 부부만 뽑아져 나온 것을 알 수 있다. 해당 행과 열에 선을 그어서 선이 겹치는 부분을 확인해 보면 쉽게 이해할 수 있을 것이다.



Posted by 살레시오
,

 심파이(sympy)의 행렬 객체는  Matrix이다. 이 Matrix 객체를 생성할 때는 리스트를 넘겨주면 된다. 넘겨준 리스트의 각 요소는 행렬의 행이 된다. 기본적인 사용 예는 다음과 같다.


>>> Matrix([[1,0],[0,1]]) #[1,0]이 1행, [0,1]이 2행이 된다.
>>> Matrix([[11, 22, 33]]) # 리스트의 첫 번째 요소인 [11,22,33]이 1행이 된다.
>>> Matrix([11, 22, 33]) # 11이 1행, 22가 2행, 33이 3행이 된다.


다른 방법으로 행의 개수와 열의 개수를 명시적으로 지정해 주는 방법이 있다.


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

>>> B = Matrix(4, 4, range(16))
⎡0   1   2   3 ⎤
⎢4   5   6   7 ⎥
⎢8   9   10  11⎥
⎣12  13  14  15⎦


행 요소와 열 요소의 관계를 파이썬 함수로 작성하여 이것을 사용할 수도 있다.


>>> def f(i,j):
 if i==j : return 1
 else : return 0

>>> Matrix(4,4,f)
⎡1  0  0  0⎤
⎢0  1  0  0⎥
⎢0  0  1  0⎥
⎣0  0  0  1⎦


명시적 함수뿐만 아니라 익명 함수도 사용할 수도 있다.


>>> Matrix(3, 4, lambda i,j:1-(i+j)%2)
⎡1  0  1  0⎤
⎢0  1  0  1⎥
⎣1  0  1  0⎦


특수한 행렬을 생성하는 함수도 있다. eye() 함수는 항등 행렬을 생성하며, zeros() 는 영행렬을 그리고 ones()는 일행렬을 만들어 준다.


>>> eye(4)
>>> zeros(2)
>>> ones(3)
>>> ones(1,3)


대각행렬을 생성하는 diag() 함수도 있다. 스칼라나 행렬을 인수로 주면 그것들로 대각행렬을 생성한다.


>>> diag(1,2,3)
>>> diag(-1, ones(2,2), Matrix([5,7,6]))


스칼라는 1x1 행렬로 간주되며 주어진 행렬들을 대각 위치에 놓는다.


 난수 행렬을 발생시킬 수도 있으며 randMatrix() 함수를 이용한다.


>>> randMatrix(4) # 4x4 행렬 생성
>>> randMatrix(3,4) # 3x4 행렬 생성
>>> randMatrix(5, symmetric = True) # 5x5의 대칭행렬 생성


이 행렬의 요소는 100이하의 양의 정수로 채워진다.


[표 1] 행렬 객체의 생성 용례들

용법

내용

Martix(list)

Matrix([list1, list2,...])

Matrix(r, c, seq)

Matrix(r, c, func)

열벡터 생성

행렬 생성

seq를 이용하여 r x c 행렬 생성

함수를 이용하여 r x c 행렬 생성

eye(n)

n x n 단위행렬

zeros(n)

zeros(r, c)

n x n 영행렬

r x c 영행렬

ones(n)

ones(r, c)

n x n 일행렬

r x c 일행렬

diag(arg1, arg2, ...)

args 요소들의 단위행렬 생성

randMatrix(n)

randMatrix(r, c)

n x n 난수행렬 (대칭행렬은 symmetric=True 옵션을 지정)

r x c 난수행렬


 한 가지 주지할 사항은 Matrix 객체는 mutable 하다는 것이다. 따라서 immutability 가 필요한 곳(예를 들어 딕셔너리의 키)에는 사용할 수 없다. immutable행렬이 필요하다면 ImmutableMatrix 를 이용하면 된다.




Posted by 살레시오
,