기본 자료형과 마찬가지로 구조체 변수도 배열을 형성할 수 있다. 예를 들어 Point 형 변수 세 개를 배열로 생성하고 싶다면 다음과 같이 하면 된다.


Point pa[3];


이렇게 하면 크기가 3인 Point형 배열 변수 pa가 생성된다. 초기화 방법은 배열의 초기화와 동일하다.


Point p0 = {0,0};
Point p1 = {1,1};
Point p2 = {2,2};
Point pa[3] = {p0, p1, p2};


또는 다음과 같이 바로 초기화를 할 수도 있다.


Point pa[3] = {{0,0}, {1,1}, {2,2}};


배열 각 요소를 통하여 구조체의 필드를 접근할 수 있다.


Point pa[3];
pa[0].x = 0;
pa[0].y = 0;
pa[1].x = 1;
pa[1].y = 1;
pa[2].x = 2;
pa[2].y = 2;


동일한 방법으로 초기화 이후에도 각 필드의 값을 자유롭게 접근하여 읽거나 변경할 수 있다.


 배열의 크기가 커지면 반복문을 이용하여 구조체 필드를 초기화하는 것이 일반적이다. 예를 들면 다음과 같다.


ex08-09.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point pta[100];
for (int k=0; k<100; k++) {
pta[k].x = 1;
pta[k].y = 1;
}
}


또는 미리 정의된 Point형 변수를 이용할 수도 있다.


ex08-09.c

#include <stdio.h>

typedef struct {
double x;
double y;
} Point;

int main(int argc, char **argv) {
Point pt = {1,1};
Point pta[100];
     // 100개의 요소를 pt로 초기화시킨다.
for (int k=0; k<100; k++) {
pta[k] = pt;
}
}


이와 같이 구조체도 배열을 생성하여 사용할 수 있다.

Posted by 살레시오
,

 넘파이(numpy)를 사용하기 위해서는 다음과 같이 import 해야 한다. winpython, python(x,y)에는 기본적으로 포함되어 있으므로 바로 사용할 수 있다. import 명령을 이용하면 되는데 다음과 같이 몇 가지 방법이 있다.


>>> import numpy # 넘파이의 모든 객체를 numpy.obj 형식으로 불러 사용할 수 있다.
>>> import numpy as np  # 넘파이의 모든 객체를 np.obj 형식으로 불러 사용할 수 있다.
>>> from numpy import * # 넘파이의 모든 객체를 내장 함수 (객체) 처럼 사용 가능.

 

넘파이는 ndarray (n-dimensional array 의 약어, 이후 배열) 라는 객체를 사용하는데 이는 리스트와 유사하지만 다차원 데이터의 연산에 좀 더 효율적이라고 한다. 리스트는 모든 형식의 객체를 저장할 수 있지만 배열은 한 행의 요소는 모두 같은 자료형어야 한다.

배열 생성

 배열을 입력하는 가장 기본적인 함수는 array() 이다. 첫 번째 인자로 리스트를 받는데 이것으로 array객체를 생성한다.

>>> np.array( [1, 2, 3] ) #1차 배열 (벡터) 생성
>>> np.array( [ [1, 2, 3], [4, 5, 6] ) # 2x3 크기의 2차원 배열 (행렬) 생성

일반적으로 3차원, 4차원, ... , n차원 배열도 중첩리스트를 이용하여 유사하게 생성할 수 잇다.

 파이썬의 내장 함수인 range()와 유사하게 배열 객체를 반환해주는 arange() 함수가 있다.

>>> np.arange(10) # 1차원 배얼 [0,1,2, ... ,9] 생성


그리고 시작점과 끝점을 균일 간격으로 나눈 점들을 생성해주는 linspace()함수도 있다.


>>> c = np.linspace(0, 1, 6)   # start, end, num-points
>>> c
    array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. ])

>>> d = np.linspace(0, 1, 5, endpoint=False)
>>> d
    array([ 0. , 0.2, 0.4, 0.6, 0.8])

생성된 배열은 reshape() 함수를 이용하여 행수와 열수를 조절할 수 있다.

>>> a = np.arange(10) # 1차원 배열 [0,1,2, ... ,9] 생성
>>> a = a.reshape(2,5) # 같은 요소를 가지고 2x5 배열로 변형


특수 행렬 생성

 넘파이에는 특수 행렬을 생성하기 위한 함수들이 있다. zeros(), ones() 라는 함수들은 각각 0과 1로 채원진 배열을 생성하는데 차수를 정수 혹은 튜플로 받는다. 예를 들어서 0으로 채워진 5x5 배열을 만들고 싶다면 다음과 같이 한다.

>>> b1 = np.ones( 10 ) # 1로 채원진 10 크기의 (1차원) 배열 생성
>>> b2 = np.zeros( (5,5) ) # 0으로 채원진 5x5 크기의 배열 생성


대각행렬을 생성하기 위한 eye() 함수와 diag() 함수도 있다. eye() 함수는 항등행렬을 생성하고 diag()는 주어진 정방행렬에서 대각요소만 뽑아내서 벡터를 만들거나 반대로 벡터요소를 대각요소로 하는 정방행렬을 만들어 준다..

  

         정의 : eye(N, M=, k=, dtype=), M은 열 수, k는 대각 위치(대각일 때가 0), dtype은 데이터형


>>> np.eye(2, dtype=int)
    array([[1, 0],
           [0, 1]])

>>> np.eye(3, k=1)
    array([[ 0.,  1.,  0.],
           [ 0.,  0.,  1.],
           [ 0.,  0.,  0.]])


         정의:  diag(v, k=0), v는 array_like, k는 대각 위치


>>> x = np.arange(9).reshape((3,3))
>>> x
    array([[0, 1, 2],
           [3, 4, 5],
           [6, 7, 8]])
>>> np.diag(x)
    array([0, 4, 8])
>>> np.diag(x, k=1)
    array([1, 5])
>>> np.diag(x, k=-1)
    array([3, 7])
>>> np.diag(np.diag(x))
    array([[0, 0, 0],
           [0, 4, 0],
           [0, 0, 8]])

 

이에 반해서 empty() 라는 함수는 크기만 지정해 두고 각각의 요소는 초기화 시키지 않는다. 따라서 이 함수로 생성된 배열의 요소는 가비지값이 채워져 있다.


>>> c = np.empty( (2,2) ) # 가비지 값으로 채원진 2x2 크기의 배열 생성



Posted by 살레시오
,

  numpy에는 배열(ndarray)과 행렬(matrix) 객체가 있는데 이들 중 어느 것을 사용해야 하는지 처음 공부할 때 혼동하기 쉽다. 언뜻 보기에는 사용법에 별 차이가 없어 보이고 행렬 연산에는 무조건 matrix를 사용해야 하는 것 아닌가 짐작했었는데 다음 사이트에 잘 정리가 되어 있어서 여기에 옮겨보도록 하겠다.

              numpy for MATLAB users

1. 결론 요약

  일단 결론부터 말하자면 ndarray 를 사용하는 것이 더 효율적이라고 명시되어 있다. 개인적으로 matrix 객체는 MATLAB 사용자의 편의를 위해서 (억지로) 만들어진 것 같은 느낌이 든다. (pylab 과 같은 모듈도 비슷한 이유로 만들어졌으나 파이썬 커뮤니티 안에서는 사용이 역시 권장되지 않는다.)

 ndarray를 사용해야 하는 이유는 다음과 같이 요약할 수 있다.

  • ndarray는 numpy에서 지원하는 표준형인 벡터/행렬/텐서 를 저장한다.

  • 많은 numpy 함수가 matrix가 아니라 ndarray를 반환한다.

  • 요소 간 연산과 선형대수 연산에 대해선 명확히 구분되어 있다.

  • 표준 벡터나 열벡터/행벡터를 표현할 수 있다.

ndarray를 사용할 경우의 한 가지 단점은 행렬의 곱셈을 수행할 때 ,dot() method를 사용해야한다는 점이다. 즉, ndarray 객체 A와 B를 행렬곱하려면 A.dot(B) 와 같이 수행해야 하며 A*B는 요소간 곱셈이 된다. 반면 matrix객체 A와 B는 단순히 A*B로 행렬곱이 수행된다.

2. 좀 더 자세한 설명

 numpy는 ndarray 와 matrix 둘 다 포함한다. ndarray는 다양한 종류의 수치 연산을 위해서 고안된 범용 n차 배열이다. 반면 matrix는 선형 대수 연산을 위해서 특별히 고안된 객체이다. 실용적인 관점에서 둘 사이의 차이점은 몇 가지 안된다.

 

  • 연산자 *, dot() 그리고 multiply()

    • ndarray 는 '*'는 요소간 곱셈이다. 행렬곱을 할때는 obj.dot() 메쏘드를 사용해야 한다.

    • 반면 matrix는 '*'이 행렬곱이다. 그리고 numpy.multiply() 함수가 요소간 곱이다.

  • 벡터와 1차 배열

    • ndarray 는 벡터 1xN, Nx1, 그리고 N크기의 1차원 배열이 모두 각각 다르다. obj[:,1] 는 크기가 n인 1차 배열을 반환한다. 그리고 1차원 배열의 전치는 작동하지 않는다. (역자 주 : ndarray로 벡터를 표현할 때는 반드시 2차 배열을 이용해야 한다.)

    • 반면 matrix 객체에서 1차 배열은 모두 1xn, 혹은 nx1 행렬(2차원 배열)로 상향 변환된다. ( matrix 객체는 내부적으로 항상 2차원 배열이다. )

  • ndarray는 고차원 배열이 가능하지만 matrix는 항상 2차원 배열 이다.

  • 편리한 attribute

    • ndarray 는 전치를 해주는 a.T attribute 가 있다.

    • matrix 도 m.H, m.I, m.A attribute들이 있으며 각각 복소전치, 역행렬, ndarray로의 변환이다.

  • 편리한 생성자

    • ndarray는 중첩된 리스트로 다차원 배열을 생성한다. 예) array( [ [1,2,3].[4,5,6] ] )

    • matrix는 MATLAB 의 문법을 지원한다. 예) matrix('[1 2 3; 4 5 6]')


좀 더  부가 설명을 하자면 ndarray를 이용하여 벡터(vector)를 표현할 때는 2차 배열로 정의해야 한다. 즉, 다음 세 가지는 모두 다르며 이 중  벡터는 두 번째와 세 번째 같이 생성해야 한다. (혼동하기 참 쉽다.)


a1 = np.array( [1, 2, 3] ) #크기 (3,)인 1차원 배열
a2 = np.array( [ [1, 2, 3] ] ) #크기 (1,3)인 2차원 배열 (행벡터)
a3 = np.array( [ [1], [2], [3] ] ) #크기 (3,1)인 2차원 배열 (열벡터)


여기서 a1.T 는 동작하지 않는다. 반면 a2.T 와 a3.T는 동작한다. 1차 배열은 행벡터나 열벡터 두 가지 모두로 취급되기도 한다.


 ndarray 객체를 사용하는데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • 1차 배열은 행벡터나 열벡터 둘 다로 취급할 수 있다. dot(A,v) 에서 v는 열벡터로 다루어지고 dot(v,A)에서는 행벡터로 취급된다. 따라서 전치를 복잡하게 수행할 필요가 없다.

    • 요소 간 곱셈이 쉽다 (예: A*B) 사실 모든 산술 연산 (+ - * / ** 등등)이 요소 간 연산이다.

    • ndarray 는 numpy 의 가장 기본 객체이므로 연산의 속도, 효율성 그리고 numpy를 이용하는 외부 라이브러리의 반환형이 이것일 가능성이 높다.

    • 다차원 배열을 쉽게 구현한다.

    • tensor algebra 에 대한 장점이 있다.(?)

  • 단점

    • 행렬간 곱에 obj.dot() 멤버함수를 사용해야 하므로 번잡할 수 있다. 세 행렬 A, B, C의 행렬곱은 dot( dot(A,B),C) 이다


matrix 객체를 사용하는 데 있어서 장점과 단점은 다음과 같다.


  • 장점

    • MATLAB 행렬과 비슷하게 동작한다.

    • 행렬곱이 A*B 와 같이 '*' 연산자이므로 선형대수 연산이 좀 더 편하다.

  • 단점

    • matrix 의 차수는 항상 2이다. 벡터도 2차 배열로 저장된다. 3차 행렬을 구현하려면 ndarray를 이용하던가 matrix객체를 요소로 갖는 리스트(list)로 구현해야 한다.

    • ndarray 가 numpy의 표준 객체이기 때문에 어떤 함수는 인수로 matrix를 주어도 반환 객체는 ndarray 이기도 하다. numpy 에 포함된 함수는 절대 그렇지 않지만 제3자 라이브러리는 그럴 수 있다.

    • 요소간 곱은 multiply(A,B)함수를 이용해야 한다.

    • *은 행렬곱인데 반해 /는 요소간 연산이다. 즉, 연산자 오버로딩에 일관성이 결여되어 있다.


 개인적으로는 numpy를 사용하기로 결정되었다면 matlab에서의 개념은 모두 버리고 numpy에서 제공하는 객체에 빨리 익숙해져야한다는 의견이다. 그래서 matrix객체나 pylab 모듈을 사용하는 것이 권장되지 않는 것이다.




Posted by 살레시오
,

 넘파이에는 ndarray 라는 객체가 있는데 레퍼런스에는 그냥 배열(array)로 지칭한다. 아마 파이썬의 기본 객체인 리스트, 튜플, 딕셔너리 와 이름이 겹치지 않으므로 혼동의 여지가 없어서 그런 것 같다. 앞으로도 그냥 배열이라고 하면 numpy.ndarray 객체를 지칭하는 것으로 하겠다.


>>> import numpy as np

이 객체를 생성하는 대표적인 함수가 array() 함수인데 첫 번째 입력인수가 array-like 객체이다. array-like 객체는 다음과 같은 것들이 있다.

 

  • 리스트 (list)

  • 튜플 (tuple)

  • 배열 (ndarray) - 배열 자체도 array()함수의 첫 번째 인자로 올 수 있다.

  • 행렬 (matrix)


예를 들면 다음과 같다.


>>> np.array([1, 2, 3])
     array([1, 2, 3])

>>> np.array([1, 2, 3.0]) # upcasting
    array([ 1.,  2.,  3.])

>>> np.array([[1, 2], [3, 4]]) #More than one dimension:
    array([[1, 2],
          [3, 4]])
>>> np.array([1, 2, 3], ndmin=2) # Minimum dimensions 2
    array([[1, 2, 3]])

>>> np.array([1, 2, 3], dtype=complex) # Type provided:
    array([ 1.+0.j,  2.+0.j,  3.+0.j])

>>> np.array(np.mat('1 2; 3 4')) # Creating an array from sub-classes:
    array([[1, 2],
           [3, 4]])

>>> np.array(np.mat('1 2; 3 4'), subok=True)
    matrix([[1, 2],
            [3, 4]])

 넘파이로 코딩하면서 직접 확인한 몇 가지 사실들을 아래에 기록해 보았다. (혹시 오류가 있다면 댓글을 남져주시길 부탁드립니다.)


스칼라 상수(변수)로 생성된 배열은 0차 배열 이다.

스칼라로 배열을 생성할 경우 배열 객체의 차수는 0차이다. 즉, 스칼라는 0차 배열이다. shape은 empty tuple을 가지고 size는 1이다.


>>> x= 10
>>> a = np.array(x)
>>> a.ndim, a.shape, a.size
     (0, (), 1)


0차 배열이라는 용어가 생소하겠지만 배열에 스칼라도 포함된다는 것이 더 논리적인 것 같다. 만약 단일 수에서 1차 배열을 생성하려면 다음과 같이 리스트(혹은 튜플)로 만들어서 넘겨야 한다.

>>> a = np.array( [x] ) # 혹은 a = np.array( (x,) )
>>> a.ndim, a.shape, a.size
     (1, (1,), 1)


이것은 요소가 한 개인 1차 배열이 된다. 즉 np.array(10) 과 np.array([10])은 엄연히 구별된다. 전자는 0차 배열, 후자는 1차 배열이다.


2차 배열에서 크기가 다른 행은 리스트다.

이것은 예를 들어서 다음과 같은 배열 a와 b를 고려해 보자.


>>> a = np.array( [ [1,2], [3,4] ] )
>>> b = np.array( [ [1,2], [3,4,5] ] )


두 변수는 모두 배열이다. 하지만 배열 a의 각 요소 a[0] 과 a[1] 은 type 이 배열인 반면 b[0], b[1] 은 리스트이다. 즉, 변수 a 는 array of array 이고 변수 b는 array of list 이다. 따라서 a[0] 은 배열의 모든 속성을 사용할 수 있지만 b[0]는 리스트이기 때문에 그럴 수 없다.


 만약 크기가 다른 행들도 모두 배열로 만들고 싶다면 다음과 같이 하면 된다.


>>> a = np.array( [ np.array([1,2]) , np.array([1,2,3]) ] )


이것은 array of array 가 된다.


리스트와 배열의 연산 결과는 배열이다.

 리스트끼리의 산술 연산은 정의되어 있지 않으나 리스트와 배열 상호간 연산은 요소간 연산이 적용된다. 그리고 그 결과도 배열이다.


>>> [1,2,3]/2 # 에러가 발생한다.
>>> [1,2,3]/np.asarray([2])
    array([ 0.5,  1. ,  1.5])


위의 두 번째 명령의 결과를 보면 연산 결과도 배열임을 알 수 있다.



Posted by 살레시오
,

 넘파이의 ndarray(이하 배열)을 슬라이싱할 때 파이썬의 리스트(list)와 다르게 원본의 참조가 생성되는데 이를 뷰(view)라고 한다. 예를 들어서 다음과 같이 2차 배열 arr을 고려하자.


>>> arr=np.arange(8).reshape(2,4)
>>> arr
array([[0, 1, 2, 3],
      [4, 5, 6, 7]])


단순 대입문을 사용하여 arr2 = arr 이라는 명령은 arr 의 참조본이 arr2에 저장된다. 따라서 arr이 수정되면 arr2 도 같이 바뀐다. 이것은 리스트의 동작방식과 같다.

 그런데 arr[0] 라고 슬라이싱하면 첫 번째 행 전체가 선택된다.


>>> b=arr[0]
>>> b
array([0, 1, 2, 3])


여기서도 마찬가지로 b 배열은 arr 배열의 첫 행에 대한 복사가 아니고 뷰(참조의 개념)이기 때문에 원본이 바뀌면 이 참조본도 따라서 바뀌게 된다.

>>> arr[0]=10
>>> arr
array([[10, 10, 10, 10],
      [ 4,  5,  6,  7]])
>>> b
Out[127]: array([10, 10, 10, 10])


이 예에서 보면 arr 배열의 첫 행을 모두 10으로 바꾸었다. 배열의 슬라이싱에 스칼라를 대입하면 그 범위의 전체 요소에 대입된다. 그런데  b 배열도 같이 바뀐 것을 알 수 있다. 이는 리스트의 슬라이싱에서는 복사본이 생성되는 것과는 다른 동작이기 때문에 주의가 필요하다.  numpy는 대용량 데이터 처리를 염두에 두고 설계되었다. 대용량 데이터의 슬라이싱이 빈번하게 일어나는 복잡한 코드를 실행시키는데 있어서 복사가 남발되면 메모리 문제를 일으킬 소지가 많기 때문에 이렇게 설계된 것으로 짐작된다.


  만약 슬라이싱의 복사본을 생성하고 싶다면 copy() 속성을 이용하면 된다.


>>> b=arr[0].copy()
>>> b
array([0, 1, 2, 3])

이제 b 배열은 원본인 arr 과 독립적인 복사본이므로 원본이 바뀌어도 아무런 영향을 받지 않는다.

  비슷한 차이가 numpy.asarray() 함수와 numpy.array() 함수에도 존재하는데 두 함수의 동작은 거의 유사하다. 가장 큰 차이점은 copy 옵션의 초기값이 다르다는 것이다. 사실 asarray()는 내부적으로 다음과 같이 정의되어 있다.

def asarray(a, dtype=None, order=None):
   return array(a, dtype, copy=False, order=order)


참고로 array() 함수의 정의부는 다음과 같다.


numpy.array(object, dtype=None, copy=True, order=None, subok=False, ndmin=0)


따라서 만약 배열이나 행렬객체를 이용하여 다시 생성된 배열은 numpy.array()함수를 이용하였다면 복사본이, numpy.asarray()함수를 이용하였다면 참조본(전체 뷰)이 생성된다.


>>> arr = np.ones((3,4))
>>> arr
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])
>>> arrB = np.asarray(arr) # 참조본 생성
>>> arrC = np.array(arr) #복사본 생성
>> arr[1]=np.pi # 원본 변경
>>> arr
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrB # 참조본은 자동으로 변경된다.
array([[ 1.        ,  1.        ,  1.        ,  1.        ],
      [ 3.14159265,  3.14159265,  3.14159265,  3.14159265],
      [ 1.        ,  1.        ,  1.        ,  1.        ]])
>>> arrC # 복사본은 변경되지 않는다.
array([[ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.],
      [ 1.,  1.,  1.,  1.]])



Posted by 살레시오
,

 자바에서는 일차원 배열뿐 아니라 이차원, 삼차원.. 등등의 다차원 배열도 사용할 수 있다. 삼차원 이상의 배열은 이차원 배열과 사용법이 동일하므로 여기서는 이차원 배열에 대해서만 설명하겠다.


 이차원 배열은 선언할 때 변수명 뒤에 대괄호 쌍 두 개가 온다. 예를 들면 다음과 같다.


int num[][];
double val[][];
char mch[][];


위의 선언문은 사용했다고 해서 바로 배열을 사용할 수는 없고 일차원 배열에서와 마찬가지로 실제 메모리를 할당받아야 하는데 new 연산자를 이용한다. new 키워드 다음에 자료형[차수][차수] 형태로 입력한다. 예를 들면 다음과 같다.


num = new int[3][4];
val = new double[2][3];
mch = new[100][1000];


메모리를 할당하면 배열의 요소는 자동으로 기본값(정수형, 실수형은 0)으로 채워진다. 위와 같이 선언과 메모리 할당을 분리해서 수행할 수도 있고 아니면 선언과 메모리 할당을 동시에 할 수도 있다.


int num[][] = new int[3][4];;
double val[][] = new double[2][3];
char mch[][] = new char[100][1000];


만약 선언과 동시에 배열을 특정한 값들로 초기화를 하는 경우라면 new 연산자를 사용할 필요 없이 다음과 같이 중괄호를 이용할 수 있다.


double val[][] = {
   {1.1, 2.2, 3.3},
   {4.4, 5.5, 6.6}
};


이 경우 주어진 초기값에 의해서 배열의 크기는 자동으로 정해지게 된다.

 배열의 요소를 참조하는 방법은 일차원 배열과 유사하게 변수명 뒤에 대괄호로 인덱스를 지정하는데 이 경우 대괄호쌍이 두 개가 온다.


val[0][0] = 10.1; // 요소값을 지정하는 경우
double da = val[1][2]; //요소값을 읽어내는 경우


이차원 배열의 경우 length 필드는 최상위 차원의 크기를 갖는다. 하위 차원의 크기를 구하고 싶다면 다음과 같이 하면 된다.


System.out.println( val.length ); // 2가 출력된다.
System.out.println( val[0].length ); // 3이 출력된다.
System.out.println( val[1].length ); // 3이 출력된다.


 자바에서 배열은 참조형(reference type)이라는 점도 숙지하고 있어야 한다. 따라서 다른 함수에서 배열을 그대로 받아서 조작하는 경우 원 배열도 영향을 받게 된다.



Posted by 살레시오
,

 자바 제네릭(generic) 프로그래밍에 대해서 실습하던 중에 제네닉 타입의 단순 배열을 생성하는데 미묘하게 안 되는게 있기도 해서 구글링을 해 보았더니 배열을 쓰지말고 웬만하면 List<>를 사용하는게 여러 모로 편하다고 하는 의견들이 많았다. 성능차도 별로 없다고 해서 직접 간단한 프로그램으로 실행 시간을 측정 해보기로 하였다..


 초기에 크기는 배열과 리스트 모두 정해졌다고 가정하고 단순 읽고 쓰는 속도만 비교하였다.


package listtestj01;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

public class ListTestj01 {
   public static    long iter = 1000000L;
   public static    int id;
   public static    double db, dSum;
   public static long le, ls;

   public static Double[] dArr;
   public static List<Double> lst;

   public static void main(String[] args) {
       dArr = new Double[100];
       for(int k=0;k<100;k++) dArr[k]=new Double(1.0);
       
       lst = new ArrayList<>();
       for(int k=0;k<100;k++) lst.add(new Double(1.0));

       doIt();
       doIt();
       doIt();
   }
   
   public static void doIt() {
       double dSum1 =0, dSum2=0;
       long id;
       for(int n=0;n<10;n++) {
           
           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db=dArr[x];
                   dArr[x]=db;
               }
           le = System.nanoTime();
           dSum1 += (le-ls)/1e9;

           ls = System.nanoTime();
           for(int k=0;k<iter;k++)
               for(int x=0;x<100;x++) {
                   db = lst.get(x);
                   lst.set(x, db);
               }
           le = System.nanoTime();
           dSum2 += (le-ls)/1e9;
        }
       System.out.println("Double Arr: "+dSum1/10+ "sec.");
       System.out.println("List<Doub>: "+dSum2/10+ "sec.");
   }
}


이 예에서 붉은 색으로 마킹된 부분을 보면 단순히 배열 요소에서 하나를 읽고 다시 하나를 쓰고를 1억번 반복하고 이것을 다시 열 번 반복해서 평균시간을 구해서 비교하는 것이다. 내 노트북(윈764비트)에서 다음과 같은 결과를 얻었다.



세 번 반복해서 재 보아도 모두 단순 배열쪽이 두 배 이상 속도가 빨랐다. 리스트가 느리더라도 그 차이가 많이 안 나길 기대했는데 이것으로 적어도 대규모 연산이 있는 곳에서는 단순 배열이 속도 면에서 훨씬 더 낫다는 (당연한) 결과를 얻었다.




Posted by 살레시오
,

 C++ 에서 동적으로 메모리를 생성하고 반환하는 명령은 new와 delete인데 이것을이용하면 객체의 배열도 동적으로 생성하고 반환할 수 있다. new를 이용하여 Rect 라는 클래스 객체의 배열을 동적으로 생성하는 구문은 다음과 같다.


Rect *pRect = new Rect[3];


이렇게 하면 연속된 세 개의 Rect 객체가 생성되는데 이 때 각 객체에 대해 기본 생성자 Rect() 가 호출된다. 이렇게 생성된 객체 배열은 보통의 배열처럼 사용된다.


pRect[0].setWidth(11);

(pRect+1)->setWidth(21); //포인터의 멤버 함수 호출 시 -> 사용

pRect[2].setWidth(33);


이렇게 할당 받은 배열의 메모리를 반환하는 형식은 다음과 같다.


delete [] pRect;


delete 명령과 pRect 포인터 이름 사이의 [] 를 빠뜨리면 안 된다. 이렇게 하면 세 개의 Rect 객체가 소멸되는데 이 때 각 객체의 소멸자가 호출된다.


 한 가지 주의할 점은 new를 이용하여 동적으로 객체 배열을 생성할 때, 매개 변수가 있는 생성자를 호출할 수 없다는 점이다. 만약 어떤 객체를 배열로 생성하여 사용할 경우 이 점을 염두에 두고 생성자와 멤버 함수를 설계해야 한다.


class Rect {
public :
   ….
   Rect();
   Rect(double width, double height); //(2)
   ….
};


위의 예에서 두 번째 생성자 (2)는 배열을 생성하면서 동시에 호출할 수 없다. 따라서 다음과 같이 분리해 놓고


class Rect {
public :
   ….
   Rect();
   void set(double width, double height);
   ….
};


객체 배열 생성 후 반복문으로 순차적으로 set()함수를 호출하는 방법을 사용해야 한다.


Rect *pRect = new Rect[100];
for (int k=0; k<100; k++)
   pRect[k].set(11,22);




Posted by 살레시오
,

5.3 배열의 예제 및 풀이 #2     [doc]     [smts]

다음과 같은 배열 예제를 작성해 보자.


  1. int형 배열에 0부터 255까지의 정수를 순서대로 저장한다.

  2. 모든 배열의 요소와 각각의 16진수 값을 출력한다.


먼저 크기가 256인 정수형 배열을 선언한다.


int ia[256];


이 경우 선언과 동시에 초기화를 할 수도 있겠지만 초기화할 요소의 숫자가 많으므로 반복문을 이용하여 수행한다.


for (int k=0; k<256; k++) {
ia[k] = k;
}

이것에 의해서 0번 요소는 0, 1번 요소는 1, …, 255번 요소는 255로 초기화된다. 이제 각각의 요소를 출력하기 위해서 다시 for 반복문을 사용한다.


for(int k=0; k<256; k++) {
printf("ia[%d] = %d : %x\n", k, ia[k], ia[k]);
}

printf()함수의 %x 서식문자는 정수형 숫자 데이터를 16진수로 출력하는 것이다.


전체 프로그램은 다음과 같다.


#include  <stdio.h>
int main(void)
{
int ia[256];
for(int k=0; k<256; k++) {
ia[k] = k;
}
for(int k=0; k<256; k++) {
printf("ia[%d] = %d : %x\n", k, ia[k], ia[k]);
}
}

실행 예:

ia[0] = 0 : 0
ia[1] = 1 : 1
ia[2] = 2 : 2

… 중간 생략…

ia[252] = 252 : fc
ia[253] = 253 : fd
ia[254] = 254 : fe
ia[255] = 255 : ff

이 예제와 같이 많은 수의 배열 요소를 초기화할 때에는 반복문이 사용되기도 한다.


Posted by 살레시오
,

5.2 배열의 예제및 풀이 #1     [doc]     [smts]

다음과 같은 예제를 배열을 이용하여 풀어 보자.


  1. 배열에 사용자가 입력한 다섯 개의 float 형 데이터를 저장한 후 그것을 화면에 출력하라.

  2. 입력 받은 숫자들 중 가장 큰 최대값을 화면에 출력하라.


먼저 다섯 걔의 float형 값을 갖는 배열을 선언해야 한다.


float fa[5];

그 다음 사용자로부터 입력 받은 수를 저장하는 반복문을 작성해야 한다.


float fi;
for(int k=0; k<5; k++) {
scanf("%f", &fi);
fa[k]=fi;
}

이 반복문에서 정수형 변수 k는 0,1,2,3,4 값에 대해서 반복을 수행하며 입력된 실수를 배열 fa에 저장하게 된다.


이제 다 입력 받은 데이터를 화면에 출력하는 것도 반복문을 사용하면 된다.


for(int k=0; k<5; k++) {
printf("fa[%d] : %f\n", k, fa[k]);
}

여기서 printf() 함수의 첫 번째 %d에는 인덱스가, %f 자리에는 배열의 k번째 요소가 출력이 된다.


전체 프로그램은 다음과 같다.


#include  <stdio.h>
int main(void)
{
float fa[5];
float fi;
for(int k=0; k<5; k++) {
scanf("%f", &fi);
fa[k]=fi;
}

for(int k=0; k<5; k++) {
printf("fa[%d] : %f\n", k, fa[k]);
}
}

실행 예:

11
-22
1.5
4.7
-123.456
fa[0] : 11.000000
fa[1] : -22.000000
fa[2] : 1.500000
fa[3] : 4.700000
fa[4] : -123.456001

이제 사용자가 입력한 숫자들 중 최대값을 찾는 알고리듬을 다음과 같이 고안해 보자. 최대값을 저장할 변수를 fmax로 선언한다.


float fmax;


그리고 사용자가 처음 입력한 숫자를 fmax의 값으로 대입한 후 그 이후에는 새로 입력된 숫자가 이전에 저장된 fmax 값보다 클 경우 fmax 값을 갱신한다. 다섯 개의 숫자에 대해서 이 작업을 수행하면 마지막에 fmax 변수에는 가장 큰 값이 남아 있게 된다.


for(int k=0; k<5; k++) {
scanf("%f", &fi);
fa[k]=fi;
if (k==0) { // 첫 번째 데이터가 입력된 경우
fmax = fi;
} else {  // 두 번째 데이터 이후
if (fi > fmax)
fmax = fi;
}
}
}

위에서 보면 if ~ else 제어문으로 첫 번째 데이터와 그 이후의 데이터를 구분했으며 k변수가 0이면 첫 데이터라고 판단한다. 반복문이 종료되면 fmax 변수에는 최대값이 저장되어 있으므로 그것을 출력하면 된다.


printf("The maximum value is %f.", fmax);

전체 프로그램은 다음과 같다.


#include  <stdio.h>
int main(void)
{
float fa[5];
float fi;
float fmax;
for(int k=0; k<5; k++) {
scanf("%f", &fi);
fa[k]=fi;
if (k==0) {
fmax = fi;
} else {
if (fi > fmax)
fmax = fi;
}
}
printf("The maximum value is %f.", fmax);
}

실행 예:

11
-22
1.5
4.7
-123.456
The maximum value is 11.000000.

이 프로그램을 조금만 고치면 최소값을 출력하도록 할 수 있다. 직접 수정해서 결과를 확인해 보기 바란다.


Posted by 살레시오
,