Algorithm/프로그래머스

[프로그래머스 : 코딩테스트 연습 - Swift] 순서쌍의 개수

버스트 캐넌 2023. 10. 13. 19:59

글 작성에 앞서 본 블로그의 모든 게시글은 블로그 주인의 개발 일지(일기) 형태의 게시글입니다.

정보를 나누는 방식보단, 제가 했던 방식을 공유하는 식의 글이라 읽어도 제대로 이해를 못 하실 수 있거나 더 좋은 다른 방법이 존재할 수 있습니다.

이 점 양해해 주시며 본 블로그의 게시글을 읽어주시면 감사하겠습니다.

programmers

안녕하세요 버스트캐넌입니다.

 

오늘은 프로그래머스 레벨 0 정답률 89%인 순서쌍의 개수 문제를 풀어보겠습니다.

 

그런데 여담이지만, 프로그래머스 문제들의 정답률이 서서히 오르는 경향이 있더라고요..?

 

어제 배열 뒤집기 문제의 정답률이 89%라서 풀었는데 오늘 확인해 보니 90%로 올랐더라구요...

 

흠.. ㅋㅋ

 

일단 시작하겠습니다.

 

문제


문제 설명

  • 순서쌍이란 두 개의 숫자를 순서를 정하여 짝지어 나타낸 쌍으로 (a, b)로 표기합니다. 자연수 n이 매개변수로 주어질 때 두 숫자의 곱이 n인 자연수 순서쌍의 개수를 return 하도록 solution 함수를 완성해 주세요.

제한사항

  • 1 ≤ n ≤ 1,000,000

입출력 예

  • n = 20, return = 6
  • n이 20 이므로 곱이 20인 순서쌍은 (1, 20), (2, 10), (4, 5), (5, 4), (10, 2), (20, 1) 이므로 6을 return 합니다.

풀이


import Foundation

func solution(_ n:Int) -> Int {
    guard n<=1000000, n>0 else {return 0}
    var num = 0
    for i in 1...n {
        for j in 1...(n/i){
            if i * j == n {
                num+=1
            }
        }
    }
    return num
}

기본 개념


반복문 (for-in)

for i in 1...10 {

	for j in 1...10{ 
    
	}
}

반복문입니다.

 

위의 예제는 이중반복문인데, 반복문 안에 반복문을 또 넣은 것입니다.

 

일단 하나씩 보자면, for는 반복문 코드이고, 그 뒤 i는 상수명이라고 합니다. 

 

상수명은 사용하지 않을 때, _(언더바)를 사용하여 생략할 수 도 있습니다.

 

in 1...10은 범위라고 합니다.

 

범위에서 좀 더 세부적으로 보자면, 1...10은 1에서 10까지 총 10번 반복한다는 뜻입니다.

 

범위에서는 굳이 상수가 아닌, 변수가 올 수도 있습니다.

 

1...n은 1부터 변수 n 까지라는 뜻입니다.

 

그리고 중간의 연산자도 종류가 다양합니다.

1.  닫힌 범위 연산자

1...10

  • 1 이상, 10 이하의 범위가 잡힙니다.

2.  반 닫힌 범위 연산자

1..<10

  • 1 이상, 10 미만의 범위가 잡힙니다.

3.  단방향 연산자

...10

1...

..<10

  • 차례대로, 처음부터 10 이하, 1부터 끝까지, 처음부터 10 미만이라는 뜻입니다.
  • 주로 최소, 최대 범위가 정해진 배열에서 쓰입니다.
  • ex) array[ ...10 ]

for문도 자주 사용할 거 같으니 알아두는 게 좋겠네요.

코드 풀이


guard n<=1000000, n>0 else {return 0}

일단 제한 사항을 지키기 위해 guard를 이용하여 n이 1 이상 1,000,000 이하인 경우만 넘겨주었고, 이외의 값이 들어오면 return을 0으로 하여 오류가 나게(난 것처럼) 하였습니다.

var num = 0
for i in 1...n {
    for j in 1...(n/i){
        if i * j == n {
            num+=1
        }
    }

그런 다음 저는 이중 반복문을 돌려, i와 j의 곱한 결과가 n과 같을 때 미리 선언해 둔 num을 1 더해주는 코드를 작성해 보았습니다.

 

여기서 num은 순서쌍의 개수라고 이해하시면 됩니다.

 

제가 처음에는 조금 다르게 했는데 처음 코드의 반복문만 조금 들고 와보자면,

for i in 1...10000000 {
    for j in 1...10000000{
        if i * j == n {
            num+=1
    }
}

n이 최대치인 1,000,000일 경우 (1,1000000)의 순서쌍이 존재하기 때문에, i와 j을 백만 번씩.. 총 1,000,000^2번을 돌려버리니 Xcode에서도 힘들어하고 프로그래머스에서는 시간초과로 실패처리를 하더라고요..

 

그래서 여기서 수정해 봤는데,

for i in 1...n {
    for j in 1...n{
        if i * j == n {
            num+=1
    }
}

1,000,000이라는 상수를 n이라는 변수로 바꿔주었습니다.

 

그러면 무조건 1,000,000^2번을 돌리지 않아도 n의 값이 작으면 조금만 돌다가 끝이 나죠!

 

그런데 이거도 안 괜찮은 코드였습니다...

 

결국엔 n이 크면 그만큼 많이 돌더라고요...

 

그래서 어떻게 할까 고민하다가, i는 n만큼 돌아도, j는 n만큼 돌필요가 없다고 생각 들었습니다.

 

예를 들자면, n이 100일 때, i가 50이라면, (50, x)의 순서쌍을 갖게 될 텐데, 곱셈의 순서쌍이다 보니 i*j가 n보다 클 경우의 수가 없잖아요?

 

그래서 i*j가 n보다 클 경우를 제외해서 for문에서 j를 (n/i)만큼 돌아가게 만들었습니다.

 

이거도 버거워하긴 하던데 그래도 정답은 나오더라고요.

다른 사람 코드 탐구


드디어 다른 사람 코드 탐구 시간이 왔습니다. (두둥)

 

어떤 알고리즘 굇수들의 짧은 코드를 볼 수 있을지 기대가 되네요.

 

저는 한두 가지의 코드를 가져올 건데, 가장 많은 분들이 작성한 코드와 가장 짧은 코드를 들고 올 예정입니다.

 

import Foundation

func solution(_ n:Int) -> Int {
    var count = 0
    for i in 1...n {
        if n % i == 0 {
            count += 1
        }
    }
    return count
}

우선 가장 많은 분들이 작성한 코드를 들고 왔습니다.

 

제 코드와 유사한데... 해석해보니까,

 

어차피 곱셈의 순서쌍이니까, n이 i로 나누어 떨어지면 자연스럽게 j도 나와서 i가 나누어 떨어지면 순서쌍의 개수(count)를 추가하는 방식이네요.

 

왜 나누어 떨어지는 경우의 수를 생각하지 못했을까요 ㅠㅠㅠ

 

그다음 짧은 코드를 들고 왔습니다.

 

import Foundation

func solution(_ n:Int) -> Int {
    return (1...n).filter{ n % $0 == 0 }.count
}

이거는... 일단 봐서는 1 이상 n이하에 필터를 걸어서 숫자가 나누어 떨어지는 개수를 가져오는 코드인가요?

 

한 번에 봐서 모르겠으니 모르는 것은 검색해 봅시다.

 

Filter

  • swift의 다른 함수를 전달인자로 받거나 함수실행의 결과를 함수로 반환하는 함수인 고차함수 중 하나로, 기존 컨테이너에서 내부의 값을 걸러 새로운 컨테이너를 만드는 함수입니다.
  • 데이터를 추출하고자 할 때 주로 쓰입니다.

$0

  • Swift에서 클로저(Closure)의 첫 번째 인수를 뜻합니다.

 

구글링 한 결과로 코드를 재해석해보자면, n이 클로저의 인수에서 나누어 떨어지는 개수를 출력하는 함수를 1부터 n까지 늘어나는 함수에 넣어서, 1부터 n까지 나누어 떨어지는 갯수를 반환하는 코드인 것 같습니다.

 

이것 역시, 많은 사람이 풀었던 코드처럼 특정 숫자에 나누어 떨어졌다는 뜻은, (특정 숫자, 몫)으로 순서쌍을 만들 수 있기에 나누어 떨어지는 개수가 순서쌍의 개수와 동일하다고 여겨 이렇게 작성한 것 같습니다.

 

후 벌써 힘드네요.. ㅋㅋㅋ

 

아무튼 오늘 글은 여기서 마치겠습니다.

 

글 읽어주셔서 감사합니다.

 

좋은 하루 보내세요~

 

궁금한 점이나 지적해야 할 부분이 있으시면 댓글 남겨주세요. 블로그 주인의 상황에 따라 답변이 없을 수 있으나, 최대한 피드백해 드리겠습니다.