minmax 함수, 그리고 일타이피

배열에서 최대값을 찾는 일은 배열 전체를 한번 훑어야 하는 일입니다. 최소값을 찾는 일도 배열 전체를 한번 훑어야 하는 일입니다. 그러지 않고서는 이 값이 최대값(또는 최소값)이라는 확신을 할 수 없을 테니까요. 여기에 어떤 신비로운 비법은 없습니다.

그런데, 배열에서 최대값과 최소값을 모두 알고자 한다면, 배열 전체를 두 번 훑어야 할까요? 그건 아닙니다. 한번만 스캔하면 최대값과 최소값을 다 찾을 수 있습니다. 작은 배열에서 최대값이나 최소값을 찾는 일은 연산 시간 같은 것을 따질 필요가 없지만, 배열이 커지면 한번에 하는 것과 두번에 하는 것이 거의 두배 시간 차이가 나는 게 당연하게 됩니다.

이런 이유로 IDL은 최소값을 구하는 MIN 함수에서도 최대값을 구할 수 있도록, 반대로 최대값을 구하는 MAX 함수에서도 최소값을 구할 수 있도록 키워드를 제공하고 있습니다. 배열을 한번 훑어서 최대값, 최소값을 동시에 구할 수 있게 하는 거죠. 문법의 일부가 이렇습니다.

Result = MIN(배열, 최소값위치를담을변수, MAX=최대값을담을변수, SUBSCRIPT_MAX=최대값위치를담을변수)

Result = MAX(배열, 최대값의위치를담을변수, MIN=최소값을담을변수, SUBSCRIPT_MIN=최소값위치를담을변수)

도움말을 보시면 여기에 절대값 옵션(/ABSOLUTE), NaN 무시 옵션(/NAN), 최대최소를구할 배열의 차원 방향(DIMENSION=) 키워드가 더 있는데, 이 글의 주제와는 관련성이 적습니다.

다음과 같은 예제 프로그램을 돌려보면 효과는 예상대로 나타납니다.

20억개의 배열요소에 대해 최대값과 최소값을 구하는 일을 할 때, mmi=min(x, max=mma) 와 같이 한번에 구하는 것과 min(x), max(x)로 두번에 구하는 방법의 소요 시간은, 제 컴퓨터에서는 요정도 나옵니다.

% Time elapsed: 0.76900005 seconds.   (한번에 min과 max)
% Time elapsed: 1.3880000 seconds.  (min과 max를 따로)

예상대로 두배 비슷한 소요시간을 보입니다.

제가 IDL Astronomy 라이브러리의 MINMAX() 함수를 꽤 자주 사용하고 있다는 사실을 알게 되었습니다. 한번에 최대값과 최소값을 구하는 함수인데, 앞에 설명드렸듯이 신비로운 기능을 지닌 함수가 아니고, 그냥 IDL에 있는 기능을 문법을 좀 편하게 바꾸어 놓은 함수일 뿐입니다. 소스코드도 열어볼 수 있습니다. 설명(comment)이 엄청 길지, 사실 코드는 몇 줄 되지도 않습니다. 이렇게 씁니다.

IDL> mm=minmax(x)

IDL> help, mm
MM FLOAT = Array[2]

리턴값은 두 개의 요소를 가지는 배열로 [최소값, 최대값]으로 이루어집니다. minmax도 최대값, 최소값의 위치(인덱스번호)도 알 수 있습니다. 내부적으로 MIN() 함수를 쓰고 있고 키워드도 거의 그대로 공유합니다.

MINMAX() 함수는 사용하기에도 좀 더 직관적인 것 같고(개인 감정인가요?), 리턴값도 유용하게 2개요소 배열로 나옵니다. 이렇게 쓸 수 있죠.

IDL> plot, xx, yrange=[minmax(xx)]

YRANGE=[min(xx), max(xx)] 를 간결하게 대체할 수 있는 유용성도 있습니다. 제 경우에는, 한번에 최대최소를 구해서 더 빠르다는 장점 보다는 코드가 보기 편해진다, 리턴값을 활용하기 유용하다는 이유로 자주 쓰고 있습니다. READCOL이나 READFITS 같이 굉장한 기능을 가진 것은 아니지만, 그거 조금 간결해진다고 손이 가네요.

참고로, 배열을 두번 훑는 거 보다는 한번 훑어서 해결하는 게 효율적인 상황이 또 있습니다. WHERE 함수와 관련된 건데요, WHERE 함수는 다들 아시다 시피 배열에서 조건을 만족하는 요소들의 위치를 리턴합니다. 조건을 만족하지 않는 요소들도 따로 관리하고 싶다면 어떻게 할까요? 조건을 반대로 만들어서 또 다시 WHERE를 돌려야 할까요? 그럴 리가 없겠죠. WHERE 함수의 문법은 다음과 같습니다.

Result = WHERE(조건문, count, COMPLEMENT=변수,  NCOMPLEMENT=변수 )

WHERE의 직관적 의미 그대로, 조건을 만족하는 요소들을 찾는 것이 기본 목적이지만, COMPLEMENT 키워드로 조건을 만족하지 않는 배열의 위치를 동시에 파악할 수 있고, 그 개수도 NCOMPLEMENT로 받아낼 수 있습니다.   이런 걸 전문용어로 일타이피라고 한다네요.