- (1/5) 볼링공 문제풀이 (1)

- (2/5) 볼링공 문제풀이 (2)

- (3/5) 볼링공 문제풀이 (3)

- (4/5) 볼링공 문제풀이 (4)

- (5/5) 과제설명

볼링공문제 (2019 SW마에스트로 입학 테스트)

A,B 두 사람이 볼링을 치고 있습니다. 두 사람은 서로 무게가 다른 볼링공을 고르려고 합니다. 볼링공은 총 N개가 있으며 각 볼링공마다 무게가 적혀 있고, 공의 번호는 1번부터 순서대로 부여됩니다. 또한 같은 무게의 공이 여러개 있을 수 있지만, 서로 다른 공으로 간주합니다. 볼링공의 무게는 1부터 M까지의 자연수 형태로 존재합니다.

예를들어 N이 5이고, M이 3이며 각각의 무게가 차례대로 1,3,2,3,2일 때 각 공의 번호가 차례대로 1번부터 5번까지 부여됩니다. 이때 두 사람이 고를 수 있는 볼링공 번호의 조합을 구하면 다음과 같습니다.

(1번,2번), (1번,3번), (1번,4번), (1번,5번), (2번,3번), (2번,5번), (3번,4번), (4번,5번)

결과적으로 두 사람이 공을 고르는 경우의 수는 8가지입니다. N개의 공의 무게가 각각 주어질 때, 두 사람이 볼링공을 고르는 경우의 수를 구하는 프로그램을 작성하세요.

  • 입력예시
입력
5 3 
1 3 2 3 2 
출력
8

문제의 수학적표현

$N$개의 볼링공의 무게를 각각 $x_1,\dots,x_N$ 이라고 하자. (단, $x_1,\dots, x_N$은 $\{1,\dots, M\}$사의 자연수의 값을 가지며 서로 같은 값을 가질 수 있다)

예시 $(a_1,\dots, a_5)=(1,3,2,3,2)$ 일 경우 가능한 조합의 집합은 아래와 같다. $(a_1,a_2), (a_1,a_3), (a_1,a_4), (a_1,a_5), (a_2,a_3), (a_2,a_5), (a_3,a_4), (a_4,a_5)$

풀이1

a=c(1,3,2,3,2)
a
[1] 1 3 2 3 2
count=0 
for (i in 1:5){
    for (j in 1:5){
        if(a[i]!=a[j]) count=count+1 
    }
}
count
[1] 16
  • 중복이 계산이 되어서 16이라는 값이 나왔다. (원래는 8이 나와야 한다.)

- 아래와 같이 수정하면 된다.

count=0 
for (i in 1:5){
    for (j in 1:5){
        if ((a[i]!=a[j]) & (j>i)) count=count+1 
    }
}
count
[1] 8

풀이2

- ((a[i]!=a[j]) & (j>i)) 에서 (j>i) 는 for문 선언부에서 처리할 수 있다.

count=0 
for (i in 1:5){
    for (j in i:5){
        if (a[i]!=a[j]) count=count+1 
    }
}
count
[1] 8

풀이3

a=c(1,3,2,3,2) 
A=rep(0,25*2) 
dim(A)=c(25,2)
A
      [,1] [,2]
 [1,] 0    0   
 [2,] 0    0   
 [3,] 0    0   
 [4,] 0    0   
 [5,] 0    0   
 [6,] 0    0   
 [7,] 0    0   
 [8,] 0    0   
 [9,] 0    0   
[10,] 0    0   
[11,] 0    0   
[12,] 0    0   
[13,] 0    0   
[14,] 0    0   
[15,] 0    0   
[16,] 0    0   
[17,] 0    0   
[18,] 0    0   
[19,] 0    0   
[20,] 0    0   
[21,] 0    0   
[22,] 0    0   
[23,] 0    0   
[24,] 0    0   
[25,] 0    0   
k=1
for (i in 1:5){
    for (j in 1:5){
        A[k,]<- c(a[i],a[j])
        k=k+1
    }
}
A
      [,1] [,2]
 [1,] 1    1   
 [2,] 1    3   
 [3,] 1    2   
 [4,] 1    3   
 [5,] 1    2   
 [6,] 3    1   
 [7,] 3    3   
 [8,] 3    2   
 [9,] 3    3   
[10,] 3    2   
[11,] 2    1   
[12,] 2    3   
[13,] 2    2   
[14,] 2    3   
[15,] 2    2   
[16,] 3    1   
[17,] 3    3   
[18,] 3    2   
[19,] 3    3   
[20,] 3    2   
[21,] 2    1   
[22,] 2    3   
[23,] 2    2   
[24,] 2    3   
[25,] 2    2   

- (1) 무게가 같은 것을 뽑지 않고 (2) 중복되는 경우를 제외

  • 중복되는 경우를 제외한다는 말은 1번공-2번공 뽑는 경우와 2번공-1번공을 뽑는경우중 하나만 고려한다는 것을 의미
vec1<-c()
vec2<-c()
for(i in 1:25){
    vec1[i] <- A[i,1] != A[i,2] 
    vec2[i] <- A[i,1] > A[i,2]
}
vec1
 [1] FALSE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE
[13] FALSE  TRUE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE  TRUE FALSE  TRUE
[25] FALSE
vec2
 [1] FALSE FALSE FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE
[13] FALSE FALSE FALSE  TRUE FALSE  TRUE FALSE  TRUE  TRUE FALSE FALSE FALSE
[25] FALSE
sum(vec1 & vec2)
[1] 8

풀이4

- vec1, vec2를 굳이 for문으로 구할필요가 없을것 같다.

a=c(1,3,2,3,2) 
A=rep(0,25*2) 
dim(A)=c(25,2)
k=1
for (i in 1:5){
    for (j in 1:5){
        A[k,]<- c(a[i],a[j])
        k=k+1
    }
}
A
      [,1] [,2]
 [1,] 1    1   
 [2,] 1    3   
 [3,] 1    2   
 [4,] 1    3   
 [5,] 1    2   
 [6,] 3    1   
 [7,] 3    3   
 [8,] 3    2   
 [9,] 3    3   
[10,] 3    2   
[11,] 2    1   
[12,] 2    3   
[13,] 2    2   
[14,] 2    3   
[15,] 2    2   
[16,] 3    1   
[17,] 3    3   
[18,] 3    2   
[19,] 3    3   
[20,] 3    2   
[21,] 2    1   
[22,] 2    3   
[23,] 2    2   
[24,] 2    3   
[25,] 2    2   
vec1<- A[,1]!=A[,2]
vec2<- A[,1]>A[,2]
sum(vec1&vec2)
[1] 8

풀이5

- 생각해보니까 단순히 첫번째 열이 두번쨰 열보다 큰지만 체크해도 된다.

a=c(1,3,2,3,2) 
A=rep(0,25*2) 
dim(A)=c(25,2)
k=1
for (i in 1:5){
    for (j in 1:5){
        A[k,]<- c(a[i],a[j])
        k=k+1
    }
}
sum(A[,1]>A[,2])
[1] 8

현재까지의 풀이정리

- 풀이1-2: 매트릭스를 사용하지 않음.

- 풀이3-5: 매트릭스를 사용함.

- 현재까지는 풀이2가 가장 간결하고 루프도 적게돌아간다. 하지만 가장 중요한 일은 틀리지 않는 것인데 풀이2는 틀리기 쉬움

- 하지만 풀이2와 같은 접근법은 디버깅이 어렵다. $\to$ 예외사항을 처리하기 어려움.

- 특히 삼성에서 실시하는 코딩테스트 문제의 경우 예외사항을 잘 처리해야 하는 문제가 주로 출제된다고 한다.

풀이6

- 사실 A를 구해주는 함수가 R에 존재함.

a=c(1,3,2,3,2)
A=expand.grid(a,a)
A
   Var1 Var2
1  1    1   
2  3    1   
3  2    1   
4  3    1   
5  2    1   
6  1    3   
7  3    3   
8  2    3   
9  3    3   
10 2    3   
11 1    2   
12 3    2   
13 2    2   
14 3    2   
15 2    2   
16 1    3   
17 3    3   
18 2    3   
19 3    3   
20 2    3   
21 1    2   
22 3    2   
23 2    2   
24 3    2   
25 2    2   
  • expand.grid(): 벡터를 입렵으로 받아서 그 벡터의 원소가 만들어내는 순서쌍 조합을 데이터프레임 형태로 리턴함
  • 데이터프레임
library(tidyverse)
── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──

 ggplot2 3.3.5      purrr   0.3.4
 tibble  3.1.5      dplyr   1.0.7
 tidyr   1.1.4      stringr 1.4.0
 readr   2.0.2      forcats 0.5.1

── Conflicts ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
 dplyr::filter() masks stats::filter()
 dplyr::lag()    masks stats::lag()

A %>% mutate(C= Var1> Var2)
   Var1 Var2 C    
1  1    1    FALSE
2  3    1     TRUE
3  2    1     TRUE
4  3    1     TRUE
5  2    1     TRUE
6  1    3    FALSE
7  3    3    FALSE
8  2    3    FALSE
9  3    3    FALSE
10 2    3    FALSE
11 1    2    FALSE
12 3    2     TRUE
13 2    2    FALSE
14 3    2     TRUE
15 2    2    FALSE
16 1    3    FALSE
17 3    3    FALSE
18 2    3    FALSE
19 3    3    FALSE
20 2    3    FALSE
21 1    2    FALSE
22 3    2     TRUE
23 2    2    FALSE
24 3    2     TRUE
25 2    2    FALSE
  • mutate(): 데이터프레임에서 새로운 column을 추가하는 기능 (기존의 컬럼을 활용하여 새로운 파생되는 칼럼을 쉽게 만들 수 있다)
A %>% mutate(C= Var1> Var2) %>% filter(C == TRUE)
  Var1 Var2 C   
1 3    1    TRUE
2 2    1    TRUE
3 3    1    TRUE
4 2    1    TRUE
5 3    2    TRUE
6 3    2    TRUE
7 3    2    TRUE
8 3    2    TRUE
  • filter(): 데이터프레임에서 특정조건을 만족하는 행을 필터링 하는 기능
A %>% mutate(C= Var1> Var2) %>% filter(C == TRUE) %>% count
  n
1 8
  • count(): 데이터프레임의 행의 숫자를 세어주는 기능

- 결과가 데이터프레임으로 나옴 $\to$ 숫자가 하나인데 굳이 데이터프레임이 자료형일 이유는 없음 (다루기 불편)

A %>% mutate(C= Var1> Var2) %>% filter(C == TRUE) %>% count -> rslt
rslt+20
  n 
1 28
1:8
[1] 1 2 3 4 5 6 7 8
1:rslt
Error in 1:rslt: NA/NaN argument
Traceback:

- 숫자로 만들기 위해서 as.numeric() 함수를 사용하면 된다.

as.numeric(rslt)
[1] 8
A %>% mutate(C= Var1> Var2) %>% filter(C == TRUE) %>% count %>% as.numeric
[1] 8

숙제

A %>% mutate(C= Var1 -Var2)
   Var1 Var2 C 
1  1    1     0
2  3    1     2
3  2    1     1
4  3    1     2
5  2    1     1
6  1    3    -2
7  3    3     0
8  2    3    -1
9  3    3     0
10 2    3    -1
11 1    2    -1
12 3    2     1
13 2    2     0
14 3    2     1
15 2    2     0
16 1    3    -2
17 3    3     0
18 2    3    -1
19 3    3     0
20 2    3    -1
21 1    2    -1
22 3    2     1
23 2    2     0
24 3    2     1
25 2    2     0