반응형

Tibero에 dump 파일을 import하는 도중에 발생.

 

Table Space의 경우 아래와 같이 AutoExtend를 On 했는데도 문제가 발생함.

 

CREATE TABLESPACE TEST_01 DATAFILE 'TEST.dbf' SIZE 1G AUTOECTEND ON;

 

문제

 

SELECT * FROM dba_data_files WHERE TABLESPACE_NAME='TEST_01';

 

을 통해 조회시 데이터파일 용량이 32G임을 확인.

 

Tibero menual을 확인해본 결과 Data File의 용량은 32GB가 최대치이고, BIGFILE 키워드를 사용하여 Table space를 생성 할 시 32TB까지 가능하다고 함.

 

하지만 메뉴얼대로

 

CREATE BIGFILE TABLESPACE TEST_01 DATAFILE 'TEST.dbf' SIZE 1G AUTOECTEND ON;

 

을 입력하는 경우 Syntax Error가 발생.(Licence가 trial이라서 안되는것인가.....?)

 

해결

Dump파일에서 사용중인 Table space의 크기를 알고 있는 상태라 충분한 크기이 data file을 할당해줌

 

CREATE TABLESPACE TEST_01 DATAFILE 

'TEST_001.dbf' SIZE 32G AUTOECTEND ON,

'TEST_002.dbf' SIZE 32G AUTOECTEND ON,

'TEST_003.dbf' SIZE 32G AUTOECTEND ON,

'TEST_004.dbf' SIZE 32G AUTOECTEND ON;

 

위와 같은 방법대로 여러개의 Datafile을 만들도록 함.

 

미해결 문제

CREATE BIGFILE TABLESPACE 만 동작한다면 쉽게 해결이 가능했을 것 같은데, 아직 안되는 원인을 찾지 못하였음.

반응형
반응형

이 글은 https://microservices.io/patterns/monolithic.html 를 참고하여 작성되었습니다.

 

1. 어플리케이션을 위한 개발 팀이 있다.

2. 새로운 팀 멤버는 빠르게 적응 해야한다.

3. 어플리케이션은 이해하기 쉽고, 수정하기 쉬워야 한다.

4. 당신은 지속적 배포(continuous deployment)를 행하고 싶다.

5. 확장성(scalability)과 가용성(availability)을 위하여 여러개의 어플리케이션 인스턴스를 여러대의 머신에 띄우고 싶다.

6. 신기술들(framework, programming language)을 사용하여 그 장점을 취하고 싶다.

 

Solution

위와 같은 사항들이 강제 될 때 모놀리식 아키텍처를 택 할 수 있고, 아래와 같은 솔루션이 하나의 방법이 될 수 있다.

- 단일 자바 WAR 파일(a single Java WAR file)

- 단일 디렉토리 계층 구조를 가지는 Rails 또는  NodeJS 코드(a single ditectory hierarchy of Rails or NodeJS code)

 

Example

이커머스 어플리케이션을 개발한다고 가정을 해보자. 고객의 주문을 받고, 재고를 확인하고, 결제와 배송도 가능해야한다.

어플리케이션은 StoreFront UI(Frontend UI)와 재고, 주문, 결제, 배송을 처리하기 위한 서버(Backend service)로 이루어질 것이다.

그리고 어플리케이션은 단일 모놀리식 아키텍처로 배포된다. 예를들면 Java Web application을 WAR형태로 Tomcat 과 같은 컨테이너에서 실행된다. Rails나 Node 의 경우에는 단일 디렉토리 구조로 배포가 되고, Nginx와 함께 실행 될 수도 있다.

가용성을 위하여 여러개의 서버 인스턴스가 실행되고, Load Balancer를 이용하여 서비스가 될 수 있다.

 

Resulting context

모놀리식 아키텍처가 가지는 아래와 같은 장점을 가진다.

  • 개발이 쉽다. easy to develop
  • 배포가 쉽다. easy to deploy
  • 스캐일링이 쉽다. easy to scale

하지만 어플리케이션과 팀의 규모가 커질수록 점점 더 중요해지는 단점이 존재한다.

 

  • 유지보수가 어려워진다.
  • IDE에 부하가 심해진다.
  • 웹 컨테이너(ex. Tomcat)에 부하가 심해진다.
  • 지속적인 개발이 어려워진다.
  • 스케일링이 어려워 질 수 있다.(장점에서 말하는 스케일링과는 범위가 다르다.)
  • 스케일링 개발의 걸림돌이 된다.
  • 기술스택을 이해하기 위해서는 장기적인 노력이 필요하다.
반응형
반응형

MSA 방식의 아키텍쳐를 택한 서비스를 개발하는 중 아래와 같이 다른 MS(Micro Service)를 호출하는 경우는 상당히 많다.

1
2
const authService = new AuthService()
authService.login(loginDto)
cs

오류 처리까지 포함한다면 아래와 같은 코드가 작성 될 수 있다.

1
2
3
4
5
6
try {
    const authService = new AuthService()
    authService.login(loginDto)
catch (err) {
    throw new Error("Cannot call rpc");
}
cs

트래픽이 얼마 없는 환경에서는 위 코드는 문제 없이 동작하는 것 처럼 보인다.

하지만, 트래픽이 꽤(추상적이지만..) 발생하고 있는 운영중인 서비스라고 가정을 한다면

 

로그인 API 호출이 발생할때마다 위 코드는 실행이 될 것이고

Auth서버가 어떠한 이유로(서버 자체의 문제, DB 문제, 또 다른 MS와의 통신 문제 등) 동작을 하지 않아 timeout 이 난다면?

 

timeout 에러가 발생하기 전까지 우리의 코드는 계속해서 어딘가에 해당 정보들을 저장하고 있을것이다.

메모리, 스레드, DB connection과 같은 자원들을 계속해서 잡아먹고 있을 것이고 결국 Memory가 모자라 이 서버까지 문제가 생길 수 있다.

 

예외처리를 했기 때문에 문제가 없다고 생각했겠지만, 이러한 코드가 반복되었다면 하나의 MS의 문제가 전체 MSA의 영향을 끼치는 큰 문제가 발생 할 수 있다.(MSA를 사용하는 목적과 크게 부합된다.... 하나의 MS가 죽어도 다른 MS들은 정상적으로 동작하여야 한다!)

 

회로차단 패턴(Circuit Break Pattern)은 이러한 문제를 방지하기 위한 패턴이다.

실패 할 수 있는(실패의 가능성이 있는) 작업을 반복적으로 실행하지 않도록 하기 위한 패턴이다.

 

Circuit Break Pattern 이라는 이름이 붙은 이유는 회로도에서 스위치를 이용하여 회로를 차단하는방법과 유사하기 때문이다.

 

 

코드로 위와 같은 기능을 구현한다면, 서버가 호출이 가능할 때에는 로직이 실행이 되고 호출이 불가능 할때에는 로직이 실행이 되지 않도록 작성을 하면 된다.

 

 

위 이미지에서는 3개의 상태를 이용하여 Circuit Breaker Pattern을 구현하기 위한 다이어그램이다.

 

Open : API 호출이 불가능한 상태(회로 스위치가 열려 회로가 끊어진 상태)

Closed : API 호출이 가능한 상태(회로 스위치가 닫혀 회로가 연결 된 상태)

Half Open : API 호출이 가능할수도 불가능할수도 있는 상태

 

세가지 상태로 구분이 된다.

 

간단한 의사코드를 작성하기에 앞서, 구현을 위한 몇가지 옵션을 정해보자.

 

1. 3번의 실패가 발생 할 시 회로를 차단하여 API 호출을 하지 않도록 한다.

2. 회로가 Open 상태가 된다면 3분의 시간 후 Half Open상태로 변경한다.

3. Half Open 상태에서 API 호출이 성공한다면 Closed 상태로 변경하고, 실패한다면 다시 3분 동안 Open 상태로 전환한다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
const status = {
    OPEN: 0,
    CLOSED: 1,
    HALF_OPEN: 2
}
 
const currentState = status.CLOSED
const halfOpenTime = 0;
const failedCount = 0
 
function login() {
    try {
        const authService = new AuthService()
        authService.login(loginDto)
    } catch (err) {
        throw new Error("Cannot call rpc");
    }
}
 
function callCircuitBreakerFn(cb) {
    if (currentState === status.CLOSED) {
        try {
            cb()
        } catch (err) {
            failedCount++
            if(failedCount >= 3) {
                currentState = status.OPEN
                halfOpenTime = Date.now() + (60 * 3)
            }
        }
    } else {
        if (Date.now() >= halfOpenTime) {
            currentState = status.HALF_OPEN
        }
 
        if (currentState === status.HALF_OPEN) {
            try {
                cb()
            } catch (err) {
                currentState = status.OPEN
                halfOpenTime = Date.now() + (60 * 3)
            }
        }
 
    }
    
}
 
cs

깔끔하지 않은 의사코드이지만, 대충 이런 형태의 코드가 작성 될 수 있을 것 같다.

실제 서비스에 적용을 한다면 여러개의 MS를 관리하기 위한 Class를 생성해야 할 것이고, 고려해야 할 사항들이 조금 더 생길 것 같다.

 

실제 서비스에 적용을 해보고 시행착오와 최종 코드등은 이후에 공유해봐야겠다.

 

참고

https://topswagcode.com/2016/02/07/Circuit-Breaker-Pattern/

 

Circuit Breaker Pattern

What is a circuit breaker? A circuit breaker is an automatically operated electrical switch designed to protect an electrical circuit from damage caused by Overcurrent/overload or short circuit. Its basic function is to interrupt current flow after Protect

topswagcode.com

 

반응형
반응형

문제

정수 X에 사용할 수 있는 연산은 다음과 같이 세 가지 이다.

  1. X가 3으로 나누어 떨어지면, 3으로 나눈다.
  2. X가 2로 나누어 떨어지면, 2로 나눈다.
  3. 1을 뺀다.

정수 N이 주어졌을 때, 위와 같은 연산 세 개를 적절히 사용해서 1을 만들려고 한다. 연산을 사용하는 횟수의 최솟값을 출력하시오.


첫째 줄에 1보다 크거나 같고, 106보다 작거나 같은 정수 N이 주어진다.


해설

Dynamic programming을 이용해서 푸는 문제입니다.

Bottom-up 방식으로 풀수 있으며 O(N)으로 해결 가능합니다.

사용하는 자료구조는 List, Array 와 같은 index와 value가 있는 자료구조 1가지만 사용하면 됩니다.

점화식을 세우면 아래와 같습니다.

List[N] = min(List[N/3], List[N/2], List[N-1]) + 1

예를들어 List[6]을 구한다고 가정하면, 아래 그림과 같이 계산이 가능합니다.


단, 위 점화식은 N이 3으로도 나누어 떨어지고, 2로도 나누어 떨어지는 경우입니다.

코드를 작성할때는 N에 따라 다르게 처리를 해주어야 합니다.


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
def min(x, y):
    return x if x <= y else y
 
= int(input())
 
minimum_count = [ 0 for _ in range(x+1)]
 
index = 0
while(True):
    if index > x:
        break
 
    if index <= 1:
        minimum_count[index] = 0
    else:
        temp_min = x + 1
        if index % 3 == 0:
            temp_index = int(index/3)
            temp_min = min(temp_min, minimum_count[temp_index])
 
        if index % 2 == 0:
            temp_index = int(index/2)
            temp_min = min(temp_min, minimum_count[temp_index])
 
        temp_min = min(temp_min, minimum_count[index-1])
        minimum_count[index] = int(temp_min + 1)
    index = index + 1
 
print(minimum_count[x])
 
cs


반응형
반응형

설명

RGB거리에 사는 사람들은 집을 빨강, 초록, 파랑중에 하나로 칠하려고 한다. 또한, 그들은 모든 이웃은 같은 색으로 칠할 수 없다는 규칙도 정했다. 집 i의 이웃은 집 i-1과 집 i+1이다.

각 집을 빨강으로 칠할 때 드는 비용, 초록으로 칠할 때 드는 비용, 파랑으로 드는 비용이 주어질 때, 모든 집을 칠할 때 드는 비용의 최솟값을 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 집의 수 N이 주어진다. N은 1,000보다 작거나 같다. 둘째 줄부터 N개의 줄에 각 집을 빨강으로 칠할 때, 초록으로 칠할 때, 파랑으로 칠할 때 드는 비용이 주어진다. 비용은 1,000보다 작거나 같은 자연수이다.

 

해설

 아래와 같이 집1, 집2, 집3인 경우의 R,G,B 페인트를 칠하는 비용이 있다고 가정한다.

Dynamic programming은 이전 계산값을 저장해서 다음 계산에 사용한다고 생각할 수 있다.

아래 그림을 보면 위에서 만든 자료구조를 가지고 같은 모양(열과 행이 같은) 자료구조를 만들어 자료를 업데이트 하는 것을 볼 수 있다.

업데이트를 하는 과정은 아래와 같다.

1. 각 샐은 현재 집을 해당 컬럼(R,G,B)로 칠했을때의 최소 비용이다.

2. 집2에 빨간색을 칠하는 경우 : 집2를 빨간색으로 칠하는 비용 + 최소값(집1을 초록색으로 칠하는 경우, 집 1을 파란색으로 칠하는 경우)

    집2에 파란색을 칠하는 경우 : 집2를 파란색으로 칠하는 비용 + 최소값(집1을 초록색으로 칠하는 경우, 집 1을 빨간으로 칠하는 경우)

위 2개의 규칙을 따라서 전체 셀을 업데이트 한다.

마지막까지의 최소비용을 구하면 해당 row에서 가장 최소인 값을 출력해주면된다.

 

코드

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
def min(a, b):
    return a if a <= b else b
#RGB를 빨,초,파
#이웃은 같은색이 안됨 i의 이웃은 i-1, i+1
#빨강 비용, 초록 비용, 파랑 비용이 주어질때 최소로 칠 할 수 있는 비용
 
#집의 수
= int(input())
matrix = [[0]*3 for i in range(n)]
 
 
for i in range(n):
    #각 색별 페인트 비용
    matrix[i][0], matrix[i][1], matrix[i][2= map(int, input().split())
 
matrix2 = [[0]*3 for i in range(n)]
 
for i in range(n):
    if i == 0:
        matrix2[i] = matrix[i]
    else:
        matrix2[i][0= matrix[i][0+ min(matrix2[i-1][1], matrix2[i-1][2])
        matrix2[i][1= matrix[i][1+ min(matrix2[i-1][0], matrix2[i-1][2])
        matrix2[i][2= matrix[i][2+ min(matrix2[i-1][0], matrix2[i-1][1])
 
print(min(min(matrix2[n-1][0], matrix2[n-1][1]), matrix2[n-1][2]))
 
cs

 

반응형
반응형

너무 풀기 쉬운문제는 안올리고 있었는데, 이 문제는 정답률이 생각보다 낮아 올리게 되었습니다.



문제

입력 받은 대로 출력하는 프로그램을 작성하시오.


입력

입력이 주어진다. 입력은 최대 100줄로 이루어져 있고, 알파벳 소문자, 대문자, 공백, 숫자로만 이루어져 있다. 각 줄은 100글자를 넘지 않으며, 빈 줄은 주어지지 않는다. 또, 각 줄은 공백으로 시작하지 않고, 공백으로 끝나지 않는다.


해설

일단 sys.stdin 으로 입력을 받고, for 문을 통해서 출력을 하면 됩니다.

단 기본적으로 입력에 \n(줄바꿈)이 들어가 있기 때문에 end 파라메터를 통해 줄바꿈을 빈 문자로 바꿔주면 줄바꿈 없이 출력을 할 수 있습니다.


코드

1
2
3
4
import sys
for i in sys.stdin:
    print(i, end='')
 
cs


반응형
반응형

문제

다음 소스는 N번째 피보나치 수를 구하는 C++ 함수이다.

1
2
3
4
5
6
7
8
9
10
11
int fibonacci(int n) {
    if (n == 0) {
        printf("0");
        return 0;
    } else if (n == 1) {
        printf("1");
        return 1;
    } else {
        return fibonacci(n‐1) + fibonacci(n‐2);
    }
}

fibonacci(3)을 호출하면 다음과 같은 일이 일어난다.

  • fibonacci(3)은 fibonacci(2)와 fibonacci(1) (첫 번째 호출)을 호출한다.
  • fibonacci(2)는 fibonacci(1) (두 번째 호출)과 fibonacci(0)을 호출한다.
  • 두 번째 호출한 fibonacci(1)은 1을 출력하고 1을 리턴한다.
  • fibonacci(0)은 0을 출력하고, 0을 리턴한다.
  • fibonacci(2)는 fibonacci(1)과 fibonacci(0)의 결과를 얻고, 1을 리턴한다.
  • 첫 번째 호출한 fibonacci(1)은 1을 출력하고, 1을 리턴한다.
  • fibonacci(3)은 fibonacci(2)와 fibonacci(1)의 결과를 얻고, 2를 리턴한다.

1은 2번 출력되고, 0은 1번 출력된다. N이 주어졌을 때, fibonacci(N)을 호출했을 때, 0과 1이 각각 몇 번 출력되는지 구하는 프로그램을 작성하시오.

 

입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다.

각 테스트 케이스는 한 줄로 이루어져 있고, N이 주어진다. N은 40보다 작거나 같은 자연수 또는 0이다.

 

반응형

해설

문제는 재귀적으로 피보나치 수열 문제를 풀었을 때 f(n)은 f(1)과 f(0)을 각각 몇번씩 호출하게 되는가?로 해석이 가능하다.

f(2)는 f(1)을 1번, f(0)을 1번 호출한다.

f(3)은 f(2)를 1번, f(1)을 1번 호출한다. 따라서 f(2)각 f(1)과 f(0)을 각각 1번씩 호출하기 때문에 총 f(1) 2번 f(0) 1번 을 호출한다.

이러한 방식으로 f(1)을 호출하는 회수와  f(0)을 호출하는 회수를 위의 피보나치 코드와 매우 비슷한 형태로 풀 수 있다.

그림이 매우 별로이지만 zero라고 된 부분이 f(0)을 호출하는 회수이고 one 이 f(1)을 호출하는 회수이다.

이 각각은 zreo(n) = zero(n-1) + zero(n-2) 와 같이 풀면서 계속해서 더해간다면 f(n)에서의 f(0), f(1) 호출 회수를 구할 수 있다.

 

코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
def count_fibonacci(n):
    zero_count = [1,0]
    one_count = [0,1]
    if n <= 1:
        return
 
    for i in range(2, n+1):
        zero_count.append(zero_count[i-1+ zero_count[i-2])
        one_count.append(one_count[i-1+ one_count[i-2])
 
    return zero_count, one_count
 
= int(input())
zero_count, one_count = count_fibonacci(40)
 
for _ in range(n):
    m = int(input())
    print("%d %d" % (zero_count[m], one_count[m]))
 
cs

 

반응형
반응형

문제

선영이는 주말에 할 일이 없어서 새로운 언어 AC를 만들었다. AC는 정수 배열에 연산을 하기 위해 만든 언어이다. 이 언어에는 두 가지 함수 R(뒤집기)과 D(버리기)가 있다.

함수 R은 배열에 있는 숫자의 순서를 뒤집는 함수이고, D는 첫 번째 숫자를 버리는 함수이다. 배열이 비어있는데 D를 사용한 경우에는 에러가 발생한다.

함수는 조합해서 한 번에 사용할 수 있다. 예를 들어, "AB"는 A를 수행한 다음에 바로 이어서 B를 수행하는 함수이다. 예를 들어, "RDD"는 배열을 뒤집은 다음 처음 두 숫자를 버리는 함수이다.

배열의 초기값과 수행할 함수가 주어졌을 때, 최종 결과를 구하는 프로그램을 작성하시오.


입력

첫째 줄에 테스트 케이스의 개수 T가 주어진다. T는 최대 100이다.

각 테스트 케이스의 첫째 줄에는 수행할 함수 p가 주어진다. p의 길이는 1보다 크거나 같고, 100,000보다 작거나 같다.

다음 줄에는 배열에 들어있는 수의 개수 n이 주어진다. (0 ≤ n ≤ 100,000)

다음 줄에는 [x1,...,xn]과 같은 형태로 배열에 들어있는 수가 주어진다. (1 ≤ xi ≤ 100)

전체 테스트 케이스에 주어지는 p의 길이의 합과 n의 합은 70만을 넘지 않는다.


해설

백준 알고리즘의 10866번을 풀때 작성해놓은 Deque 클래스를 그대로 활용하여 코드를 작성하였다. Deque 클래스에 print() 함수를 추가하였다. print함수는 방향을 True, False값으로 받아 출력을 할 수 있도록 만들었다.

이 문제를 풀다 막혀서 검색을 하는 경우에는 class의 내용보다는 아래 입력을 받고 처리하는 코드를 먼저 보고, 필요에 따라 클래스에서 해당 함수가 어떤 작업을하는지 확인하면 편하게 확인이 가능할것 같다.


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
import ast
class Deque():
    def __init__(self, size):
        self.queue = [0 for x in range(size + 1)]
        self.front_flag = 0
        self.rear = 0
        self.max_size = size
        self.list_size = size + 1
 
    def push_back(self, num):
        if (self.rear + 1) % self.list_size == self.front_flag:
            return -1
        self.queue[self.rear] = num
        self.rear = (self.rear +1) % self.list_size
 
    def push_front(self, num):
        if (self.rear + 1) % self.list_size == self.front_flag:
            return -1
        self.front_flag = self.list_size - 1 if self.front_flag == 0 else self.front_flag - 1
        self.queue[self.front_flag] = num
 
    def pop_front(self):
        if self.size() == 0:
            return -1
        temp = self.queue[self.front_flag]
        self.front_flag = (self.front_flag + 1) % self.list_size
        return temp
 
    def pop_back(self):
        if self.size() == 0:
            return -1
        self.rear = self.list_size - 1 if self.rear == 0 else self.rear - 1
        return self.queue[self.rear]
 
    def size(self):
        if self.front_flag == self.rear:
            return 0
        elif self.rear > self.front_flag:
            return self.rear - self.front_flag
        else:
            return self.max_size - (self.front_flag - self.rear) + 1
 
    def empty(self):
        return 1 if self.front_flag == self.rear else 0
 
    def front(self):
        if self.size() == 0:
            return -1
        return self.queue[self.front_flag]
 
    def back(self):
        if self.size() == 0:
            return -1
        return self.queue[self.rear - 1]
 
    def print(self, isFront):
        if self.size() == 0:
            print("[]")
            return
        output = "["
        if isFront:
            i = self.front_flag
            while(True):
                output = output + "%d"%self.queue[i]
                i = (i + 1) % self.list_size
                if i == self.rear:
                    output = output + "]"
                    break
                output = output + ","
        else:
            i = self.rear
            while(True):
                i = (i - 1) % self.list_size
                output = output + "%d"%self.queue[i]
                if i == self.front_flag:
                    output = output + "]"
                    break
                output = output + ","
        print(output)
#n개의 테스트 케이스
= int(input())
 
for i in range(n):
    #명령어 리스트
    operator = list(input())
    #m크기의 배열
    m = int(input())
    deque = Deque(m)
    #배열
    str = input()
    array = ast.literal_eval(str)
    for i in array:
        deque.push_back(i)
 
    isFront = True
    isValid = True
    for op in operator:
        if op == "R":
            isFront = not isFront
        elif op == "D":
            temp = 0
            if isFront:
                temp = deque.pop_front()
            else:
                temp = deque.pop_back()
 
            if temp < 0:
                print("error")
                isValid = False
                break
        else:
            print("unacceptable op")
 
    if isValid:
        deque.print(isFront)
 
cs


반응형
반응형

문제

지민이는 N개의 원소를 포함하고 있는 양방향 순환 큐를 가지고 있다. 지민이는 이 큐에서 몇 개의 원소를 뽑아내려고 한다.

지민이는 이 큐에서 다음과 같은 3가지 연산을 수행할 수 있다.

  1. 첫 번째 원소를 뽑아낸다. 이 연산을 수행하면, 원래 큐의 원소가 a1, ..., ak이었던 것이 a2, ..., ak와 같이 된다.
  2. 왼쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 a2, ..., ak, a1이 된다.
  3. 오른쪽으로 한 칸 이동시킨다. 이 연산을 수행하면, a1, ..., ak가 ak, a1, ..., ak-1이 된다.

큐에 처음에 포함되어 있던 수 N이 주어진다. 그리고 지민이가 뽑아내려고 하는 원소의 위치가 주어진다. (이 위치는 가장 처음 큐에서의 위치이다.) 이때, 그 원소를 주어진 순서대로 뽑아내는데 드는 2번, 3번 연산의 최솟값을 출력하는 프로그램을 작성하시오.


입력

첫째 줄에 큐의 크기 N과 뽑아내려고 하는 수의 개수 M이 주어진다. N은 50보다 작거나 같은 자연수이고, M은 N보다 작거나 같은 자연수이다. 둘째 줄에는 지민이가 뽑아내려고 하는 수의 위치가 순서대로 주어진다. 위치는 1보다 크거나 같고, N보다 작거나 같은 자연수이다.


해설

deque 클래스를 만들고, deque를 이용해서 문제를 풀었습니다.

기본적인 deque의 함수들은 백준의 기본 deque 문제가 제시하는 기준에 맞춰서 구현을 하였고, 제가 추가로 구현한 함수는 find함수로 특성 숫자가 몇번째 index에 있는지 알려주는 함수만 추가로 만들었습니다.


코드

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
class Deque():
    def __init__(self, size):
        self.queue = [0 for x in range(size + 1)]
        self.front_flag = 0
        self.rear = 0
        self.max_size = size
        self.list_size = size + 1
 
    def push_back(self, num):
        if (self.rear + 1) % self.list_size == self.front_flag:
            return -1
        self.queue[self.rear] = num
        self.rear = (self.rear +1) % self.list_size
 
    def push_front(self, num):
        if (self.rear + 1) % self.list_size == self.front_flag:
            return -1
        self.front_flag = self.list_size - 1 if self.front_flag == 0 else self.front_flag - 1
        self.queue[self.front_flag] = num
 
    def pop_front(self):
        if self.size() == 0:
            return -1
        temp = self.queue[self.front_flag]
        self.front_flag = (self.front_flag + 1) % self.list_size
        return temp
 
    def pop_back(self):
        if self.size() == 0:
            return -1
        self.rear = self.list_size - 1 if self.rear == 0 else self.rear - 1
        return self.queue[self.rear]
 
    def size(self):
        if self.front_flag == self.rear:
            return 0
        elif self.rear > self.front_flag:
            return self.rear - self.front_flag
        else:
            return self.max_size - (self.front_flag - self.rear) + 1
 
    def empty(self):
        return 1 if self.front_flag == self.rear else 0
 
    def front(self):
        if self.size() == 0:
            return -1
        return self.queue[self.front_flag]
 
    def back(self):
        if self.size() == 0:
            return -1
        return self.queue[self.rear - 1]
 
    def find(self, num):
        index = 0
        i = self.front_flag
        while(True):
            if self.queue[i] == num:
                return index
 
            index = index + 1
            i = (i + 1) % self.list_size
            if i == self.rear:
                break
 
 
 
n, m = map(int, input().split())
deque = Deque(n)
 
temp = list(map(int, input().split()))
 
for i in range(n):
    deque.push_back(i+1)
 
#이동 회수를 저장하기 위한 변수
count = 0
for i in range(m):
    # 찾아야 되는 숫자의 현재 위치
    index = deque.find(temp[i])
 
    mid = (n - i) / 2
    if index <= mid or (n-i) <= 2:
        #앞쪽임
        while(deque.front() != temp[i]):
            deque.push_back(deque.pop_front())
            count += 1
 
    else:
        #뒤쪽임
        while(deque.front() != temp[i]):
            deque.push_front(deque.pop_back())
            count += 1
    deque.pop_front()
 
print(count)
 
cs


반응형
반응형

문제

조세퍼스 문제는 다음과 같다.

1번부터 N번까지 N명의 사람이 원을 이루면서 앉아있고, 양의 정수 M(≤ N)이 주어진다. 이제 순서대로 M번째 사람을 제거한다. 한 사람이 제거되면 남은 사람들로 이루어진 원을 따라 이 과정을 계속해 나간다. 이 과정은 N명의 사람이 모두 제거될 때까지 계속된다. 원에서 사람들이 제거되는 순서를 (N, M)-조세퍼스 순열이라고 한다. 예를 들어 (7, 3)-조세퍼스 순열은 <3, 6, 2, 7, 5, 1, 4>이다.

N과 M이 주어지면 (N,M)-조세퍼스 순열을 구하는 프로그램을 작성하시오.


입력

첫째 줄에 N과 M이 빈 칸을 사이에 두고 순서대로 주어진다. (1 ≤ M ≤ N ≤ 1,000)


해설
이 문제는 원형큐를 이용해서 풀 수 있을것 같네요. 첫번째 코드는 일반 큐를 사용하여 문제를 풀었고, 두번째 코드는 원형큐를 사용해서 문제를 풀었습니다.
제가 원형큐를 사용하여 문제를 푼 가장 큰 이유는 기본 Queue에서의 pop() 때문인데요. pop의 구현 내용을 보면 python list의 pop(0)을 호출한것을 확인하실 수 있습니다. pop(0)의 경우 가장 앞의 요소를 pop하는 대신에 전체 리스트를 전부 왼쪽으로 이동(shift)시키는 작업이 동반되는 O(N)의 작업입니다.  p.s.그냥 pop()의 경우 마지막 요소만 pop하기 때문에 O(1)입니다.
반면에 원형큐의 경우는 front와 rear를 사용하여 문제를 풀기 때문에 전체 배열을 shift하는 과정 없이 사용이 가능합니다.

* 백준 알고리즘 파이썬 코드가 시간초과 나는 경우
파이썬 자체가 실행속도가 느리다보니 다른 언어에서 시간초과가 나지 않는 코드를 파이썬으로 구현하였을떄 시간초과가 나는 경우가 있다고 합니다.
이런 경우에 pypy3를 언어로 설정하는 경우에 해결이 될 수 있습니다.
단, 알고리즘 자체의 시간 복잡도가 큰 경우에는 pypy3를 사용해도 시간초과가 나게 됩니다.
예를들어 아래 일반 Queue를 사용한 경우에는 pypy3를 사용해도 시간초과가 나지만 원형큐를 사용한 경우에는 pypy3를 사용하는 시간초과가 나지 않습니다.

코드 - Queue
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Queue:
    def __init__(self):
        self.queue = []
 
    def push(self, num):
        self.queue.append(num)
 
    def pop(self):
        return self.queue.pop(0if len(self.queue) != 0 else -1
 
    def size(self):
        return len(self.queue)
 
    def empty(self):
        return 1 if self.size() == 0 else 0
 
    def front(self):
        return self.queue[0if self.size() != 0 else -1
 
    def back(self):
        return self.queue[-1if self.size() != 0 else -1
 
    def max(self):
        return max(self.queue)
 
n, m = map(int, input().split())
 
queue1 = Queue()
output_str = "<"
 
for i in range(1, n+1):
    queue1.push(i)
 
while(True):
    for i in range(m-1):
        queue1.push(queue1.pop())
 
    output_str = output_str + str(queue1.pop())
    if queue1.empty() == 1:
        break
    else:
        output_str = output_str + ", "
 
print(output_str + ">")
 
cs


코드 - CircularQueue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class CircularQueue():
    def __init__(self, size):
        self.queue = [0 for x in range(size + 1)]
        self.front = 0
        self.rear = 0
        self.max_size = size
        self.list_size = size + 1
 
    def push(self, num):
        if (self.rear + 1) % self.list_size == self.front:
            return -1
        self.queue[self.rear] = num
        self.rear = (self.rear +1) % self.list_size
 
    def pop(self):
        if self.size() == 0:
            return -1
        temp = self.queue[self.front]
        self.front = (self.front + 1) % self.list_size
        return temp
 
    def size(self):
        if self.front == self.rear:
            return 0
        elif self.rear > self.front:
            return self.rear - self.front
        else:
            return self.max_size - (self.front - self.rear) + 1
 
    def empty(self):
        return 1 if self.front == self.rear else 0
 
    def max(self):
        return max(self.queue)
 
    def print(self):
        print(self.queue)
        print(self.front)
        print(self.rear)
 
n, m = map(int, input().split())
 
queue = CircularQueue(n)
 
output_str = "<"
 
for i in range(1, n+1):
    queue.push(i)
 
while(True):
    for i in range(m-1):
        queue.push(queue.pop())
 
    output_str = output_str + str(queue.pop())
    if queue.empty() == 1:
        break
    else:
        output_str = output_str + ", "
 
print(output_str + ">")
 
cs


반응형

+ Recent posts