시리즈:수학인듯 과학아닌 공학같은 컴퓨터과학/알고리즘 기초 편집하기

편집하면 당신의 IP 주소가 공개적으로 기록됩니다. 계정을 만들고 로그인하면 편집 시 사용자 이름만 보이며, 위키 이용에 여러 가지 편의가 주어집니다.

편집을 취소할 수 있습니다. 이 편집을 되돌리려면 아래의 바뀐 내용을 확인한 후 게시해주세요.

최신판 당신의 편집
7번째 줄: 7번째 줄:


== 들어가기 전 ==
== 들어가기 전 ==
{{--|일단 이게 입문인지부터 생각해보자}}
빨간 컵에 [[칠성사이다]]가 담겨져 있고 파란 컵에 [[스프라이트]]가 담겨져 있다. 여기서 파란 컵에 칠성사이다를 넣고 빨간 컵에 스프라이트를 넣을려면 어떻게 해야 될까? 해답은 간단하다. 다른 컵(여기선 노란 컵이라 하겠다.)에 빨간 컵의 칠성사이다를 노란 컵에 붓고 빨간 컵이 비게 되면 파란 컵의 스프라이트를 빨간 컵에 붓고 파란 컵이 비게 되면 노란 컵에 담았던 칠성사이다를 파란 컵에 부으면 된다. 갑자기 노란 컵이 튀어나와서 쌩뚱맞다고 생각하는 사람이 있을 수 있겠지만 다른 컵을 쓰면 안 된다고는 안했다. 알고리즘은 문제를 해결하기 위한 것이지 [[수수께끼]]나 [[넌센스]] 문제가 아니다.
빨간 컵에 [[칠성사이다]]가 담겨져 있고 파란 컵에 [[스프라이트]]가 담겨져 있다. 여기서 파란 컵에 칠성사이다를 넣고 빨간 컵에 스프라이트를 넣을려면 어떻게 해야 될까? 해답은 간단하다. 다른 컵(여기선 노란 컵이라 하겠다.)에 빨간 컵의 칠성사이다를 노란 컵에 붓고 빨간 컵이 비게 되면 파란 컵의 스프라이트를 빨간 컵에 붓고 파란 컵이 비게 되면 노란 컵에 담았던 칠성사이다를 파란 컵에 부으면 된다. 갑자기 노란 컵이 튀어나와서 쌩뚱맞다고 생각하는 사람이 있을 수 있겠지만 다른 컵을 쓰면 안 된다고는 안했다. 알고리즘은 문제를 해결하기 위한 것이지 [[수수께끼]]나 [[넌센스]] 문제가 아니다.


이 이야기가 왜 나왔냐면 프로그래밍을 할 때 아래 소스처럼 2개의 변수 값을 서로 바꿀 경우 변수 2개 가지고 끙끙 거리지 말고 간단하게 변수 하나 더 사용해서 쉽게 해결하면 되기 때문이다.  
이 이야기가 왜 나왔냐면 프로그래밍을 할 때 아래 소스처럼 2개의 변수 값을 서로 바꿀 경우 변수 2개 가지고 끙끙 거리지 말고 간단하게 변수 하나 더 사용해서 쉽게 해결하면 되기 때문이다. <del>이제 제 3의 변수를 쓰지 않고 변수 2개의 값을 맞바꾸는 알고리즘을 생각해보자.</del>
{| class="collapsible collapsed"
{| class="collapsible collapsed"
|-
|-
69번째 줄: 70번째 줄:
여담으로, 위 취소선의 답은 의외로 간단하다. 정수가 저장되어 있는 a와 b가 있을 때, a와 b를 더한 다음 a에 저장한다. 그 다음 a에서 b를 뺀 값 을 b에 저장한다.(원래 a값이 되었다.) 그 다음 a에서 b를 뺀 값을 a에 저장하면 된다. <del>숫자가 아니고 객체면? 음료수는 더하고 빼면 섞이잖아</del> 하지만 이 방법은 [[산술 오버플로우]]의 문제 때문에 자료형이 표현 가능한 범위보다 더 적은 범위의 변수만 바꿀 수 있다.
여담으로, 위 취소선의 답은 의외로 간단하다. 정수가 저장되어 있는 a와 b가 있을 때, a와 b를 더한 다음 a에 저장한다. 그 다음 a에서 b를 뺀 값 을 b에 저장한다.(원래 a값이 되었다.) 그 다음 a에서 b를 뺀 값을 a에 저장하면 된다. <del>숫자가 아니고 객체면? 음료수는 더하고 빼면 섞이잖아</del> 하지만 이 방법은 [[산술 오버플로우]]의 문제 때문에 자료형이 표현 가능한 범위보다 더 적은 범위의 변수만 바꿀 수 있다.


사실 덧셈보다 더 빠른 방법은 XOR 회로를 사용하는법이 있다. 값이 같지 않으면 a=a^b b=a^b a=a^b를 넣어두면 정수가 아니더라도, 산술적 연산이 아니라 비트 배열 자체가 바뀌기 때문에 값이 교환된다. 하지만, 컴퓨터의 최적화 정도에 따라서 그냥 노란 컵을 이용하는 방법이 더 빠를 수도 있다.
사실 덧셈보다 더 빠른 방법은 XOR 회로를 사용하는법이 있다. 값이 같지 않으면 a=a^b b=a^b a=a^b를 넣어두면 정수가 아니더라도, 산술적 연산이 아니라 비트 배열 자체가 바뀌기 때문에 값이 교환된다.<s>하지만 병렬 프로그래밍에서는 무리DA☆ZE★</s> 하지만, 컴퓨터의 최적화 정도에 따라서 그냥 노란 컵을 이용하는 방법이 더 빠를 수도 있다.


== 왜 알아야 하나? ==
== 왜 알아야 하나? ==
124번째 줄: 125번째 줄:
import java.util.Scanner;
import java.util.Scanner;


public class example{
int res = 0;
  public static void main(String[] args){
Scanner sc = new Scanner(System.in);
  int res = 0;
int N = sc.nextInt();
  Scanner sc = new Scanner(System.in);
  int N = sc.nextInt();


  for(int i=1; i<=N; i++){
for(int i=1;i<=N;i++){
      res += i;
    res += i;
  }
}


  sc.close();
System.out.println(res);
  System.out.println(res);
  }
}
</syntaxhighlight>
</syntaxhighlight>
}}
}}
174번째 줄: 170번째 줄:
import java.util.Scanner;
import java.util.Scanner;


public class example{
int res = 0;
  public static void main(String[] args){
Scanner sc = new Scanner(System.in);
      int res = 0;
int N = sc.nextInt();
      Scanner sc = new Scanner(System.in);
res = N * (N+1) / 2
      int N = sc.nextInt();
      res = N * (N+1) / 2
      sc.close();


      System.out.println(res);
System.out.println(res);
  }
}
</syntaxhighlight>
</syntaxhighlight>
}}
}}
200번째 줄: 191번째 줄:
<math>n=10</math> 정도의 작은 데이터에서는 100배 더 느리다고 해도 불과 0.0001초 정도밖에 차이가 나지 않을 것이다. 데이터의 크기가 적으므로 두 프로그램 모두 매우 빠르기 때문이다! 하지만 <math>n=100,000</math>에서는 100배의 차이는 생각하는 것만큼 매우 크다. <math>1000n</math>번 계산하는 프로그램이 0.3초간 계산해서 답을 내놓는 사이에, <math>n^2</math>번 계산하는 프로그램은 30초는 생각해야 답을 내놓을 수 있을 것이다. <math>n=1,000,000</math>라던지, <math>n=10,000,000</math>와 같은 식으로 n이 더더욱 커지면 어떻게 될까? 두 프로그램의 시간 차이는 기하급수적으로 커지게 될 것이다.
<math>n=10</math> 정도의 작은 데이터에서는 100배 더 느리다고 해도 불과 0.0001초 정도밖에 차이가 나지 않을 것이다. 데이터의 크기가 적으므로 두 프로그램 모두 매우 빠르기 때문이다! 하지만 <math>n=100,000</math>에서는 100배의 차이는 생각하는 것만큼 매우 크다. <math>1000n</math>번 계산하는 프로그램이 0.3초간 계산해서 답을 내놓는 사이에, <math>n^2</math>번 계산하는 프로그램은 30초는 생각해야 답을 내놓을 수 있을 것이다. <math>n=1,000,000</math>라던지, <math>n=10,000,000</math>와 같은 식으로 n이 더더욱 커지면 어떻게 될까? 두 프로그램의 시간 차이는 기하급수적으로 커지게 될 것이다.


위의 예시에서 알 수 있었던 것은 '''계산 횟수에 붙은 상수는 별로 중요하지 않다는 것'''이다. 1,000이라는 상수가 무색해질 정도로 n이 커지면, 그 때는 n이 커짐에 따라 증가하는 계산 횟수가 훨씬 더 중요해진다.<ref>물론, 상수 <math>C=100,000,000</math> 수준으로 엄청나게 커지면 그 때는 문제가 되지만, 실제 프로그래밍에서 그런 상황은 발생하기 어렵다.</ref> 그래서 시간복잡도를 나타낼 때에는 상수는 모두 떼어버리기로 하자. <math>n</math>번 계산하든, <math>7n+1312</math>번 계산하든, <math>142857n</math>번 계산하든 모두 <math>O(n)</math>로 나타내는 것이다!  비슷한 이유로, <math>3^n+2n^3+12451n^2+33n\log n+151511</math>번 계산<ref>잔 작업의 처리가 많은 알고리즘인 듯하다</ref>하는 알고리즘의 시간 복잡도는 <math>O(3^n)</math>로 간단히 나타낼 수 있다.
위의 예시에서 알 수 있었던 것은 '''계산 횟수에 붙은 상수는 별로 중요하지 않다는 것'''이다. 1,000이라는 상수가 무색해질 정도로 n이 커지면, 그 때는 n이 커짐에 따라 증가하는 계산 횟수가 훨씬 더 중요해진다.<ref>물론, 상수 <math>C=100,000,000</math> 수준으로 엄청나게 커지면 그 때는 문제가 되지만, 실제 프로그래밍에서 그런 상황은 발생하기 어렵다.</ref> 그래서 시간복잡도를 나타낼 때에는 상수는 모두 떼어버리기로 하자. <math>n</math>번 계산하든, <math>7n+1312</math>번 계산하든, <math>142857n</math>번 계산하든 모두 <math>O(n)</math>로 나타내는 것이다!  비슷한 이유로, <math>3^n+2n^3+12451n^2+33n\log n+151511</math>번 계산<ref>잔 작업의 처리가 많은 알고리즘인 듯하다</ref>하는 알고리즘의 시간 복잡도는 <math>O(2^n)</math><ref><math>3^n</math>도 O 표기법으로는 <math>2^n</math>와 같다. 물론, <math>150^n</math>라던지 <math>14500^n</math>와 같은 시간 복잡도와도 같은 시간 복잡도를 나타내는 것이지만, 가장 간단한 형태인 <math>2^n</math>로 나타내는 것.</ref>로 간단히 나타낼 수 있다.


위의 소스들의 시간 복잡도는 어떻게 쓸 수 있을까? 소스 1은 <math>O(N)</math>의 시간 복잡도를, 소스 2는 <math>O(1)</math>의 시간 복잡도를 가지게 된다는 것을 알 수 있다. 직관적으로도 알 수 있듯이, <math>O(1)</math>의 알고리즘이 <math>O(N)</math>의 알고리즘보다 더 빠른 알고리즘이라고 할 수 있다.
위의 소스들의 시간 복잡도는 어떻게 쓸 수 있을까? 소스 1은 <math>O(N)</math>의 시간 복잡도를, 소스 2는 <math>O(1)</math>의 시간 복잡도를 가지게 된다는 것을 알 수 있다. 직관적으로도 알 수 있듯이, <math>O(1)</math>의 알고리즘이 <math>O(N)</math>의 알고리즘보다 더 빠른 알고리즘이라고 할 수 있다.
253번째 줄: 244번째 줄:
array3 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # 리스트 안에 리스트가 중첩되어 있다.
array3 = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] # 리스트 안에 리스트가 중첩되어 있다.
# 리스트 안에는 리스트를 포함하여 어떤 객체도 넣을 수 있다.
# 리스트 안에는 리스트를 포함하여 어떤 객체도 넣을 수 있다.
</syntaxhighlight>
[[자바]]에서는 대괄호의 위치가 변수명보다 앞에 있다.
<syntaxhighlight lang="Java">
int[] array1;
int[] array2 = new int[10]; //heap 메모리에 할당하여 사용
int[] array3 = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; //C언어와 동일한 초기화
</syntaxhighlight>
</syntaxhighlight>


276번째 줄: 260번째 줄:
[[파일:Data stack.svg|섬네일|스택 자료구조]]
[[파일:Data stack.svg|섬네일|스택 자료구조]]
먼저 들어간 자료가 나중에 나오는 자료구조. 유식한 말로 후입선출(後入先出) 구조라고 한다. 자료를 넣는 push 함수와 자료를 빼는 pop 함수를 갖는 게 정석이다. empty, full 등 다른 함수들을 가질 수 있다.
먼저 들어간 자료가 나중에 나오는 자료구조. 유식한 말로 후입선출(後入先出) 구조라고 한다. 자료를 넣는 push 함수와 자료를 빼는 pop 함수를 갖는 게 정석이다. empty, full 등 다른 함수들을 가질 수 있다.
[[C++]]의 [[STL]] (Standard Template Library의 약자이다. 간단하게 설명하자면 여러 자료 구조, 함수, 알고리즘 등을 쓰기 쉽게 정형화해서 라이브러리화 해둔 것)에서 지원한다.
[[C++]]의 [[STL]]에서 지원한다.


아래는 가장 기본적인 스택의 구현이다.
아래는 가장 기본적인 스택의 구현이다.
340번째 줄: 324번째 줄:
elppa
elppa
>>>  
>>>  
</syntaxhighlight>
[[자바]]의 배열 자료구조로 스택 클래스를 만들면 다음과 같을 것이다. C언어와 동일한 기능을 수행하도록 구현하였다.
<syntaxhighlight lang="Java">
public class Stack{
    int size = 500;
    int[] arr = new int[size];
    int top = 0;
   
    int push(int n){
        if(top > size){
          return -1;
        }
        arr[top++]=n;
        return 0;
    }
    int pop(){
      if(top <= 0){
        return -1;
      }
      return arr[--top];
    }
    public static void main(String[] args){
      Stack stack = new Stack();
      stack.push(1);
      stack.push(2);
      stack.push(3);
      System.out.printf("%d %d %d", stack.pop(), stack.pop(), stack.pop());
    }
}
</syntaxhighlight>
</syntaxhighlight>


416번째 줄: 368번째 줄:




[[파이썬]]에서 리스트 자료구조로 큐를 구현하는 클래스를 작성 하자면 다음과 같을 것이다.
[[파이썬]]에서 리스트 자료구조로 큐를 구현하는 클래스를 작성하자면 다음과 같을 것이다.
<syntaxhighlight lang="Python">
<syntaxhighlight lang="Python">
class queue:
class queue:
433번째 줄: 385번째 줄:
def size(self):
def size(self):
return len(self.items)
return len(self.items)
</syntaxhighlight>
[[자바]]에서 [[연결 리스트]] 자료구조로 큐를 구현하는 클래스를 작성 하자면 다음과 같을 것이다.
<syntaxhighlight lang="Java">
import java.util.Queue;
public class Queue {
    Queue<Integer> queue = new LinkedList<>();
    void enQueue(int n){
        queue.add(n);
    }
    int deQueue(){
        return queue.poll();
    }
}
</syntaxhighlight>
</syntaxhighlight>


523번째 줄: 457번째 줄:
[[파일:6n-graf.svg|섬네일|이런 거.]]
[[파일:6n-graf.svg|섬네일|이런 거.]]


함수의 그래프와는 전혀 다르다. 알고리즘을 배우다 보면 자주 나오는 구조로, 점들, 그리고 점들을 연결하는 선들만으로 이루어진 [[지도]] 비스무리한 걸 연상하면 편하다. 이때의 점을 '''정점(vertex)''', 선을 '''간선(edge)'''이라고 부른다.<ref>사실 우리가 수학에서 잘만 써먹던 대로 ‘꼭짓점’과 ‘변’이라고 하면 된다. 그거랑 정확히 똑같은 개념이다. 그런데 이상하게 이 동네에서는 ‘정점’과 ‘간선’이라는 새로운 용어를 쓴다. 이래 놓고 “수학에서 말하는 그래프랑 다르다”는 식으로 생각하는 바보도 있으니 기가 막힐 노릇이다. 그래프 이론은 엄연히 수학의 일부이다.</ref>
함수의 그래프와는 전혀 다르다. {{ㅊ|고등학교 교육과정에서 이산수학이 빠지고 행렬도 빠짐에 따라 같이 빠지게 되는 비운의 단원}} 알고리즘을 배우다 보면 자주 나오는 구조로, 점들, 그리고 점들을 연결하는 선들만으로 이루어진 [[지도]] 비스무리한 걸 연상하면 편하다. 이때의 점을 '''정점(vertex)''', 선을 '''간선(edge)'''이라고 부른다.<ref>사실 우리가 수학에서 잘만 써먹던 대로 ‘꼭짓점’과 ‘변’이라고 하면 된다. 그거랑 정확히 똑같은 개념이다. 그런데 이상하게 이 동네에서는 ‘정점’과 ‘간선’이라는 새로운 용어를 쓴다. 이래 놓고 “수학에서 말하는 그래프랑 다르다”는 식으로 생각하는 바보도 있으니 기가 막힐 노릇이다. 그래프 이론은 엄연히 수학의 일부이다.</ref>




809번째 줄: 743번째 줄:
{{YouTube|kPRA0W1kECg}}
{{YouTube|kPRA0W1kECg}}


{{ㅊ|뿅뿅뿅뿅뿅}} 일단 이걸 보고 뭘 배우면 좋을지 생각해보자. 참고로 이걸 돌릴 수 있는 프로그램이 [http://panthema.net/2013/sound-of-sorting 동영상에 적힌 웹사이트]에 있다. 직접 속도조절을 해보면서 뭐가 빠른지, 어떻게 작동하는지 등등을 유심히 관찰하자. {{--|근데 영어다.}} 참고로 소리를 직접 켜야 들을 수 있다.
{{ㅊ|뿅뿅뿅뿅뿅}} 일단 이걸 보고 뭘 배우면 좋을지 생각해보자. 참고로 이걸 돌릴 수 있는 프로그램이 [http://panthema.net/2013/sound-of-sorting 동영상에 적힌 웹사이트]에 있다. 직접 속도조절을 해보면서 뭐가 빠른지, 어떻게 작동하는지 등등을 유심히 관찰하자. {{--|[[영포자도 쉽게 알 수 있는 영어|근데 영어다.]]}} 참고로 소리를 직접 켜야 들을 수 있다.


영상을 보다보면 마지막의 보고(Bogo) 정렬은 결과를 보여주지 않는걸 보고 의아해 할 수도 있는데, 보고 정렬은 모든 값을 랜덤하게 줄세우는걸 반복하다보면 '''언젠가는''' 정렬이 된다는 식으로 동작하는 알고리즘이라서 그렇다(...) 알고리즘의 구성도 랜덤배치->정렬여부 확인의 무한루프. 당연하지만 효율성은 바닥을 긴다. 잘하면 한방에 정렬되지만, 영원히 정렬되지 않을 수도 있다. [[기글 하드웨어]]의 한 유저가 10개의 값으로 테스트하니 결과를 보기까지 '''4천만번'''의 읽기작업이 수행되었다고 한다.<ref>http://gigglehd.com/zbxe/10760945</ref><s>설마 이런데도 배우고 싶다고는 안하겠지?</s>
영상을 보다보면 마지막의 보고(Bogo) 정렬은 결과를 보여주지 않는걸 보고 의아해 할 수도 있는데, 보고 정렬은 모든 값을 랜덤하게 줄세우는걸 반복하다보면 '''언젠가는''' 정렬이 된다는 식으로 동작하는 알고리즘이라서 그렇다(...) 알고리즘의 구성도 랜덤배치->정렬여부 확인의 무한루프. 당연하지만 효율성은 바닥을 긴다. 잘하면 한방에 정렬되지만, 영원히 정렬되지 않을 수도 있다. [[기글 하드웨어]]의 한 유저가 10개의 값으로 테스트하니 결과를 보기까지 '''4천만번'''의 읽기작업이 수행되었다고 한다.<ref>http://gigglehd.com/zbxe/10760945</ref><s>설마 이런데도 배우고 싶다고는 안하겠지?</s>
리브레 위키에서의 모든 기여는 크리에이티브 커먼즈 저작자표시-동일조건변경허락 3.0 라이선스로 배포됩니다(자세한 내용에 대해서는 리브레 위키:저작권 문서를 읽어주세요). 만약 여기에 동의하지 않는다면 문서를 저장하지 말아 주세요.
글이 직접 작성되었거나 호환되는 라이선스인지 확인해주세요. 리그베다 위키, 나무위키, 오리위키, 구스위키, 디시위키 및 CCL 미적용 사이트 등에서 글을 가져오실 때는 본인이 문서의 유일한 기여자여야 하고, 만약 본인이 문서의 유일한 기여자라는 증거가 없다면 그 문서는 불시에 삭제될 수 있습니다.
취소 편집 도움말 (새 창에서 열림)

| () [] [[]] {{}} {{{}}} · <!-- --> · [[분류:]] · [[파일:]] · [[미디어:]] · #넘겨주기 [[]] · {{ㅊ|}} · <onlyinclude></onlyinclude> · <includeonly></includeonly> · <noinclude></noinclude> · <br /> · <ref></ref> · {{각주}} · {|class="wikitable" · |- · rowspan=""| · colspan=""| · |}

이 문서는 다음의 숨은 분류 1개에 속해 있습니다: