자료구조와 알고리즘 문제를 풀 때의 접근 방식은?

2013-03-25 09:49

지난 주말 자료구조와 알고리즘 수업의 과제를 했다. 내가 가정이 있고, 다양한 활동으로 인해 많은 시간 과제에 투자를 할 수는 없지만 그래도 과제 중에 중요하다고 생각하는 몇 개는 직접 풀어보려고 생각하고 있다. 그리고 머리가 굳어서 인지, 머리가 나빠서 인지 모르겠지만 생각보다 쉽지 않아서 모든 과제를 풀기는 힘들다는 것을 느꼈다.

어제 과제를 하면서 느끼는 부분이 있어 질문으로 남겨 본다. 자료구조와 알고리즘 문제를 풀어 나갈 때의 접근 방식은 어떤 방식으로 접근하는 것이 좋을까?

수업을 담당하시는 교수님은 알고리즘에 대한 명세를 작성하고 pseudo code까지 만들어서 생각을 정리한 후에 소스 코드를 구현하는 방식으로 접근하라고 하셨다. 소스 코드는 지금까지 머리 속으로 정리한 내용을 구현하면 된다. 수업을 들을 때는 나도 그렇게 해봐야지라고 생각했는데 막상 문제를 풀려고 보니 생각보다 쉽지 않았다. 머리 속으로 알고리즘의 패턴을 인식하고 이에 대한 명세와 pseudo code를 만드는 것이 쉽지 않았다. 즉, 문제는 명확하게 이해하고 풀어나갈 수는 있는데 이를 프로그래밍화하는 것은 생각보다 쉽지 않았다. 한참을 멍하게 있다가 다른 방식으로 접근해 보기로 했다.

나는 바보다라는 전제를 깔고 접근했다. 따라서 알고리즘에서 가장 간단하다고 생각하는 요구사항을 먼저 프로그래밍을 구현한다. 이에 대한 구현이 끝나면 다음 단계의 요구사항을 구현한다. 이와 같이 2,3 단계를 구현하다보니 소스 코드 상에 패턴이 나타나기 시작했다. 소스 코드 중복을 제거하려고 노력하면 할 수록 패턴이 보이기 시작했다. 물론 이 과정도 쉬운 과정은 아니었다. 어느 단계에서는 다음 단계로 진행하지 못해 노트북을 떠나 머리를 식힌 적도 있다. 개발하던 소스 코드를 버리고 다시 작성한 것이 3,4번은 되는 듯하다. 이와 같이 구현하기를 반복하면서 알고리즘의 패턴을 찾았고, 알고리즘의 추상화를 할 수 있는 단계에 다다를 수 있었다. 이와 같은 접근 방식에 대해서는 추후에 블로그를 통해 글을 남겨보도록 하겠다.

자료구조와 알고리즘 문제를 풀 때 두 가지 접근 방식이 다르다. 어떤 방식으로 접근하는 것이 좋을까? 자료구조와 알고리즘을 처음 배우는 입장에서는 교수님이 추천해 주는 방법을 따르고 싶지만 생각보다 쉽지 않음을 피부로 느낀다. 다른 분들은 어떤 방식으로 접근해 왔으며, 접근하고 있는지 궁금하다. 내가 접근하고 있는 방법도 괜찮은 것인지도 궁금하다.

BEST 의견 원본위치로↓
2013-03-26 17:27

@urstory 맞습니다. 납기가 없다는 것은 정말 중요하다는 생각이 듭니다. 말씀하신데로 SLiPP 사이트 만들면서 무지 재미있게 만들고 있습니다. 더 많은 시간 투자하고 싶지만 시간적인 여력이 많지 않아서 아쉬울 때가 많네요. 지금 해결해야하는 이슈도 여럿 있는데 미뤄두고 있습니다.

과제에 대한 이슈도 똑같고요. 학생과 면담할 때도 가끔 이야기하는데 과제를 잘해서 점수를 잘 따는 것에 집중하지 마라. 학습이 더 중요하다. 따라서 모든 과제에 집중하지 말고 자신의 성장에 도움이 되는 몇 가지를 정해 자기 주도적으로 풀어보라고 조언하고 있습니다. 일정에 대한 압박을 주지 않고 학습의 본질에 집중했으면 하는 바람으로 이야기하는데 어떻게 받아들일지는 모르겠네요.

배움이라는 것에 시험이나 과제는 오히려 나쁜 영향을 준다는 생각이 듭니다. 그 보다는 동기부여가 훨씬 더 중요하고, 동기부여가 되어 있다면 굳이 하지 말라고 하더라도 알아서 하겠죠. 좋은 선생은 교육을 할 때 많은 지식을 전달하는데 집중하기 보다 동기부여를 하는데 더 집중해야 되지 않을까를 고민하는 요즘입니다.

좋은 글 잘 읽었습니다. 감사합니다.

7개의 의견 from SLiPP

2013-03-25 11:07

자료구조 본지도 20년이 된듯 한데 개인적인 사견을 남겨본다.

일단 학문적으로 자료구조와 알고리즘을 배우는 상황에서의 그 해결방법은 철저히 학문적으로 접근 하여야 할듯 하다. 즉, 수학과 같이 기본공식이 있고 그 공식에 대한 응용을 이용한 문제 해결 방법으로 진행 되어야한다. 자료구조에서도 pointer -> linkedlist -> b-tree(예시임..) 와 같이 기본지식에서 점점 확장된 응용방식을 진행 하게 된다. 그렇기 때문에 자료구조와 알고리즘이라는 학문에서 과제의 수행은 그간 배워온 기본지식에서 응용하여 문제를 해결하는 방식의 접근이 필요할듯 하다. 물론 이건 철저히 학문적인 접근에서의 관점이다. 학문적으로야 새로운 알고리즘(이론)들을 만들어 내어야 하는 것이고 그렇기때문에 과제어서도 그러한 성격이 포함되어 있을듯 생각이 된다. 그래서 너처럼 구현을 위주로 문제를 푸는건 좀 힘들지 않을까? 한다. 교수님의 말씀처럼 이론적인 퓨도코드를 먼저 생각 해야한다. 자판이 아닌 연필과 종이로 논리를 만드는것이 가장 중요하지 않을까? 한다. 이론적으로만 완벽하다면 구현은??? 을이하잖아(병정무기....) 내가 이렇게 이야기하는것은 어디까지나 학문적인 관점이다 학교에서는 그 학문을 가르키는 것이니까..

내가 보기에 넌 너무 실용적인 접근을 하고 있는것 같은데 Next방향성은 니가 생각 하는 것이라고 동의 하지만 과목들을 봤을때 기존 커리큘럼에서 크게 벗어나지 않은것이 이런 괴리감을 만들어 내는것 같다. 기존의 자료구조, 소프트웨어 공학은 이미 학문적으로 확립된 분야라 이 분야를 공부하는 최고의 목적은 새로운 이론과 알고리즘의 정의/발견/정립.. 일 것이고 수많은 학생중 거기에 해당되는 학생은 극 소수 일것이다. 그 다음 목적이 바로 기존 지식의 이해일 것이다. - 대부분의 학생이 해당될 것이고 내가쓰는 ArrayList가 어떻게 구현되는지 알고 쓰자는 목적일 것이다.(물론 도움이 될때도 있을것이고) 결국 학문이기 때문에 후자 보다는 전자에 더 가까운 방식이 되게 된다. (그런 의미에서 후자를 원한다면 내가 보기엔 커리큘럼 이름을 바꿔야 하지 않을까? 한다... 실제로 학문적인 접근이 아닌 실용적인 접근을 노력 하겠지만 이미 동일한 이름의 학문이 있는 상황에서 택할수있는 길은 그렇게 많지 않을것 같기에... <--이부분은 나중에 술자리에서 길게 이야기 하자꾸나)

그렇기 때문에 이러한 과제들은 기존 이론을 이해하는? 훈련일것이라 생각이 들고 이런 과제들에서 문제 해결에 목적이 아닌 기존 자료구조와 알고리즘의 이해를 확실히 하는 방향으로 생각 하는게 좋지 않을까? 한다. ^^;

네 질문의 답은 아닌것 같지만 니 글을 보고 생각난걸 그냥 두서없이 적어본다. ^^

2013-03-25 13:45

"나는 바보다"라는 전제가 잘못되었다고 생각합니다. 아는만큼 보인다. 가 전 알고리즘을 구하는 올바른 방식이라고 봅니다. 알고리즘이라는 것이 단순히 문제만 해결하는 것이 아니라 좀더 좋은 알고리즘을 찾는 것이 중요한 부분이라고 생각하거든요.

그래서 무작정 스스로 푸는 것보다는. 선배들이 앞서 연구. 사람들이 구현한 내용부터 찾아보거 익히고 이사람들이 어떤 선수과정을 익혔는지 알아보고 그 길을 따라가는 것이 먼저라고 생각합니다.

물론 따라만 갈수밖에 없는 상황일지라도요.

다만 재미나게 공부하려면 왜 이사람이 이런식으로 문제를 해결하였는지 그 알고리즘의 뒷이야기를 공부하는 것이 더 좋지 않을까? 생각합니다.

예전 수학자들는 그래서 서로 서신ㄷㅎ 많이 나누고 함께 문제릉 해결해나갔다고 하더군요

2013-03-25 14:07

@jhindhal.jhang @urstory 두 분다 좋은 의견 감사합니다.

제가 쓴 글은 요즘 제가 계속해서 고민하고 있는 부분과 연결되는 내용입니다. 기초 학문이 정말 중요하고, 필요한 부분이라는 것은 다들 공감합니다. 저 또한 필요하다고 생각합니다.

제가 중,고등학교를 다니면서 가장 잘한 과목이 수학이었습니다. 하지만 수학이 정말 재미있는 학문이라는 생각을 해본적이 거의 없습니다. 답이 정확하게 딱 떨어져서 나오기 때문에 그나마 과목 중에서 좋아했던 듯 합니다. 하지만 대부분의 접근 방식은 공식을 외우고 여기서 응용력을 키우는 식으로 접근했던 듯 합니다. 왜 그와 같은 공식이 나오게 되었는지에 대한 과정에 대해 우리나라 교육은 너무 소홀했던 것으로 생각합니다. 그 결과 지금은 수학을 거의 공부하지 않고 있습니다. 수학이 정말 재미있다고 생각하지 않기 때문입니다.

저는 알고리즘과 수학이 똑같다고 생각합니다. 여기서 중요하게 생각해야 할 부분은 선배들이 만들어 놓은 자료구조와 알고리즘을 더 많이 아는 것이 중요할까라는 생각을 해봤습니다. 내가 수학을 그렇게 공부했는데 지금까지 남아 있는 지식은 많지 않고, 나는 수학을 다시는 들여다 보고 있지 않아요. 알고리즘에 대해 학생들이 제가 수학을 경험한 것과 똑같은 경험을 하지 않을까라는 생각을 해봅니다. 추후 학교를 졸업하면 다시는 들여다보지 않는거 아니야라는 생각요.

지금 단계는 자료구조와 알고리즘을 아는 것도 중요하지만 그 자료구조와 알고리짐을 만들어가는 과정을 이해하는 것이 더 중요하지 않을까라는 생각을 합니다. 어차피 현실의 문제는 한 가지로 정해진 것은 아니기 때문에 문제에 부딪혔을 때 이를 잘 해결해 나가는 방법을 학습하는 것이 더 중요하지 않을까라는 생각을 합니다. 그래야 앞으로 발생할 다양한 문제에 대해 다양한 해결 방법을 제시할 수 있지 않을까라는 생각이 듭니다.

과정을 중요하게 생각한다면 자료구조와 알고리즘을 접근할 때 소스 코드 레벨부터 접근해도 좋지 않을까라는 생각을 합니다. 소스 코드를 구현하다보면 소스 코드에서 패턴을 찾을 수 있고, 이 패턴을 해결하는 과정에서 해당 자료구조와 알고리즘을 더 깊이 있게 인식할 수 있지 않을까요? 주말에 완전히 다른 방식으로 접근해 보면서 그런 생각을 해봤음다. 조만간 제가 주말에 구현한 하노이의 탑 문제 풀이 과정을 공유해 볼께요. 그 과정을 보고 더 많은 이야기 나누었으면 좋겠어요. 저도 아직 이에 대한 확신이 없거든요. 저의 경험도 너무 한쪽으로 치우쳐져 있기 때문에 이와 관련해 더 많은 사람들과 이야기 나누고 싶네요.

2013-03-25 16:13

수학을 잘하셨다니 부럽습니다.

전 중학교까지 수학을 좋아했는데, 고등학교땐 수학이 정말 싫었습니다.

그러다가, 몇일전에 수학이 재미없는 이유라는 어떤 글을 봤는데......

예전 수학자들은 수학 한문제를 풀기 위하여 혹은 증명하기 위하여 몇달이 걸렸다는 말이 있더라고요.

페르마의 마지막 정리로 유명한 페르마도 법률가였지 직업이 수학자는 아니였습니다.

말그대로 취미가 수학이었죠.

그럼 왜 수학이 재미없느냐? 하나의 문제를 풀어가는 과정을 즐기는 것이 아니라. 요즘은 시험을 보기 위하여 한시간에 20문제 30문제를 풀어야 하니 기계적으로 푸는 방법만 익숙하게 배우기때문이라고 하더군요.

그말을 보고, 프로그래밍이 재미없는 이유도 같은 이유가 아닐까? 하는 생각이 들었습니다.

한시간동안 문제를 풀게하는 수학과

납기는 생명이라면서 어느 기한까지 꼭 만들라고 하는 프로그래밍.

아마 자바지기님께서는 SLiPP사이트 만들면서 재미있지 않나요? 전 그게 납기가 없어서 라고 생각합니다. 알고리즘도 마찬가지라고 생각합니다. 알고리즘을 배우는 사람에게 다음주까지 꼭 과제를 내놔라. 라고 한다면 어떤 사람은 금방 답이 나와서 과제를 할 수 있는 사람도 있겠지만, 어떤 사람은 생각하기에 충분하지 않은 시간일수도 있다고 생각합니다.

과정을 이해하는 것이 중요하다라고 말씀하시는 것은.... 알고리즘이란 과정이 이제 시험의 영역이 아닌 순수하게 접근하기 때문이 아닐까요? 흐으.

전 수학이나 프로그래밍이나 알고리즘이나 문제해결하는 것은 다 비슷하다고 봅니다. 어쩌면 프로그래밍 좋아하는 사람은..... 수학도 좋아할 수 있지 않을까? 하고 생각하고 있습니다.

그래서 요즘 나오는 수학책도 사서 보고 하는데..... 이런 확신은 들더라고요. 고등학교때 날 가르친 수학선생님은 확실히 재미없게 가르쳤다....라는 확신.... 수학책이 재미있었습니다. :-)

충분한 시간과 반드시 답을 내라는 압박이 없다면 수학도 알고리즘도 프로그래밍도 좋은 취미라고 전 생각합니다. 그리고..... 과정을 이해하는 것 자체가 말씀하신 것처럼 중요하다고 생각합니다.

다만, 현재 알고리즘을 배우는 사람의 입장이 어떤 입장이냐가 중요하다고 생각합니다. 배울게 알고리즘만 있는게 아니라..... 엄청나게 많은 과정이 있다면? 이것저것 해야할 것이 많다면??? 취미로 할 수없다면......

여하튼, 그런 이유로 전 딸이랑 수학을 같이 해보고 싶은데.... 바쁘다는 핑계로 잘 안되네요....

2013-03-26 17:27

@urstory 맞습니다. 납기가 없다는 것은 정말 중요하다는 생각이 듭니다. 말씀하신데로 SLiPP 사이트 만들면서 무지 재미있게 만들고 있습니다. 더 많은 시간 투자하고 싶지만 시간적인 여력이 많지 않아서 아쉬울 때가 많네요. 지금 해결해야하는 이슈도 여럿 있는데 미뤄두고 있습니다.

과제에 대한 이슈도 똑같고요. 학생과 면담할 때도 가끔 이야기하는데 과제를 잘해서 점수를 잘 따는 것에 집중하지 마라. 학습이 더 중요하다. 따라서 모든 과제에 집중하지 말고 자신의 성장에 도움이 되는 몇 가지를 정해 자기 주도적으로 풀어보라고 조언하고 있습니다. 일정에 대한 압박을 주지 않고 학습의 본질에 집중했으면 하는 바람으로 이야기하는데 어떻게 받아들일지는 모르겠네요.

배움이라는 것에 시험이나 과제는 오히려 나쁜 영향을 준다는 생각이 듭니다. 그 보다는 동기부여가 훨씬 더 중요하고, 동기부여가 되어 있다면 굳이 하지 말라고 하더라도 알아서 하겠죠. 좋은 선생은 교육을 할 때 많은 지식을 전달하는데 집중하기 보다 동기부여를 하는데 더 집중해야 되지 않을까를 고민하는 요즘입니다.

좋은 글 잘 읽었습니다. 감사합니다.

2013-03-26 21:46

위의 소스코드를 보니... 예전에 c로 하노이 탑 만들던 기억이...어렴풋이.....나네요..

재귀호출로 구하는 문제를 비재귀호출로 구하는 것이 더 성능을 높일 수 있다는 것을..... 예전에 본기억이 나서.... 객체지향적으로 한번 하노이 탑을 풀어볼까? 재귀호출을 이용하지 말아보자!! 라는 마음으로.... 풀어보았습니다. 이때 랜덤함수를 이용!! (성능은 따지지 말자! 라는 생각으로.....)

public class HanoiMain {


	public static void main(String[] args) {
		String[] names = new String[]{"a", "b", "c"};
		HanoiTower towers[] = new HanoiTower[names.length];
		
		// tower 초기화 
		for(int i = 0; i < towers.length; i++){
			if(i == 0)
				towers[i] = new HanoiTower(names[i], towers.length, false);
			else
				towers[i] = new HanoiTower(names[i], towers.length, true);
		}
		
		// 초기화된 tower값 출력. 
		for(int i = 0; i < towers.length; i++){
			System.out.println(towers[i]);
		}


		break_while:
		while(true){
			// 아래의 2줄을 랜덤하게 가지고 오지 않고, 좀 머리를 쓰는 방식으로 고치자.
			int i = (int)(Math.random() * towers.length);
			int j = (int)(Math.random() * towers.length);
			if(i != j){
				HanoiRing ring = towers[i].pop();
				if(ring == null)
					continue;
				boolean pushflag = towers[j].push(ring);
				if(!pushflag){
					towers[i].push(ring);
				}else{
					System.out.println(towers[i].getTowerName() + " 에서 " +  ring.getSize() + " 사이즈 링을 꺼내서 " + towers[j].getTowerName() + " 으로 이동 ");
					if(towers[towers.length -1].isOK()){
						System.out.println("마지막 하노이탑으로 이동이 완료 되었습니다.");
						for(int k = 0; k < towers.length; k++){
							System.out.println(towers[k]);
						}
						break break_while;
					} // if
				} // else
			} // if
			
		}
	} // main


}
import java.util.List;



public class HanoiTower {
	private List<HanoiRing> list;
	private int maxHeight;
	private String towerName;
	
	
	/    *
	 * maxHeight개의 링을 가지는 타워 생성 
	 * @param towerName 타워이름
	 * @param maxHeignt 타워가 가질 수 있는 링의 수
	 * @param isEmpty 링을 처음에 가질지 안가질지의 여부 , false 일 경우에만 링을 가지게 된다.
	 */
	public HanoiTower(String towerName, int maxHeignt, boolean isEmpty){
		list = new ArrayList<>();
		this.maxHeight = maxHeignt;
		this.towerName = towerName;
		if(!isEmpty){
			for(int i = maxHeignt; i >= 1 ; i--){ // 큰 것부터..작은 순서로 집어넣어야 한다.
				push(new HanoiRing(i));
			}
		}
	}
	
	
	
	public int getMaxHeight() {
		return maxHeight;
	}




	public String getTowerName() {
		return towerName;
	}




	/    *
	 * 탑에 링을 넣는다. 아래의 링이 현재 링보다 클 경우에만 들어가진다.
	 * @param ring
	 */
	public boolean push(HanoiRing ring){
		if(!isEmpty()){
			HanoiRing lastHanoiRing = list.get(list.size() -1);
			if(lastHanoiRing.getSize() < ring.getSize()){ // 현재 들어온 링의 크기가 마지막 링보다 클경우..
				return false;
			}
		}
		list.add(ring);
		return true;
	}
	
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		for(int i = 0; i < list.size(); i++){
			sb.append(list.get(i).getSize() + "    ");
		}
		return towerName + " : " + sb.toString();
	}


	public boolean isOK(){
		if(maxHeight == list.size())
			return true;
		else
			return false;
	}
	
	/    *
	 * 링이 없다면 null을 반환 있을 경우 맨 위에 있는 것을 하나 반환.
	 * @return
	 */
	public HanoiRing pop(){
		if(isEmpty())
			return null;
		return list.remove(list.size()-1);
	}
	
	/    *
	 * 링이 없다면 empty를 반환
	 * @return
	 */
	public boolean isEmpty(){
		if(list.size() > 0)
			return false;
		else
			return true;
	}
}
public class HanoiRing {
	private int size;


	public HanoiRing(int size) {
		super();
		this.size = size;
	}


	public int getSize() {
		return size;
	}


	@Override
	public String toString() {
		return "HanoiRing [size=" + size + "]";
	}
	
	
}

사실... 랜덤으로 문제를 해결한 이유는.......

http://www.youtube.com/watch?feature=player_embedded&v=nL2v6U4EGGA

위의 동영상을 보고.... 랜덤하게... 하노이탑을 빨리 옮기는 것을 것을... 여러개 구해본다면...... 그네 잘타는 녀석을 건질 수 있는 것처럼... 하노이의 달인....이 나오지 않을까하는 기대로......

하지만... 그 이상은 머리가 돌아가지 않아서...랜덤하게..구하는 것 까지만.... -_-;; ㅎㅎ

추가하자면, 하노이 탑은 위쪽의 링이 아래쪽의 링보다는 큰 것만 와야 하는 형태의 독특한 구조의 Stack으로생각해서....

첫번째 하노이 탑에서 링을 뽑은 후... 다른 하노이 탑에 집어 넣었는데... 집어넣을 수 없다면, 원래 하노이 탑에 다시 집어넣고.

또 랜덤하게 2개의 탑을 구해서... 링을 옮기는 형식으로 했습니다.

마치... 아기가 랜덤하게 옮기듯이......

마치 사람이... 탑을 보고... 옮기듯이..직관적으로 옮기는 형태로 구현하고 싶었는데 귀차니즘과.... 머리아픔이..엄습해서...ㅎㅎ

의견 추가하기