1. 퀵소트 설명

퀵소트에서 가장 중요한 것은 퀵소트를 이해하는것이다.

퀵소트는 이해하기 까다로운 알고리즘이다. 무서운 마음으로 한번 보자.

 

퀵소트는 분할 정복(divide and conquer) 개념을 사용한다.

그럼 무엇이 분할이고 무엇이 정복인지 알아보자.

 

 

우선 정복 부터 알아보자.

배열 = [5,2,4,1,3]

위와 같이 정렬되지 않은 배열이 있다.

위 배열중에서 일단 하나만 내자리를 찾자. 라고 생각하자

 

일단 하나만 내자리를 찾자의 단계는 아래와 같다.

1. 가장 우측의 인덱스로 피벗을 정한다(내자리를 찾을 놈)

2. 피벗기준으로 작은놈들은 왼쪽, 큰놈들은 오른쪽!

3. 2번 수행 시 자동적으로 자기 자리 fix 왜냐? 작은놈은 왼쪽에, 큰놈은 오른쪽에 있으니깐!

 

정복 예제

1. p == pivot

2. i == loop돌면서 p와 비교할 대상

3. j == left

[5, 2, 4, 1, 3]
i            p 
l

i(5)는 p(3)보다 작은가? → 아니요 아무것도 하지말고 i를 오른쪽으로 한칸 당겨라

[5, 2, 4, 1, 3]
 l  i        p 

i(2)는 p(3)보다 작은가? i를 l(left)와 바꾸고, i, l을 우측으로 한칸 이동해라

[2, 5, 4, 1, 3]
    l  i     p 

i(4)는 p(3)보다 작은가? → 아니요  아무것도 하지말고 i를 오른쪽으로 한칸 당겨라

[2, 5, 4, 1, 3]
    l     i  p 

i(1)는 p(3)보다 작은가? → 네  i를 l(left)와 바꾸고, i, l을 우측으로 한칸 이동해라

[2, 1, 4, 5, 3]
       l    i,p 

i(1)는 p(3)보다 작은가? → 네  i를 l(left)와 바꾸고, i, l을 우측으로 한칸 이동해라

 

 

i와 p가 같아졌다는 말은 비교 loop가 끝났다는 말이다.

이제 l(left)와 p을 바꾸자.

[2, 1, 3, 5, 4]
       p    i,l 

 

1차 정복이 완료된 모습이다.

피벗 3을 기준으로 왼쪽엔 작은것, 오른쪽엔 큰것이 놓여있다. 그리고 3은 자기자리를 잡았다.

 

여기서 생각을 멈춰라. 멈추고 분할을 생각해보자

도대체 무엇이 분할이란 말인가?

위의 정복 프로세스를 분할해서 다시 하겠다는 말이다.

어떻게? 피벗 기준으로 왼쪽으로 한번, 오른쪽으로 한번

 

 

분할

[2, 1, 3, 5, 4] // 3은 고정


[2, 1] // 왼쪽 배열
 i  p
 l
 
[5, 4] // 오른쪽 배열
 i  p
 l

왼쪽 배열을 위와같은 정복 프로세스를 돌리면 [1, 2] 배열이 완성되고

오른쪽 배열을 돌리면 [4, 5] 배열이 완성된다.

이렇게 하면 [1,2,3,4,5] 정렬된 배열이 완성된다.

 

분할의 핵심 개념은 정복 프로세스를 범위를 줄이며 반복한다. 즉 범위를 줄이면서 재귀적으로 정복 함수를 호출하자!

 

코드

class QuickSort {

    public static void swap(int[] arr, int a, int b) {
        int temp = arr[b];
        arr[b] = arr[a];
        arr[a] = temp;
    }          

    public static void recursiveSearch(int[] arr, int left, int right) { // 분할
        if (left >= right) {
            return;
        }

        int pivotIndex = partition(arr, left, right);

        recursiveSearch(arr, left, pivotIndex - 1);
        recursiveSearch(arr, pivotIndex + 1, right);

    }


    public static int partition(int[] arr, int left, int right) { // 정복
        int i = left - 1;

        while (i < right) {
            ++i;
            if (arr[i] < arr[right]) {
                swap(arr, i, left);
                left++;
            }
        }

        swap(arr, left, right); // 좌랑 우랑 바꿔서 피벗은 자기자리 확정!

        return left;

    }

}

 

출처 : https://core.ewha.ac.kr/publicview/C0101020140321143516139010?vmode=f

스레드는 왜있을까?? 프로세스는 실행중인 프로그램을 프로세스라고 한다. 이 프로세스(프로그램)은 졸라게 복잡한 계산을 필요로 한다고 가정해보자. 어떻게 컴퓨터 운영체제의 부하를 줄이면서 최대한 빠르게 계산을 처리할 수 있을까??

 

바로 스레드를 사용하면 된다. 위의 그림을 간략히 설명하면 Code 영역에 실행해야할 명령어 집합(instruction)이 들어있고 data영역에 전역변수와 같은 변수들이 들어 있다. 계산을 할 때는 CPU만 졸라게 빨리 움직이면 되기 때문에 Code와 data영역은 공유하고 다음에 수행해야할 명령어 주소를 가르키는 PC 값을 각각 쓰레드에 매핑 시킨 후 계산을 처리하면 된다! (뒤에 좀 더 자세히)

 

 

출처 : https://core.ewha.ac.kr/publicview/C0101020140321143516139010?vmode=f

그래서 Thread는 하나의 프로세스에서 독립된 PC, register set, stack space를 가지며 code와 data, OS resource를 공유한다.

 

다음에 수행할 명령어(instruction)를 가르키는 PC값과 CPU 연산을 위해 필요한 임시 저장 공간 register, 함수를 실행하기 위한 고유의 stack 영역이 스레드마다 각각 필요하다는 말이다.

 

스레드를 좀 더 자세히 기술하면 하나의 스레드가 blocked(waiting) 상태인 동안에도 동일한 태스크 내의 다른 스레드가 실행되어 좀 더 빠르게 처리를 할 수 있다는 뜻이다.

 

하나의 스레드가 blocked 되었단 말은 I/O 작업 같이 오랜 시간이 걸리는 작업을 할 때를 뜻한다. 예를 들면 웹 브라우저에서 고용량 이미지를 받아올때다.

 

동일한 태스크 내(하나의 브라우저 안에서) 다른 스레드가 실행 된다(고용량 이미지를 받아오지만, 이미지를 받아오는 I/O 스레드가 blocked될 동안 일반 텍스트를 Display 해주는 스레드는 일함) 

 

 

출처 : https://core.ewha.ac.kr/publicview/C0101020140321143516139010?vmode=f

위의 그림은 싱글스레드와 멀티스레드의 차이를 그림으로 보여준다.

 

끝!

 

ALU(Arithmetic Logic Unit)

CPU는 덧셈이나 뺄셈과 같은 연산의 주체다 이건 누구나 다 아는 사실 하지만 좀 더 깊게 들어가면 ALU라는 녀석이 실제 연산을 담당한다. ALU 연산은 크게 두가지로 나뉜다 하나는 덧셈이나 뺄셈 같은 산술연산, 하나는 AND와 OR 같은 논리 연산이다.

 

 

Control Unit

프로그램을 컴파일하면 실행파일이 되고 이 실행파일에는 CPU에게 일을 시키는 명령어가 저장되어 있다.

이 명령어가 CPU 내부의 ALU로 전송되었다고 가정하자. 명령어는 다 0과 1로 구성되어있다(당연하다 컴퓨터는 0과 1밖에 모르니) 32비트 명령어라면 "00001111 00001111 00001111 00001111"와 같이 구성되어 있을 것이다.

과연 ALU가 0과 1로 구성되어있는 명령어를 이해할 수 있나? 못한다 ALU는 연산만 하니깐 이명령어를 해석해주는 놈이 바로 컨트롤 유닛이다.

 

 

레지스터(Register)

명령어가 CPU로 들어왔다고 가정하자. 덧셈 명령어 그리고 덧셈에 필요한 피연산자. 명령어는 컨트롤 유닛으로 피연산자는 ALU로 보내면된다. 하지만 ALU나 컨트롤 유닛이 지금 다른 명령어를 해석하고 있다면?? CPU 내부에 데이터를 저장해두고 CPU가 필요할 때 직접 가져다 쓰면 좋을 것 같다. 그렇다 CPU 내부의 조그마한 메모리 공간을 레지스터라고 한다.(연산할때 일일이 메모리에서 가져다가 연산하면 속도가 개판된다)

 

 

버스 인터페이스(Bus Interface)

명령어와 데이터가 CPU로 어떻게 들어 왔을까? 바로 버스 인터페이스 때문이다. 서로 데이터를 주고 받기 위해서 어떤 매개체가 필요하다 그것이 바로 버스 인터페이스다.

'컴퓨터 기본' 카테고리의 다른 글

퀵소트 자바  (0) 2021.05.26
Thread 란?  (0) 2020.05.07
클래스 관계 HAS-A 상속(합성, 통합)  (0) 2019.11.08
class의 static이란?(실무에서 바로 통하는 자바)  (0) 2019.10.28
유니코드 vs 아스키코드  (0) 2019.06.14

HAS-A는 뭐냐? ~이 ~을 가진다. 컴퓨터는 CPU와 RAM을 가진다, 경찰은 총을 가진다.

HAS의 의미 그대로 "가진다"의 뜻이다.

HAS-A 관계는 두가지의 종류가 있는데 하나는 합성(composition) 다른 하나는 통합(aggregation)이다.

합성관계는 없어서는 안될 아주 강한 결합 상태고, 통합은 없어도 괜찮아~ 정도의 약한 결합 상태이다.

 

합성의 예를 들면 컴퓨터와 CPU의 관계이다. 컴퓨터는 CPU 없이는 존재할 수 없다. 왜냐? 아무것도 못하니깐

통합의 예는 경찰과 총의 관계이다. 근육맨 경찰들은 총이 없어도 그만이지 않는가

 

1. 합성의 예시

합성의 관계

class CPU:
    print('빵빵한 CPU')


class RAM:
    print('든든한 RAM')

class Computer:
    def __init__(self):
        self.cpu = CPU()
        self.ram = RAM()
        print('CPU와 RAM 없이는 존재 할 수 없으니 나는 합성 관계야!!')

computer = Computer()

 

2. 통합의 예시

통합의 관계

class Gun:
    def __init__(self, gun):
        self.gun = gun

    def bbang(self):
        print(self.gun + ' 빵야 빵야')


class Police:
    def __init__(self):
        self.gun= None

    def get_gun(self, gun):
        self.gun = gun

    def fire_gun(self):
        if self.gun:
            self.gun.bbang()
        else:
            print('총 없다 손으로 맞자')


police = Police()
police.fire_gun()
police.get_gun(Gun('M16'))
police.fire_gun()

통합의 예시는 이미 설명했듯이 있어도 그만 없어도 그만이다. 경찰이 총이없으면 손으로 총이 있으면 총으로!(경찰이 M16을 들면 일반 시민은 바로 복종할 듯 하다)

'컴퓨터 기본' 카테고리의 다른 글

Thread 란?  (0) 2020.05.07
컴퓨터 하드웨어 구성(CPU)  (0) 2020.02.11
class의 static이란?(실무에서 바로 통하는 자바)  (0) 2019.10.28
유니코드 vs 아스키코드  (0) 2019.06.14
운영체제 32bit vs 64bit  (0) 2019.06.14

이상하게 프로그래밍 하면서 static을 많이 쓸 일이 많이 없었다. 내가 하수라서 쓸 타이밍을 모르는건지 자주 안쓰이는 건지는 모르겠지만 한번씩 static이 나올때마다 약간의 두려움을 없애고자 이 글을 작성한다(실무에서 바로 통하는 자바를 읽다가)

 

 

static이란?

인스턴스들은 똑같은 클래스를 바탕으로 하더라도 인스턴스마다 내부의 변숫값과 메서드의 행동이 다르지만 static을 붙이면 변수와 메서드를 클래스에서 유일한 것으로 다룰 수 있다

static 변수는 클래스에서 인스턴스를 몇 개 만들어도 메모리에는 단 하나만 생성된다. static 변수는 모든 인스턴스에서 같은 값을 공유한다.

 

아래의 그림을 보다싶이 Dog Class의 인스턴스들은 각각의 name을 가지지만 type은 포유류 단 하나다. 왜냐? static이 붙었으니깐! 내 생각엔 클래스가 인스턴스 될때 name과 같은 인스턴스 변수들은 메모리에 할당될때 새롭게 생성+할당되지만 type과 같은 static변수는 기본값으로 static 변수의 주소값 즉 포인터 변수 같은 그런 느낌이 살짝쿵 든다. 

 

실무에서 바로 통하는 자바

 

 

아래의 그림은 static 변수를 변경했을때다 모든 인스턴스 type 변수는 static 변수를 Point 함으로 한놈이 변경하면 바로 모든 인스턴스에 영향을 미친다. 느낌이 그냥 딱 포인터다

실무에서 바로 통하는 자바

 

그리고 static 메서드를 사용할떄는 인스턴스화 안해도 쓸수 있다. 사용법 : 클래스명.메서드명(인수) 이건 뭐 다 아는사실이니.

 

마지막으로 JAVA 초보지만 System.out.println의 out은 System 클래스의 static 변수이고, PrintStream 클래스의 인스턴스다. 결과적으로 System.out.println은 PrintStream 객체의 메서드인 println을 실행하는 것이다. 이 말이 무엇이냐? 지금 System 클래스에서 바로 out을 사용했다.. System은 클래슨데?? 인스턴스도 안만들었는데?? 그렇다 static이니깐! 

'컴퓨터 기본' 카테고리의 다른 글

Thread 란?  (0) 2020.05.07
컴퓨터 하드웨어 구성(CPU)  (0) 2020.02.11
클래스 관계 HAS-A 상속(합성, 통합)  (0) 2019.11.08
유니코드 vs 아스키코드  (0) 2019.06.14
운영체제 32bit vs 64bit  (0) 2019.06.14

0과 1밖에 모르는 컴퓨터에 문자를 인식 시키려면 어떻게 해야할까?

 

문자를 0과 1로 이루어진 2진수로 나타내면 된다. 문자 하나에 정수 하나를 매핑해 두면 이 정수는 특정 문자를 표현한다는 것 을 알수있다. 이렇게 매핑된 정수를 코드 포인트라고 하며 문자와 문자에 매핑된 코드 포인트를 모아 놓은 집합을 부호화된 문자 집합이라고 한다. 

 

역시 무슨말인지 하나도 모르겠다.

 

아스키 코드, 유니코드

 

 - 아메리칸인들을 위한 대표적인 문자 인코딩 방식이다. 비트 일곱개로 문자를 표현하고 문자를 총 128개 까지 표현할 수 있다. 그말은 코드 포인트도 128개란 말이다.(0000000 ~ 1111111)

정수하나에 문자를 매핑시킨다, 코드 포인트는 아래의 표를 보면 정확히 알 수 있다

110 0001의 정수(2진수로)를 아스키코드 문자로 나타낸다면(매핑) a인것이다

110 0001을 코드 포인트라고 부른다.

파이썬으로 나타낸 아스키코드

 

아스키코드 표

 

다시 아스키코드로 돌아가서 아스키코드는 한글을 표현할 수 없다 왜냐하면 아스키코드를 만든 제작자가 아주 이기적이기 때문에 숫자와 영어만을 고려해서 만든것이다. 그래서 나온게 바로 유니코드이다. 유니코드는 7비트만을 이용해서 문자 집합을 표현하는 아스키와 달리 16비트로 확장해서 65,536개의 문자를 표현할 수 있게 확장한 버전인것이다.

이로서 한글을 나타낼 수 있게되었다! 

 

유니코드는 위와 위사한 표를 제공하는데 한글은 16비트로 AC00 ~ D7AF까지 자리 잡고있다

AC00은 '가'를 나타내는 16진수 코드포인트이다.

파이썬에서 AC00을 쳤을때 '가' 출력

자 이제 문자와 코드포인트의 관계를 매핑하는 표에 대해서도 알아봤다. 하지만 진짜 중요한건 인코딩이다

 

유니코드 인코드 방식

 - 코드 유닛코드포인트를 특정한 방법으로 인코딩했을때 변환되어 얻어지는 비트의 나열이다.

이말은 무엇이냐 UTF-8로 인코딩을 하면 코드 유닛은 UTF-8의 알고리즘에 의해 인코딩 된 코드 유닛을 뱉고 UTF-16으로 인코딩하면 UTF-16의 알고리즘에 의해 인코딩 된 코드 유닛을 뱉는다는 말이다

인코딩 방식에 따라 코드 유닛이 상이함

 

문자 '가'를 가지고 어떤것으로 인코딩 하냐에따라 결과물(코드 유닛)이 달라진다.

 

이제 이 코드 유닛을 어디에 쓰는가? 이것만 확인하면 정말 핵심은 끝이다.

  ※ UTF-8 , UTF-16의 변환법은 각각 다르고 어려우니 PASS

UTF-8 표

 

'가'만 보자 제일 왼쪽에 UTF8: 234, 176, 128이라고 나오고 그다음으로 UNICODE: AC0이라고 나오는 것을 볼 수 있다.

위의 '가'를 UTF-8으로 인코딩하면 0xea 0xb0 0x80이라는 16진수가 튀어나온다.

이를 다시 정수로 변환하면 234, 176, 128이 나온다 그렇다 UTF-8표에 나오는 바로 그 숫자이다.

UTF-8표에 매핑하는 방법

이런식으로 인코딩을 해서 나오는 값(코드 유닛)을 UTF-8표에 매핑시켜서 출력 시켜주는 것이다.

 

끄읏!!

운영체제를 선택할때 32bit와 64bit를 선택한다

많은 사람들이 32bit는 4GB까지 밖에 메모리 지원이 되지 않는걸 안다

하지만 왜?! 도대체 왜 그런지 아는 사람은 5%미만 일 것이다 심지어 전산실에 근무하고있는 사람 조차 모르는 사람이 있다..(IT 전공자가 모른다면 문제가 있다.)

 

1. 32bit와 64bit의 의미

 가. 속도의 측면에서: 데이터를 한 번에 몇 개 보낼 수 있는지를 나타내는 지표가 32비트와 64비트의 차이이다. 흔히 고속도로에 비유해서 32비트는 차선이 32개인 차선이고 64비트는 64개의 차선이라고 표현한다. 그렇다 데이터를 2배나 더 많이 보낼 수 있기 때문에 속도가 향상된다.

 

 나. 메모리의 측면에서: 32비트 컴퓨터는 메모리 주소를 32비트로 표현하고, 64비트 컴퓨터는 메모리 주소를 64비트로 표현한다. 무슨 말인고 하니 32비트 컴퓨터는 2의 32승 = 4,294,967,296 = 4 x 1024 x 1024 x 1024 바이트 즉 4GB 메모리를 가리킬 수 있는 주소를 가지게 됩니다. 이러한 이유로 32bit 컴퓨터는 4GB 이상의 메모리를 지원 할 수 없다는 이유이다!

64비트는 물론 2의 64승 = 18,446,744,073,709,551,616바이트 까지 지원한다. 수가 너무 커진다 그냥 꼽으면 꼽는데로 메모리를 늘릴 수 있다고 생각하자.

 

그래서 32bit를 쓸때 포인터 변수의 크기는 32bit 즉 4바이트고 64bit일때는 64bit 즉 8바이트다

 

 

2. 메모리에 대하여(가르킨다, Point!)

 - 설명하기 쉽게 8bit 컴퓨터로 메모리에 대해서 이야기 해보자 한다

 

8비트 컴퓨터니깐 요즘 많이쓰는 32bit, 64bit 컴퓨터에 비해서 속도가 느리고(속도적 측면) 수용할 수 있는 메모리도 작을것이다(메모리 측면).

 

8비트 컴퓨터는 2의 8승 = 256바이트를 표현 할 수 있다.(메모리의 주소는 0000 0000부터 1111 1111까지)

  ※ 아래의 그림을 보면 메모리 주소 한 개는 1바이트를 가리킨다 

3. 메모리 주소는 16진수로

 - 컴퓨터가 0과 1로 이루어진 2진수로 표현된다고 했는데 정작 메모리 주소를 이야기할때는 16진수로 이야기한다. 16진수가 좀 더 멋져서 폼잡을라고 쓰는가 했는데 16진수를 쓰면 2진수보다 짧고 간결하게 표현 할 수 있기 때문에 쓴다는걸 알았다.(사람은 배워야한다)

 

위의 8비트를 표현할때 메모리주소를 표현하려면 0000 0000 8자리가 필요하지만 16진수를 쓰면 0x00으로 두자리로 간단하게 표현된다. 

 

아래는 8비트의 2진수 0000 0001을 hex 즉 16진수로 바꿔보았다 0x01 짧아서 좋다...

 

+ Recent posts