LRU 알고리즘

가장 오랫동안 참조되지 않은 페이지를 교체하는 기법

 

 

 

페이지 교체 알고리즘

페이징 기법으로 메모리를 관리하는 운영체제에서

새로운 페이지를 할당하기 위해 현재 할당된 페이지 중 어느 것과 교체할 지를 결정하는 방법

 

종류 : FIFO, LFU, LRU

 

1) FIFO : 페이지가 주기억장치에 적재된 시간을 기준으로 교체될 때 페이지를 선정하는 기법

2) LFU : 가장 최근에 사용한 프로그램 중 적은 횟수로 참조하는 페이지 교체

3) LRU : 가장 오랫동안 참조되지 않은 페이지 교체

 

 

LRU 알고리즘

[ 예시 ]

Input : 12345

Output : 5413

위의 그림에서 참조 스트링이 입력되고, 이에 따른 주기억 장치의 상태를 보면 됩니다.

 

- 4초 : 1 이 재참조되어 순서가 변경됩니다.

- 6초 : 캐시사이즈가 가득차서 가장 오랫동안 참조되지 않은 2를 제거 후 저장합니다.

 

 

[ 문제 ]

캐시 크기가 주어지고, 캐시가 비어 있는 상태에서 N개의 작업을 CPU가 차례대로 처리한다면 N개의 작업을 처리한 후

캐시 메모리 상태를 가장 최근 사용된 작업부터  차례대로 출력하는 프로그램을 작성하시오.

 

- 입력

1) 첫 번째 줄에 캐시 크기 (S) 와 작업 개수 (N) 이 입력됨

2) 두 번째 줄에 N 개의 작업 번호가 처리순으로 주어짐 ( 1 ~ 100 )

 

- 출력

마지막 작업 후 캐시메모리의 상태를 가장 최근 사용된 작업부터 차례로  출력

 

[ 예시 입력 ]

5 9

1 2 3 2 6 2 3 5 7

[ 과정 ]

1) 1

2) 2 1

3) 3 2 1

4) 2 3 1

5) 6 2 3 1

6) 2 6 3 1

7) 3 2 6 1

8) 5 3 2 6 1

9) 7 5 3 2 6

 

[ 예시 출력 ]

7 5 3 2 6

 

[ 소스코드 ]

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class LRU {
    public static void main(String[] args) {
        Scanner kb = new Scanner(System.in);
        int s = kb.nextInt();
        int n = kb.nextInt();
        List<Integer> arr = new ArrayList<>();
        for (int i = 0; i < n; i++) {
            arr.add(kb.nextInt());
        }
        myLRU(s, arr);
    }

    public static void myLRU(int s, List<Integer> arr) {
        List<Integer> temp = new ArrayList<>();
        int cnt = 0;
        boolean check = false;

        while (cnt < arr.size()) {
            // 이미 숫자가 있는지 확인
            for (int i = 0; i < temp.size(); i++) {
                if (temp.get(i) == arr.get(cnt)) {
                    temp.remove(i);
                    temp.add(0,arr.get(cnt));
                    check = true;
                    break;
                }
            }
            if(!check){
                temp.add(0,arr.get(cnt));
            }
            // 최대 크기를 넘은 경우
            if(temp.size() > s){
                temp.remove(temp.size() - 1);
            }
            cnt++;
            check = false;
        }
        
        for (Integer i : temp) {
            System.out.print(i + " ");
        }
    }
}

'알고리즘' 카테고리의 다른 글

다항식 프로그램 ( c++ )  (0) 2022.10.14
시뮬레이션과 완전탐색  (0) 2022.10.10
DFS 코드 이해하기 ( java )  (1) 2022.10.06
Dynamic Programming  (0) 2022.09.26
후위 표기식  (0) 2022.09.25

[ 문제 ]

다항식 프로그램 작성

 

[ Input ]

입력에는 두 개의 식이 순서대로 입력된다. 첫 번째 식은 첫 줄에는 항의 개수 n (1<=n<=20)이 들어온다.  
다음 줄 부터는 최고차항 순서로 (계수, 차수)가 들어오며 공백으로 구분된다. 두 번째 식도 같은 방법으로 입력된다.

 

[ Output ]

먼저, 두 개의 식을 “(항의 개수) = 다항식의 형식으로 각 줄에 출력한다. 이때 Sample Output에 맞추어 공백을 구분하는데 유의한다. 각 계수는 소수점 첫째자리 까지 출력하고 계수가 0인 항은 출력하지 않는다 (Sample Output 참고).
그리고 마지막줄에는 두 개의 다항식을 더하여 하나의 다항식으로 출력한다.

 

Sample Input

3
3 12
2 8
1 0
4
15 11
14 3
2 2
1 1

 

Sample Output

(3) = 3.0 x^12 + 2.0 x^8 + 1.0 x^0
(4) = 15.0 x^11 + 14.0 x^3 + 2.0 x^2 + 1.0 x^1
(7) = 3.0 x^12 + 15.0 x^11 + 2.0 x^8 + 14.0 x^3 + 2.0 x^2 + 1.0 x^1 + 1.0 x^0

 

Sample Input

4
3 12
2 8
10 3
1 0
3
15 12
-14 8
2 3

 

Sample Output

(4) = 3.0 x^12 + 2.0 x^8 + 10.0 x^3 + 1.0 x^0
(3) = 15.0 x^12 + -14.0 x^8 + 2.0 x^3
(4) = 18.0 x^12 + -12.0 x^8 + 12.0 x^3 + 1.0 x^0

 

Sample Input

4
10 5
32 4
2 3
5 2
5
1 12
2 10
3 5
4 3
5 2

 

Sample Output

(4) = 10.0 x^5 + 32.0 x^4 + 2.0 x^3 + 5.0 x^2
(5) = 1.0 x^12 + 2.0 x^10 + 3.0 x^5 + 4.0 x^3 + 5.0 x^2
(6) = 1.0 x^12 + 2.0 x^10 + 13.0 x^5 + 32.0 x^4 + 6.0 x^3 + 10.0 x^2

 

 

 

[ 소스코드 ]

#include <cstdio>
#include <iostream>

using namespace std;

#define MAX_DEGREE 80

int degreeArr[MAX_DEGREE]; // degree 입력 배열

class polynomial{
    float coef[MAX_DEGREE]; // 계수 입력 배열 
    int polyNum[MAX_DEGREE]; // 차수 입력 배열

    public:
    int degree;
    polynomial(){
        degree = 0;
    }

    void read(int num){
        scanf("%d",&degree);
        degreeArr[num] = degree;
        for(int i=0; i<degree; i++){
            scanf("%f %d",coef+i, polyNum+i);
        }
    }

    void display(int num){
        printf("(%d) = ",num);
        for(int i=0; i<degree; i++){
            if(i == degree-1){
                printf("%.1f x^%d", coef[i], polyNum[i]);
            }
            else printf("%.1f x^%d + ", coef[i], polyNum[i]);
        }
        printf("\n");
    }

    void add( polynomial a, polynomial b){

        /* 
        차수가 큰 다항식을 자신으로 복사 후 나머지 계수를 자기 계수 배열에
        차수를 맞춰 더한다.

        - 해결할 문제
        1) degree 순이 아닌 높은 차수 순으로 정렬

        - 해결 과정
        1) c에 a를 복사한다.
        2) b의 차수 배열과 c의 차수 배열을 비교해서 c에 넣어준다.
        3) 넣어주는 조건
            > c 차수가 b의 차수보다 큰 경우 b의 해당 원소 앞에 넣어준다.
            > c 차수 배열의 마지막 원소보다 b의 차수가 작다면 뒤로 배열을 다 넣어준다.
            > c 차수와 b차수가 같은 경우 b의 해당 계수을 더해준다.
        */
        *this = a; // c = a의 degree 와 polyNum 이 복사됨
        int degreeCnt = 0;

        for(int i=0; i<b.degree; i++){ // b를 순환

            for(int j=0; j<degree; j++){ // c에서 제 자리를 찾기 위해 c를 순환
                if(polyNum[j] < b.polyNum[i]){ //b의 차수가 c의 차수보다 큰 경우
                    int insertPoef = b.polyNum[i];
                    int insertCoef = b.coef[i];
                    for(int k=degree; k >= j; k--){
                        coef[k+1] = coef[k];
                        polyNum[k+1] = polyNum[k];
                    }
                    polyNum[j] = insertPoef;
                    coef[j] = insertCoef;
                    degreeCnt++; // degree 증가 카운트를 해놓고 반복문이 끝나면 degree에 더해준다.
                    j = j+1; // 자릿수가 늘어남에 따라 j도 증가
                    break;
                }
                else if(polyNum[j] == b.polyNum[i]){ //b와 c의 차수가 같은 경우
                    coef[j] += b.coef[i];
                    break; // 더 해주는게 반복되지 않게 종료시켜줌
                }
            }
            degree += degreeCnt;
            degreeCnt = 0;
        }
    }

    bool isZero(){
        return degree == 0;
    }

    void negate(){
        for(int i=0; i<degree ;i++) coef[i] = -coef[i];
    }
};

int main(){
        polynomial a,b,c;
        a.read(0);
        b.read(1);
        c.add(a,b);

        int num = c.degree;

        a.display(degreeArr[0]);
        b.display(degreeArr[1]);
        c.display(num);
        return 0;
}

 

 

 

'알고리즘' 카테고리의 다른 글

LRU ( Least Recently Used Algorithm )  (1) 2022.11.17
시뮬레이션과 완전탐색  (0) 2022.10.10
DFS 코드 이해하기 ( java )  (1) 2022.10.06
Dynamic Programming  (0) 2022.09.26
후위 표기식  (0) 2022.09.25

시뮬레이션

문제에서 제시한 알고리즘을 한 단계씩 차례대로 직접 수행하는 것

 

 

종류

  • 알고리즘은 간단한데 코드가 지나치게 긴 문제
  • 실수 연산을 다루고 특정 소수점 자리까지 출력해야 하는 문제
  • 문자열을 특정한 기준에 따라 끊어 처리해야 하는 문제
  • 적정한 라이브러리를 찾아서 사용해야 하는 문제

 

특징

일반적으로 2차원 공간을 다루는 문제가 많이 나온다.

2차원 공간을 다루기 위해 행렬 개념을 사용하며, 이차원 공간에서의 방향 벡터가 자주 나옴

 

 

 

완전탐색

모든 경우의 수를 다 계산하는 방법

완전 탐색 알고리즘은 비효율적인 시간 복잡도를 가지므로 데이터 개수가 100만개 이하일 때 적절합니다.

 

 

 

 

 

구현 : [ 완전 탐색, 시뮬레이션 ]

구현으로 묶이는 알고리즘 유형은 모든 경우를 하나씩 직접 수행해서 결과를 도출하는 것이 답이 잘 나옵니다.

그래서 머리속으로 생각하는 것을 코드로 잘 표현할 수 있는가가 풀이의 핵심 입니다.

'알고리즘' 카테고리의 다른 글

LRU ( Least Recently Used Algorithm )  (1) 2022.11.17
다항식 프로그램 ( c++ )  (0) 2022.10.14
DFS 코드 이해하기 ( java )  (1) 2022.10.06
Dynamic Programming  (0) 2022.09.26
후위 표기식  (0) 2022.09.25

이번 블로깅은 DFS 코드에 대해 분석하는 내용이라 DFS에 대한 개념을 어느 정도 아는 상태여야 이해가 됩니다.

 

1. 그래프의 탐색

그래프의 탐색은 하나의 정점에서 모든 정점을 차례로 한 번씩 방문하는 것을 의미합니다.

그 알고리즘은 DFS 와 BFS가 있습니다.

 

 

 

2. DFS ( Depth First Search ) - 깊이 우선 탐색

루트 노드나 임의의 노드에서 시작해 깊숙히 탐색한 다음 다시 원점으로 돌아가 다른 루트를 탐색하는 방법입니다.

즉, 다음 노드로 넘어가기 전에 완전 탐색을 하는 방법입니다.

 

일반적으로 재귀, 스택, 인접 행렬, 인접 리스트를 이용해 구현합니다.

 

주로, 미로 탐색과 같이 한 방향으로 계속 탐색해야 하는 경우에 사용되며, 만약 막 다른 곳에 도달하면 그 길에 표식을 남기고

가장 가까운 갈림길로 돌아와 다시 탐색을 진행합니다.

이러한 방법으로 갈림길을 순차적으로 탐색해 목적지까지의 경로를 구합니다.

 

 

 

 * 특징

1) 깊게 탐색

2) 모든 노드를 방문하고자 하는 경우에 사용

3) 노드의 방문 여부를 반드시 검사해야함 ( 그렇지 않으면 무한 루프에 빠질 수 있음 )

4) BFS 에 비해 구현이 쉬움

5) BFS 에 비해 느림

6) 해가 없는 경우에 대한 예외 발생 코드가 필요

7) 해를 구한다고 해도 최단 경로라는 보장은 못함

 

 

 

위의 그래프를 직접 만들고, DFS를 진행해본다고 가정하고 코드를 작성해 보겠습니다.

 

 

[ 소스코드 ]

public class graphDFS {
    private int nV; //정점
    private int[][] dfsGraph; //간선
    private boolean[] visitArr; //방문한 배열

    public graphDFS(int nV) {
        this.nV = nV;

        this.dfsGraph = new int[this.nV + 1][this.nV + 1];
        this.visitArr = new boolean[this.nV + 1];
    }

    // 그래프 리턴
    public int[][] getGraph() {
        return this.dfsGraph;
    }

    // 양방향 그래프 추가
    public void put(int x,int y) {
        this.dfsGraph[x][y] = 1;
        this.dfsGraph[y][x] = 1;
    }

    // 단방향 그래프 추가
    public void putSingle(int x, int y) {
        this.dfsGraph[x][y] = 1;
    }
    //그래프 출력
    public void printGraphToAdjArr() {
        System.out.println("\n"+"[그래프 출력]");
        for (int i = 1; i < dfsGraph.length; i++) {
            for (int j = 1; j < dfsGraph[i].length; j++) {
                System.out.print(this.dfsGraph[i][j]+" ");
            }
            System.out.println();
        }
    }

    // 그래프 탐색
    public void dfs(int vIdx) {
        visitArr[vIdx] = true;
        System.out.print(vIdx + " ");
        for (int i = 1; i <= nV; i++) {
            if(dfsGraph[vIdx][i] == 1 && visitArr[i] == false){
                dfs(i);
            }
        }
    }

    public static void main(String[] args) {
        int nV = 8;

        graphDFS graph = new graphDFS(nV);

        graph.put(1, 2);
        graph.put(1, 3);
        graph.put(2, 4);
        graph.put(2, 5);
        graph.put(3, 6);
        graph.put(3, 7);
        graph.put(4, 8);
        graph.put(5, 8);
        graph.put(6, 8);
        graph.put(7, 8);

        graph.dfs(1);
        graph.printGraphToAdjArr();

    }
}

[ dfs 함수 설명 ]

- visitArr : 탐색을 했다는 걸 알 수 있는 배열

- visitArr[vIdx] = true  : true가 되면 탐색 한 것

- if ( dfsGraph [ vIdx ] [ i ] == 1 : 방문한 인덱스에 다음 정점까지의 경로가 있는 경우

- visitArr[i] : 방문할 정점의 경로가 방문한 적이 없는 경우

- dfs (i) 재귀를 통해 방문한다.

'알고리즘' 카테고리의 다른 글

다항식 프로그램 ( c++ )  (0) 2022.10.14
시뮬레이션과 완전탐색  (0) 2022.10.10
Dynamic Programming  (0) 2022.09.26
후위 표기식  (0) 2022.09.25
decryptCaesarCipher  (0) 2022.08.09

Dynamic Programming

큰 문제를 작은 문제로 나누어 푸는 문제

 

 

Divide and Conquer ( 분할정복 ) 과 차이점

작은 문제가 중복되어 일어나는지 안일어나는지입니다.

 - 분할 정복 : 큰 문제를 해결하기 어려워 작은 문제로 나누어 푸는 방법 ( 작은 문제에서 반복은 없음 )

 - 동적 프로그래밍 : 작은 문제들이 반복되는 것 ( 답은 바뀌지 않음 )

 

 

Dynamic Programming 방법

모든 작은 문제들은 한번만 풀어야 합니다.

정답을 구한 작은 문제를 어딘가에 메모해놓고, 다시 그보다 큰 문제를 풀어나갈 때 똑같은 작은 문제가 나타나면

메모한 작은 문제의 결과값을 이용합니다.

 

 

Dynamic Programming 조건

1) 작은 문제가 반복일 일어날 경우

2) 같은 문제는 구할 때마다 정답이 같음

 

 

Memoization ( 메모이제이션 )

작은 문제들이 반복되고, 이 작은 문제들의 결과값은 항상 같다는 점을 이용해 한번 계산한 작은 문제를 저장해놓고 다시 사용하는 것을 의미합니다. 피보나치로 예를 들 수 있습니다.

 

 

[ 피보나치 수열 예제 ]


피보나치는 1, 1, 2, 3, 5, 8 .. 의 수를 이루게 됩니다. 재귀로 풀게 될 경우 했던 작업을 또 해야하므로 일정 수 이상의 순열을 구하기가 어렵습니다.

 - 점화식 : [이전 수열] +  [두 단계 전 수열의 합]


 - DP 조건이 해당되는지 확인
     1) 작은 문제들이 반복된다.
         : F(5) = F(4) + F(3) , F(4) = F(3) + F(2) 로 F(3) 이라는 수열이 F(4), F(5) 에서 반복됩니다.
      2) 같은 문제를 구할때 마다 정답이 같다.
         : 첫번째, 두번째 수열은 각각 1로 고정되어 있습니다. 즉, 3번째 수열은 항상 결과가 2입니다. 그리고 4번째 수열은 3번째와 2번째 

           수열을 이용해 구하므로 언제나 정답이 같습니다.

 

public class fibonacci { 
    static long[] memo;
    public static int fibonacci_rec(int n) { // 재귀
        if (n <= 1) {
            return n;
        } else {
            return fibonacci_rec(n-1) + fibonacci_rec(n-2);
        }
    }

    public static long fibonacci_memoization(int n) { // 메모이제이션
        if (n <= 1) {
            return n;
        }
        else if(memo[n] != 0){
            return memo[n];
        }
        else {
            return memo[n] = fibonacci_memoization(n - 1) + fibonacci_memoization(n - 2);
        }
    }
    public static void main(String[] args) {
        memo = new long[10];
        System.out.println(fibonacci_rec(10));
        System.out.println(fibonacci_memoization(10));

    }
}

 

 

[ 재귀와의 차이점 ]

기존 재귀함수와는 다르게 이미 계산된 값을 Memo 라는 배열에 저장하여 사용한다는 차이점이 있습니다.

 

 

 

Bottom-up , Top-down

 

 1. Bottom-up

작은 문제부터 구해가는 방법입니다.

    public static int bottomUp(int n){
        int[] arr = new int[n];
        arr[0] = 0;
        arr[1] = 1;
        if(n==0) return arr[0];
        else if(n==1) return arr[1];
        else{
            for(int i=2; i<=n; i++){
                arr[i] = arr[i - 1] + arr[i - 2];
            }
            return arr[n];
        }
    }

 

 2. Top-down

 재귀함수로 구현하는 경우 대부분이 top-down이라고 생각하면 됩니다. 큰 문제를 풀 때 작은 문제가 아직 풀리지 않았다면 그제서야 작은 문제를 해결하게 됩니다.

 

 

* Top-down / Bottom-up 의 장점

 - Top-down : 소스의 가독성이 증대

 - Bottom-up : 풀기는 쉽지만 소스의 가독성이 안좋음

'알고리즘' 카테고리의 다른 글

시뮬레이션과 완전탐색  (0) 2022.10.10
DFS 코드 이해하기 ( java )  (1) 2022.10.06
후위 표기식  (0) 2022.09.25
decryptCaesarCipher  (0) 2022.08.09
인접 행렬 길찾기  (0) 2022.08.04

- 조건 :

 

 

- 작성 코드 :

import java.util.ArrayList;
import java.util.Scanner;
import java.util.Stack;

public class postfix {

    public static Stack<Character> stack = new Stack();

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        String input = sc.nextLine();
        System.out.println(postfix(input));
    }

    public static int priority(char ch) {
        if(ch=='+' || ch=='-'){
            return 0;
        }
        else if(ch=='*' || ch=='/'){
            return 1;
        }
        else return -1;
    }

    public static String postfix(String input) {
        String str = "";

        for (int i = 0; i < input.length(); i++) {
            char ch = input.charAt(i);
            if(('0'<=ch && ch<='9') || ch == '.'){
                str += ch;
            }
            else if (ch == '(') {
                stack.push(ch);
            }
            else if (ch == ')'){
                while(!stack.empty()){
                    char stackChar = stack.pop();
                    if(stackChar == '('){
                        break;
                    }
                    else {
                        str += stackChar;
                    }
                }
            }

            else if(ch =='*' || ch =='+' || ch =='-' || ch == '/'){
                if(stack.empty()){
                    System.out.println(ch);
                    stack.push(ch);
                } else{
                    int op1 = priority(ch); // 입력 연산자 우선순위
                    int op2 = priority(stack.peek()); // 스택 연산자 우선순위

                    if(op1 <= op2){
                        str += stack.pop();
                    } else {
                        stack.push(ch);
                    }
                }
            }
        }

        while(!stack.empty()){
            str += stack.pop();
        }

        return str;
    }
}

 

 

- 출력화면 :

'알고리즘' 카테고리의 다른 글

DFS 코드 이해하기 ( java )  (1) 2022.10.06
Dynamic Programming  (0) 2022.09.26
decryptCaesarCipher  (0) 2022.08.09
인접 행렬 길찾기  (0) 2022.08.04
인접 행렬 생성하기  (0) 2022.08.04

decryptCaesarCipher

문제

암호화된 문자열과 암호화 키를 입력받아 복호화된 문자열을 리턴해야 합니다.
카이사르 암호(Caesar cipher)는 평문(plaintext)을 암호키 secret개만큼 (오른쪽으로) 평행이동시켜 암호화 합니다. 복호화는 암호화된 문자열을 원래의 평문으로 복원하는 것을 말합니다.

'hello'를 secret 3으로 암호화한 경우: 'khoor'
'codestates'를 secret 11로 암호화한 경우: 'nzopdelepd'

입력

인자 1 : str

  • String 타입의 알파벳 소문자 문자열

인자 2 : secret

  • int 타입의 암호화 키

출력

  • String 타입을 리턴해야 합니다.

주의 사항

  • 빈 문자열을 입력받은 경우, 빈 문자열을 리턴해야 합니다.
  • 공백은 그대로 두어야 합니다.
  • 입력된 문자열은 모두 소문자만 입력됩니다.

입출력 예시

String output = decryptCaesarCipher("khoor", 3);
System.out.println(output); // --> hello

output = decryptCaesarCipher("zruog", 3);
System.out.println(output); // --> world

 

 

해결방법

1. 입력받은 str 을 char[] 형으로 변환하여 인덱스로 다룰 수 있게 만듬

2. 아스키 코드를 참조하여 [ 해당 자리 문자 - secret ] 을 해줌으로써 복호화

3. 소문자만 가능하므로 97 ~ 122 에서 순환하게 만들어줌

 

소스코드

public class decryptCaesarCipher {
    public static void main(String[] args) {
        String res = decryptCaesarCipher("nzop delepd dfaazced jzf", 11);
        System.out.println(res);
    }

    public static String decryptCaesarCipher(String str, int secret) {
        // TODO:
        /*
        - when : 암호화된 문자열과 암호화된 키를 입력받음
        - then : 복호화된 문자열을 리턴
        - how : 카이사르 암호는 평문을 암호키개만큼 오른쪽으로 평행이동시켜 암호화한다.
         */
        String result = "";
        int temp = 0;
        int overNum = 0;
        //when
        char[] ch = str.toCharArray();

        //how
        for (int i = 0; i < ch.length; i++) {
            if(ch[i] == ' '){
                result += ch[i];
            }
            else{
                if(ch[i]-secret >= 97) temp = ch[i]-secret;
                else{
                    overNum = 96 - (ch[i] - secret);
                    temp = 122 - overNum;
                }
                System.out.println("i: "+i+" "+ "temp: "+temp);
                result += Character.toString((char)temp);
            }
        }

        //then
        return result;
    }
}

'알고리즘' 카테고리의 다른 글

Dynamic Programming  (0) 2022.09.26
후위 표기식  (0) 2022.09.25
인접 행렬 길찾기  (0) 2022.08.04
인접 행렬 생성하기  (0) 2022.08.04
문자열에서 숫자 추출 알고리즘  (0) 2022.08.01

문제

주어진 인접행렬에서 한 정점으로부터 다른 정점으로 이어지는 길이 존재하는지 반환해야 합니다.

입력

인자 1: matrix

  • Array 타입을 요소로 갖는 인접 행렬이 담긴 2차원 배열

인자 2: from

  • int 타입의 시작 정점

인자 3: to

  • int 타입의 도착 정점

출력

  • boolean 타입을 리턴해야 합니다.

 

import java.util.LinkedList;
import java.util.Queue;

public class AdjacentFindRoot {
    public static void main(String[] args) {
        /*
        - 인접 행렬에서 한 정점으로 부터 다른 정점으로 이어지는 길이 존재하는지 반환
        - 입력 : 2차원 배열, 시작정점, 도착정점
        - 출력 : 시작정점에서 도착 정점으로 가는 길이 있으면 true 를 반환
         */

        int[][] test = new int[][]{
                {0, 1, 0, 0, 0},
                {0, 0, 0, 1, 0},
                {0, 1, 0, 0, 0},
                {0, 1, 1, 0, 0},
        };
        boolean directions = getDirections(test, 1, 4);
        System.out.println(directions);
    }

    public static boolean getDirections(int[][] matrix, int from, int to) {

        Queue<Integer> queue = new LinkedList<>();
        queue.add(from);

        boolean[] visited = new boolean[matrix.length];
        visited[from] = true;

        while(queue.size() > 0){
            int now = queue.poll();
            if(now == to) return true;

            for (int i = 0; i < matrix[now].length; i++) {
                if (matrix[now][i] == 1 && !visited[i]) {
                    queue.add(i);
                    visited[i] = true;
                }
            }
        }
        return false;
    }
}

'알고리즘' 카테고리의 다른 글

후위 표기식  (0) 2022.09.25
decryptCaesarCipher  (0) 2022.08.09
인접 행렬 생성하기  (0) 2022.08.04
문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
동전 교환 알고리즘  (0) 2022.07.30

문제

방향이 있는 간선과 방향이 없는 간선들의 목록들을 받아 2차원 배열의 인접행렬을 반환하는 함수를 작성하세요.

 

조건

각 간선은 3가지 정보를 담고 있습니다.

  • 0번째: 간선의 시작 정점 (0 이상의 정수)
  • 1번째: 간선의 도착 정점 (0 이상의 정수)
  • 2번째: 방향성 (1 == 일시 무향, 0 == 일시 방향)

입력

인자 1: edges

  • int 타입의 방향/무향인 간선들의 목록이 담긴 배열

출력

  • Array 타입을 리턴해야 합니다.
  • 2차원 배열의 인접 행렬

주의 사항

  • 정점 0에서 정점 4로 이어주는 간선이 존재할 경우 정점 1, 2, 3도 존재합니다.
  • 반환하는 인접행렬은 2차원 배열이며, 행(row)는 바깥 배열, 열(column)은 안쪽 배열입니다.
    • int[][] matrix = new int[][]{{0, 0}, {0, 0}}
    • matrix[0] == 0번째 행
    • matrix[0][0] == 0번째 행의 0번째 열
  • 두 정점간의 간선의 유무는 0과 1로 표시합니다.
    • 0: 두 정점간에 간선이 존재하지 않을 경우
    • 1: 두 정점간에 간선이 존재할 경우
  • 아래의 2차원 배열에서 세로축은 시작 정점, 가로축은 도착 정점입니다.
  • 음수는 올 수 없습니다.
  • self loop 없습니다.

 

public class AdjacentMatrix {
    public static void main(String[] args) {
        /*
        - 방향이 있는 간선과 방향이 없는 간선들의 목록들을 받아 2차원 배열의 인접 행렬을 반환하는 함수를 작성
        - 조건
        1) 0번째 : 간선의 시작 정점
        2) 1번째 : 간선의 도착 정점
        3) 2번째 방향성 ( 1==무향, 0==유향 )
        - 출력
        1) Array type
        2) 2차원 배열 인접 행렬
        - 주의사항
        1) 0 ~ 4로 이어주는 간선이 존재할 경우 1,2,3 정점도 존재합니다.
        2) 두 정점간 간선의 유무는 0과 1로 표시
         */

        int result[][] = new int[][]{
                {0, 3, 0},
                {0, 2, 0},
                {1, 3, 0},
                {2, 1, 0},
        };
        int[][] matrix = createMatrix(result);
        for(int i=0; i< matrix.length; i++){
            for(int j=0; j< matrix.length; j++){
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println();
        }
    }

    public static int[][] createMatrix(int[][] edges){
        // 가장 큰 정점
        int maxLength = 0;

        for(int i=0; i< edges.length; i++){
            for(int j=0; j< 2; j++){
                if(maxLength < edges[i][j]){
                    maxLength = edges[i][j];
                }
            }
        }

        int[][] result = new int[maxLength+1][maxLength+1];

        for(int i=0; i< edges.length; i++){
            if(edges[i][2] == 1){
                result[edges[i][0]][edges[i][1]] = 1;
                result[edges[i][1]][edges[i][0]] = 1;
            } else {
                result[edges[i][0]][edges[i][1]] = 1;
            }
        }

        return result;
    }
}

'알고리즘' 카테고리의 다른 글

decryptCaesarCipher  (0) 2022.08.09
인접 행렬 길찾기  (0) 2022.08.04
문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
동전 교환 알고리즘  (0) 2022.07.30
Binary Search Algorithm  (0) 2022.07.28

문제.

문자열을 입력받아 문자열에서 숫자를 모두 찾아 더한 뒤에 해당 값을 (숫자와 공백을 제외한 나머지) 문자열의 길이로 나눈 값을 정수로 반올림하여 리턴해야 합니다.

 

해결 메서드 키워드

  • Character.isDigit()
  • str.charAt()
  • Character.getNumericValue()
  • Math.round()

 

[ 소스코드 ]

public static void main(String[] args) {
        int result = 0;
        double sum = 0.0;
        String str = "Hello6 9World 2, Nic8e D7ay!";
        String temp = "";

        if(str.length() == 0) System.out.println(0);

        for(int i=0; i<str.length(); i++){
            if(Character.isDigit(str.charAt(i))){
                sum += Character.getNumericValue(str.charAt(i));
            }
            else if(str.charAt(i) == ' ') continue;
            else{
                temp += str.charAt(i);
            }
        }
        result =  (int) Math.round(sum/(temp.length()));
 }

'알고리즘' 카테고리의 다른 글

인접 행렬 길찾기  (0) 2022.08.04
인접 행렬 생성하기  (0) 2022.08.04
동전 교환 알고리즘  (0) 2022.07.30
Binary Search Algorithm  (0) 2022.07.28
Brute Force Algorithm과 시뮬레이션  (0) 2022.07.28

[문제]

 

자신이 감옥에 간 사이 연인이었던 줄리아를 앤디에게 빼앗겨 화가 난 조지는 브레드, 맷과 함께 앤디 소유의 카지노 지하에 있는 금고를 털기로 합니다. 온갖 트랩을 뚫고 드디어 금고에 진입한 조지와 일행들. 조지는 이와중에 감옥에서 틈틈이 공부한 알고리즘을 이용해 target 금액을 훔칠 수 있는 방법의 경우의 수를 계산하기 시작합니다.

예를 들어 $50 을 훔칠 때 $10, $20, $50 이 있다면 다음과 같이 4 가지 방법으로 $50을 훔칠 수 있습니다.

  • $50 한 장을 훔친다
  • $20 두 장, $10 한 장을 훔친다
  • $20 한 장, $10 세 장을 훔친다
  • $10 다섯 장을 훔친다

 

 

냅색 알고리즘

유명한 다이나믹 프로그래밍 문제 중 하나입니다.

 

위의 문제를 풀기 전에 간단하게 1, 2, 5, 10 원 동전들이 있고, 이 동전들로 10원을 만드는 방법을 구해보겠습니다.

 

d [i][j] = i 가지 종류의 동전을 사용하여 j 금액을 만드는 경우의 수라고 가정을 해봅니다.
그러면 저희가 구할 수는
d [4][10] = 4가지 ( 1,2,5,10 ) 종류의 동전을 사용하여 10원을 만드는 경우의 수

 

  • 풀이
  1.  [ 코인을 저장할 coin 배열 ] [ 동전 개수를 저장할 dy배열 ] 을 생성
  2.  dy[j] = j원을 거슬러 주기 위해 사용된 동전의 최소 개수
  3. 코인별로 거슬러 줄 동전 개수를 계산하고 dy에 저장

 

 

아래의 표를 보고 이해해보겠습니다.

d [1][2] = 1원으로 2원을 만드는 방법 1가지

 

d [2][2] = 0원에 2원을 추가해 2원을 만드는 방법 1가지 ( d[2][0] ) + d [1][2]

d [2][3] = 1원에 2원을 추가해 3원을 만드는 방법 1가지 + 1원으로 3원 만드는 방법 1가지 ( d[2][1] + d[1][3] )

d [2][4] = 2원에 2원을 추가해 4원을 만드는 방법 + 1원으로 4원 만드는 방법

                 ( ( d[2][0] + d[2][2] )+ ( d[1][2] + d[2][2] ) + d[1][4] )

d [2][5] = 4원에 1원 추가하는 방법 ( 2 ) + 1원으로 5원

....

 

 

public class CoinChange {
    public static void main(String[] args) {
        int[] coin = {10,20,50};

        long result = napsac(50,coin);
        System.out.println(result);
    }

    public static long napsac(int target, int[] type){

        long[] dy = new long[target+1];
        // 인덱스 1부터 시작

        dy[0] = 1;
        // 최소의 개수인 1부터 시작

        for(int i=0; i<type.length; i++){

            for(int j=1; j<=target; j++){
                if(type[i] <= j){
                    dy[j] += dy[j-type[i]];
                    /*
                    ex.
                    type[i] = 10, j=11일때,
                    dy[11-10] = dy[1]
                    dy[11] += dy[1]
                    즉, dy[11] 자리에 dy[1] 의 값을 더해준다.
                    */
                }
            }
        }
        return dy[target];
    }
}

'알고리즘' 카테고리의 다른 글

인접 행렬 생성하기  (0) 2022.08.04
문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
Binary Search Algorithm  (0) 2022.07.28
Brute Force Algorithm과 시뮬레이션  (0) 2022.07.28
Greedy Algorithm  (0) 2022.07.28
이진 탐색

데이터가 정렬된 상태에서 절반씩 반으로 나눠 분할 정복 기법으로 특정한 값을 찾아내는 알고리즘

 

 

동작 과정
  1. 정렬된 배열의 중간 인덱스 지정 ( 그 지정 값을 pivot 이라 부름 )
  2. 찾으려 하는 값과 동일하면 종료하고, 아니면 3단계로 이동
  3. 찾으려는 값이 중간 인덱스의 값보다 큰지, 작은지 확인
  4. pivot 보다 작은 경우 반으로 나눈 왼쪽 배열, pivot보다 큰 경우 반으로 나눈 오른쪽 배열을 선택
  5. 선택한 배열에서 다시 1단계부터 반복

 

이진 탐색을 사용 하는 경우
  1. 정렬된 배열에서 요솟값을 더 효율적으로 검색할 때 사용
  2. 데이터의 양이 많으면 많을수록 효율이 높아서 정렬된 데이터의 양이 많을 때 사용

 

* 단, 항상 효율이 좋은 것은 아닙니다.

-> 데이터의 양이 적고, 앞쪽에 위치한 데이터를 탐색할 때는 선형 탐색이 더 빠릅니다.

 

 

이진 탐색의 한계
  1. 정렬된 배열에서만 가능합니다.
  2. 규모가 작은 배열이라도 정렬되어 있지 않다면 이진 탐색을 사용해도 효율이 낮습니다.

 

이진 탐색 사용 사례
  1. 사전 검색
  2. 도서관 도서 검색
  3. 대규모 시스템에 대한 리소스 사항 파악
  4. 반도체 기업들에서 디지털/아날로그 레벨 측정하는 경우

 

BST 와 차이점

BST는 Tree는 하나의 노드가 두개의 하위 트리를 가지는 이진트리의 자료구조를 나타냅니ㅏㄷ

'알고리즘' 카테고리의 다른 글

문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
동전 교환 알고리즘  (0) 2022.07.30
Brute Force Algorithm과 시뮬레이션  (0) 2022.07.28
Greedy Algorithm  (0) 2022.07.28
바빌로니아법으로 제곱근 값 구하기  (0) 2022.07.28
구현 능력을 보는 대표적인 사례

brute force ( 완전 탐색 )simulation ( 시뮬레이션 )

 

시뮬레이션

문제에서 요구하는 복잡한 구현 요구 사항을 하나도 빠뜨리지 않고 코드로 옮겨 마치 시뮬레이션 하는 것과 같은 모습을 그리는 것입니다.

 

- 예시
메신저로 대화할 때, 아래의 조건을 충족해야 한다.
1) 아이디는 닉네임, 소속이 담긴 배열이여야 한다.
2) 소속은 false, true, null 중 하나
3) 소속이 셋 중 하나가 아니라면 아이디, 닉네임, 소속, 대화 내용의 문자열을 모두 X로 표시
4) 아이디와 닉네임은 길이를 2진수로 바꾼 뒤, 바뀐 숫자를 더함
5) 닉네임과 대화 내용은 공백 : 공백 으로 구분 [ ex. 'blue', 'Green', 'null' : hello. ]

이러한 과정들을 소스코드로 옮겨 담는 것입니다.

 

 

 

Brute-Force Algorithm (BFA)

순수 컴퓨팅 성능에 의존하여 모든 가능성을 시도하여 문제를 해결하는 무차별 대입 방법입니다.

 

따라서 Brute Force를 사용한다는 것은 최적의 솔루션이 아니라는 것을 의미하기도 합니다.

 

공간복잡도, 시간복잡도의 요소를 고려하지 않고 최악의 시나리오를 취하더라도 솔루션을 찾으려고 하기 때문입니다.

 

그렇다면 브루트 포스 알고리즘을 왜 사용할까요?

 

 

BFA 사용하는 경우
  1. 프로세스를 높이는데 사용하는 다른 알고리즘이 없을 때
  2. 문제를 해결하는 여러 솔루션이 있고 각 솔루션을 확인해야 할 때
[ 예시 : 어떤 문서 중 'park' 란 문자열을 찾아야 한다고 가정 ]
문서는 사전처럼 정렬되어 있지 않기 때문에, 문서에 있는 모든 단어 들을 반복해서 비교해야합니다.
그래서 시간 복잡도는 O(n) 이 됩니다.

이처럼 브루트 포스 알고리즘은 문제에 더 적절한 해결 방법을 찾기 전에 시도하는 방법입니다.
그러나 데이터 범위가 점점 커질수록 상당히 비효율적입니다.
따라서 프로젝트 규모가 커진다면 더 효율적인 알고리즘을 사용해야 할 것입니다.

 

BFA는 어디서 사용하고 있을까?

반복문을 통해 범위를 줄이지 않고 하나하나 비교하는 것은 Brute Force 를 활용한 Algorithm입니다.

 

 1. 순차 검색 알고리즘

 

배열 안에 특정 값이 존재하는 0번 인덱스부터 차례로 검색

 

2. 문열 매칭 알고리즘

 

[ 길이가 n인 전체 문자열 ] 과 [ 길이가 m인 문자열 ] 을 비교해

길이가 m인 문자열 패턴을 포함하는지를 검색합니다.

 

3. 선택 정렬 알고리즘

 

 전체 배열을 검색해 현재 요소와 비교하고 컬렉션이 완전히 정렬될 때까지

현재 요소보다 더 작거나 큰 요소를 교환하는 정렬 알고리즘입니다.

 

4. 그 밖의 Brute Force 활용 알고리즘

 - 버블 정렬

 - BFS, DFS

 - DP

'알고리즘' 카테고리의 다른 글

문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
동전 교환 알고리즘  (0) 2022.07.30
Binary Search Algorithm  (0) 2022.07.28
Greedy Algorithm  (0) 2022.07.28
바빌로니아법으로 제곱근 값 구하기  (0) 2022.07.28
Greedy Algorithm

매번 선택의 순간마다 최적의 상황을 쫓아 최종적인 해답에 도달하는 방법

 

탐욕 알고리즘의 해결 방법
  1. 선택 절차 : 현재 상태에서 최적의 해답 선택
  2. 적절성 검사 : 선택된 해가 문제의 조건에 만족하는지 검사
  3. 해답 검사 : 문제가 해결되었는지 확인 후 해결되지 않았다면 선택 절차로 돌아가 다시 반복
* 실생활 사례

손님으로 온 박씨가 "과자" , "음료" 를 선택해 물건 가격은 4040원이 나왔습니다.
손님은 계산을 위해 5000원을 내밀었고, "동전의 개수를 최소한"으로 거슬러 달라고 요청하였습니다.
4040원의 동전의 개수를 최소한으로 주는 방법이 탐욕 알고리즘의 대표적인 예제입니다.

1. 선택 절차
거스름돈 동전 개수를 줄이기 위해 현재 가장 가치있는 동전을을 우선 선택
 : 500원 -> 100원 -> 50원 -> 10원

2. 적절성 검사
1번 과정을 통해 동전들의 합이 거슬러 줄 금액을 초과하는지 검사합니다.
초과하면 가장 마지막에 선택한 동전 삭제 후, 1번으로 돌아가 한 단계 작은 동전을 선택

3. 해답 검사
선택된 동전들의 합이 거슬러 줄 금액과 일치하는지 검사합니다.
액수가 부족하면 1번부터 다시 반복합니다.

거스름돈 960원에 대한 위의 과정을 거치는 과정을 보겠습니다.

[1]
1. 500원 2개
2. 값이 초과됨으로 500원 1개, 100원 1개 선택

[2]
1. 500원 1개, 100원 5개
2. 값이 초과됨에 따라 500원 1개, 100원 4개, 50원 2개 선택 ....

이러한 과정을 거치게 됩니다.
그러면 총 500원 1개, 100원 4개, 50원 1개, 10원1개를 선택하게 됩니다.

 

탐욕 알고리즘의 반례 ( 마시멜로 실험 )

- 조건 : 지금 마시멜로를 받겠다고 하면 1개를 받고, 1분 기다렸다가 받으면 2개를 받을 수 있음

- 반례사항

: 탐욕 알고리즘에 따라 "현재" 최적의 선택을 하므로 1개의 마시멜로를 선택합니다.

 하지만 전체적으로 보면 1분뒤에 2개를 받는게 최적의 선택이 되므로 그리디 알고리즘은 최적의 해를 이끌어내지 못합니다.

 

탐욕 알고리즘이 최적의 해를 구하는 조건
  1. 탐욕적 선택 속성 : 앞의 선택이 이후의 선택에 영향을 주지 않는다.
  2. 최적 부분 구조 : 문제에 대한 최종 해결 방법은 부분 문제에 대해 최적 문제 해결 방법으로 구성됩니다.

 

결론

탐욕 알고리즘은 항상 최적의 결과를 도출하는 것은 아니지만,

어느정도 근사한 값을 빠르게 도출해준다는 장점이 있습니다.

문제 : Math.sqrt 없이 제곱근 구하기

루트4 = 2 처럼 한번에 구할 수 있는 수도 있지만

루트2 = 1.414213... 이 무리수인 것 처럼 근사값을 구하는 방식도 존재합니다.

그래서 이러한 경우를 위해 바빌로니아 법을 사용합니다.

 

우선 바빌로니아 법이 뭔지를 알아봅시다.

 

* 바빌로니아 법

임의의 수의 제곱근에 빠르게 수렴하는 수열을 만들어 근삿값을 구하는 방법입니다.
아래의 과정에 따라 양의 실수 a에 대하여 다음 과정을 따라 제곱근a의 근삿값을 구할 수 있습니다.



- 포인트는 다음과 같습니다.

 

1) x는 임의의 양의 실수

2) x는 항상 루트a보다 크므로 하한이 있는데 계속 감소하여 수렴

3) n이 높아질수록 정밀도가 올라감

 

위의 수식을 코드로 작성하기전에 적어보면 

X2 = Xn+1 , X1 = Xn 으로 가정하고,
X2 = 1/2 * (X1 + a/X1) = X1 * X1 + a / 2 * X1

그러면 위의 수식을 이용해 자바코드로 알고리즘을 풀어보도록 하겠습니다.

 

public class Babylonian {
    private double x,m;
    private double result;
    private int count;
    private double guess;

    // 근사값 구하기
    public double computeRoot(double a) {
        x = a;
        m = 0;
        while (m <= x) {
            // 제곱했을 때 딱 떨어진다면 바로 리턴
            // 제곱했을 때 수가 더 커진다면 제곱한 수의 -1을 리턴
            result = Math.floor((m+x) / 2);
            if (result * result > x) {
                x = result - 1;
            }
            else {
                m = x + 1;
            }
        }
        return x;
    }

    // 바빌로니아 공식
    public double BL(double num) {
        guess = computeRoot(num);
        if((num * num) == x) return num;
        while (guess * guess < num ) {
            guess += 0.01;
        }
        return guess;
    }
    
    public static void main(String[] args) {
        int num = 6;
        double b = BL(num);
        String res = String.format("%.2f",b);
        System.out.println(res);
    }
}

 

 

'알고리즘' 카테고리의 다른 글

문자열에서 숫자 추출 알고리즘  (0) 2022.08.01
동전 교환 알고리즘  (0) 2022.07.30
Binary Search Algorithm  (0) 2022.07.28
Brute Force Algorithm과 시뮬레이션  (0) 2022.07.28
Greedy Algorithm  (0) 2022.07.28

+ Recent posts