1. 코드카타
오늘 내가 푼 알고리즘 문제는 '소수 만들기' 이다.
소수 만들기 |
숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return하도록 solution 함수를 완성하기 |
ex) nums = [1, 2, 3, 4] -> result = 1 (1+2+4 = 7) nums = [1, 2, 7, 6, 4] -> result = 4 (1+2+4 = 7, 1+6+4 = 11, 2+7+4 = 13, 7+6+4 = 17) |
우선은 nums에 있는 숫자들 중 서로 다른 3개를 골라서 더하는 코드부터 짰다.
for (i in nums.indices){
for (j in i+1 until nums.size){
for (k in j+1 until nums.size){
num = nums[i]+nums[j]+nums[k]
}
}
}
이 코드는 어제도 썼으니 자세한 설명은 생략하고, 하나만 짚자면 'nums.indices'인데 다른 사람들의 코드를 참고할 때 많이 보이던 단어 중에 하나가 indices여서 이건 또 무언가 하고 찾아봤더니….
indices : index의 복수형 |
인덱스의 복수형이었다.
그러니까 for문 안에 있는 저 nums.indices는 nums의 모든 인덱스들을 다 돌린다는 뜻이었다.
나는 저걸 표현할 때 보통 0 until nums.size(length)를 썼는데 이것보다는 훨씬 간결한 표현이라 생각해서 이번에는 저걸 사용해봤다.
nums에 있는 숫자들 중 서로 다른 3개를 골라서 더하는 수식까지는 완성했다.
이제 남은 건 서로 다른 3개의 숫자를 더한 이 값이 소수인지 아닌지 판별하는 건데….
당연하겠지만 이 문제의 핵심은 '이 숫자가 소수인지 아닌지를 판별할 수 있는 코드를 작성'하는 것과 '서로 다른 3개를 골라서 더한 값들 중 소수의 개수를 찾는 코드를 작성'하는 것이다.
Kotlin 안에 소수인지 아닌지 판별해주는 기능이 있었다면 편리하기야 하겠지만 그런 기능을 넣어놨을 것 같지는 않아서 내 손으로 직접 소수인지 아닌지 판별할 수 있는 코드를 만들기로 했다.
소수는 1과 자기 자신으로밖에 나눌 수 없는 수를 말하는데 이걸 바꿔말하면 1과 자기 자신 사이에 있는 수로 나누면 나머지가 0이 되서는 안 된다.
이를 이용해서 기본적인 틀부터 짰다.
for (i in 2 until number) {
if (number % i == 0) {
return false
}
}
return true
이 코드를 해석하자면 처음에 number를 2로 나누는 것부터 시작해서 나머지가 0이 되면 즉시 false 값을 return하고, 나머지가 0이 아니면 숫자를 하나씩 늘려나가면서 for문을 반복한다. 그러다가 for문이 끝났음에도 if문을 만족하지 않으면 for문을 빠져나오고 true 값을 return한다.
그러니까, 여기서 false 값을 return하면 소수가 아니고, true 값을 return하면 소수라는 것이다.
(참고로 본래 소수는 '1을 제외한' 이라는 조건이 있기에 그 조건 역시 집어넣어야 하지만, 이번에는 1 이하의 숫자가 나올 수가 없어서 생략했다.)
이제 이걸 위의 식과 결합해서 식을 완성할 차례다.
var f = 0
for (a in 2 until num){
if (num % a == 0){
f++
break
}
}
if (f == 0) answer++
위의 식에서는 true, false 값을 return할 수 없으니 그것과 비슷하게 구현하기 위해서 임의의 변수를 지정한 다음, 나머지가 0이 되면 거기에 1을 더하는 식으로 true, false를 구현시켰다.
그리하여 완성된 최종 코드는 다음과 같다.
class Solution {
fun solution(nums: IntArray): Int {
var answer = 0
var num: Int
for (i in nums.indices){
for (j in i+1 until nums.size){
for (k in j+1 until nums.size){
num = nums[i]+nums[j]+nums[k]
var f = 0
for (a in 2 until num){
if (num % a == 0){
f++
break
}
}
if (f == 0) answer++
}
}
}
return answer
}
}
위 코드의 구동 방식은 다음과 같다.
우선 처음에 nums에 있는 서로 다른 3개의 합인 num을 구한다.
그리고 그 num을 가지고 소수인지 아닌지 판별하는 for문을 돌린 뒤 for문이 끝나면 맨 밑에 if문으로 넘어간다.
f가 0이면 true이므로 answer에 1을 더하여 카운팅하고, f가 1이면 false이므로 그대로 넘어가고 다시 num을 구하는 for문을 반복시킨다.
실제로 테스트 케이스를 돌려보니 통과가 되었고, 그대로 제출하니 문제 해결!
그런데 저걸 다시 한 번 보다가 문득 이런 생각이 들었다.
코드가 굉장히 난잡하지 않나?
물론 잘 돌아가기만 하면 문제 없는 것도 맞고, 실제로 개발하는 과정에서도 어떻게 했는데 돌아가서 그대로 놔두는 경우도 있다는 이야기도 들었지만, 유지/보수를 할 때 코드가 직관적이지 않아서 생기는 문제들도 있다는 이야기 또한 들었다.
그렇기에 내가 지금 짠 코드를 다른 사람이 봤을 때 과연 한 눈에 파악할 수 있을까? 라고 생각해보니 아닐 거라는 생각이 먼저 들었다.
그리하여 이번에는 이 코드보다 더 간단하거나, 혹은 직관적인 코드가 있는지 생각해보기로 했다.
그렇게 한참을 고민하다가 내 뇌리에 스치는 하나의 생각.
아예 저 '소수를 판별하는 코드'를 함수로 만들면 어떨까?
자, 다시 위의 코드를 불러보자.
for (i in 2 until number) {
if (number % i == 0) {
return false
}
}
return true
여기서 true, false 값만 가지는 데이터 타입이 Kotlin에 존재하는데 바로 불리언(Boolean)이다.
Boolean : true, false 둘 중에 1개의 값을 저장할 수 있는 데이터 타입으로, 주로 조건문에서 의사결정을 하기 위한 용도로 많이 사용. |
그리고 나는 이걸 이용해서 소수를 판별할 수 있는 함수를 만들었다.
fun isPrime(number: Int): Boolean {
for (i in 2 until number) {
if (number % i == 0) {
return false
}
}
return true
}
이제 isPrime(number) 함수에 임의의 숫자를 집어넣기만 하면 무조건 true, false 둘 중 하나의 값을 return한다.
true를 return하면 소수라는 뜻이고, false를 return하면 소수가 아니라는 뜻이다.
이제 이 함수를 아까 만든 num 구문에 집어넣어보자.
for (i in nums.indices){
for (j in i+1 until nums.size){
for (k in j+1 until nums.size){
num = nums[i]+nums[j]+nums[k]
if(isPrime(num)) answer++
}
}
}
아까보다 훨씬 간결해졌다.
여기서 if 조건문 안에 isPrime(num)이 무조건 true, 혹은 false를 return하기 때문에 만약 num이 소수이면 true를 리턴하게 되고, if 조건문이 () 안의 조건이 true일 경우에 뒤의 식을 실행시키기 때문에 결론적으로 num이 소수일 경우에만 카운팅하는 식이 완성되었다.
이를 최종 정리하면 다음과 같다.
class Solution {
fun isPrime(number: Int): Boolean {
for (i in 2 until number) {
if (number % i == 0) {
return false
}
}
return true
}
fun solution(nums: IntArray): Int {
var answer = 0
var num: Int
for (i in nums.indices){
for (j in i+1 until nums.size){
for (k in j+1 until nums.size){
num = nums[i]+nums[j]+nums[k]
if(isPrime(num)) answer++
}
}
}
return answer
}
}
역시 이것 또한 테스트 케이스를 돌려보니 통과.
막상 적다보니 아까보다 줄이 더 늘어난 것 같은 착각이 들었지만, 어쨌든 이번에 함수를 사용하면 어떤 점이 좋은지, 그리고 불리언 타입을 사용하는 방법에 대해서도 알았으니 됐다.
뭐든 남으면 피가 되고 살이 되는 법이다.
2. Git / Github 기초
오늘 Git / Github 특강이 있어서 들었는데 직접 두 눈으로 보면서 왜 개발자들이 Git을 선호하는지 알게 됐고, 어떻게 Git을 써야하는지 조금은 이해할 수 있었다. (조금이라고 한 것은 아직 이걸 직접 사용하지 않아서, 그리고 이걸로 TIL도 작성할 수 있다고 하는데 어떻게 이걸로 TIL을 작성할 수 있는지 몰라서.)
다음은 오늘 Git / Github 특강에서 들은 내용을 내 나름대로 정리한 글이다.
(사실 강의 내용은 이것보다 더 많은데 기억을 토대로 적다보니 너무 간단해져버렸다.)
리눅스 명령어
Git 강의인데 뜬금없이 웬 리눅스 명령어부터 보여주냐 할 수 있지만, 가장 기초적인 부분이라기에 먼저 정리해봤다.
1) pwd (print working directory)
현재 사용하는 폴더가 무엇인지 보여주는 명령어다.
2) ls (list)
내 폴더 안에 있는 폴더와 파일 내역을 보여주는 명령어다.
3) ls -a (list all)
기본적인 기능은 2번과 같지만, 숨겨진 파일까지 모두 보여주는 명령어다.
4) cd + 폴더명
ls 명령어에서 확인된 폴더로 이동시켜주는 명령어다.
※ cd.. → 현재 있는 폴더보다 상위 폴더로 이동시켜준다.
5) mkdir + 폴더명 (make directory)
폴더를 만들 수 있는 명령어다. 폴더명을 붙여서 폴더의 이름도 지정할 수 있다.
6) touch + 파일명
파일을 만들 수 있는 명령어다. 파일명을 붙여서 파일의 이름도 지정할 수 있다.
(확장자도 별도로 지정할 수 있다. 안 붙여도 상관 없음)
Git/Github 필수 명령어
1) git init
코드 관리를 시작하는 명령어다. 프로젝트 시작 전에 한 번만 입력하면 된다.
※ 현재 있는 폴더 내에 .git이라는 폴더가 생성되서 모든 기록을 저장하므로 정확한 프로젝트 폴더(경로)에서 입력해야 한다.
2) git add / git commit
두 명령어 다 코드를 저장하기 위한 명령어인데 쓰임새는 다르다.
- git add + 파일명 : 저장하기 전 저장할 파일을 지정. 'add.'를 입력하면 모든 파일을 지정한다.
- git commit -m "메시지" : 변경된 사항들을 저장한다. 메시지를 통해 뭘 변경했는지 한 눈에 확인할 수 있다.
※ git status : add / commit 여부를 확인하는 명령어.
3) git log
저장한 내역을 확인할 수 있는 명령어다.
이제 여기서부터는 Github와 연동하여 쓸 수 있는 명령어들이다.
4) git clone
Github에 저장되어 있는 repository를 복사하는 명령어이다.
5) git push
내가 작업한 것들을 Github로 보내는 명령어이다.
6) git pull
Github에 있는 작업 내역들을 내 작업 내역에 반영시키는 명령어이다.
※ 상대방이 push한 파일이 있으면 무조건 pull을 먼저 한 뒤에 push를 해야 한다. 그렇지 않으면 오류가 생김.
'[TIL][내일배움캠프]' 카테고리의 다른 글
[내일배움캠프][TIL] 23.11.27 (월) - K번째수 정렬, Kotlin 문법 기초 (1) | 2023.11.27 |
---|---|
[내일배움캠프][TIL] 23.11.24 (금) - 콜라 문제, 앱개발 강의 (1) | 2023.11.24 |
[내일배움캠프][TIL] 23.11.23 (목) - 기사단원의 무기, 개발자의 마음가짐 (2) | 2023.11.23 |
[내일배움캠프][TIL] 23.11.21 (화) - 두 개 뽑아서 더하기, it (1) | 2023.11.21 |
[내일배움캠프][TIL] 23.11.20 (월) - 숫자 문자열과 영단어, Git/Github (0) | 2023.11.20 |