오늘 한 것 코딩 테스트 연습 소수 찾기 문제 설명
1부터 입력받은 숫자 n 사이에 있는 소수의 개수를 반환하는 함수, solution을 만들어 보세요.
소수는 1과 자기 자신으로만 나누어지는 수를 의미합니다. (1은 소수가 아닙니다.)
제한사항
n은 2이상 1000000이하의 자연수입니다.
나의 답
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 function firstSolution (n ) { const min = Math .sqrt(n); const array = Array .from({ length : n - 1 }, (_, i ) => i + 2 ); const minArr = Array .from({ length : min }, (_, i ) => i + 1 ); let primeArr = array; for (let i = 0 ; i < minArr.length; i++) { let cnt = 0 ; for (let j = 1 ; j < minArr[i]; j++) { if (minArr[i] % j === 0 ) cnt++; } if (cnt === 1 ) { primeArr = primeArr.filter((v ) => v % minArr[i] !== 0 || v === minArr[i]); } } return primeArr.length; } console .log(firstSolution(10 )); console .log(firstSolution(5 ));
처음에는 성능은 신경쓰지 않고 생각 나는데로 만들었다.
효율성 검사에서 탈락했다. 지금 다시 보니 그럴만한 코드다.
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 function secondSolution (n ) { const min = Math .sqrt(n); const primeArr = []; let currentArr = Array .from({ length : n - 1 }, (_, i ) => i + 2 ); const isPrime = (num ) => { let cnt = 0 ; for (let i = 1 ; i < num; i++) { if (num % i === 0 ) cnt++; } return cnt === 1 ; }; for (let i = 2 ; i < min; i++) { if (isPrime(i)) { primeArr.push(i); currentArr = currentArr.filter((v ) => v % i !== 0 ); } } return [...primeArr, ...currentArr].length; } console .log(secondSolution(10 )); console .log(secondSolution(5 ));
나름 다시 고민해서 구현한 코드이다.
소수 판별 함수를 따로 분리하고 filter()
메서드로 소수의 배수들을 제한다.
하지만 효율성 검사에서 역시나 실패했다.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const currentArr = Array .from({ length : n - 1 }, (_, i ) => i + 2 );const isPrime = (num ) => { let cnt = 0 ; for (let i = 1 ; i < num; i++) { if (num % i === 0 ) cnt++; } return cnt === 1 ; }; for (let i = 2 ; i * i < n; i++) { if (isPrime(i)) { for (let j = i + i - 2 ; j < n; j += i) currentArr[j] = 0 ; } } return currentArr.filter((v ) => v).length;console .log(thirdSolution(10 )); console .log(thirdSolution(5 ));
정말 불필요한 부분은 최대한 제거했다. Math.sqrt()
메서드 조차도 뺴버렸다.
filter()
로 순회하던 과정을 제거하고 for
문을 통해 소수의 배수(j += i
)로 순회하며 값 0을 할당한 뒤, 마지막에 filter()
로 모든 0을 삭제한다.
위 세가지 경우에 대해 성능을 검사해보니, 3번이 다른 두 경우보다 확실히 유의미한 성능차를 보였다.
데이터가 커질 수록 자료구조를 순회하는 방법이 중요함을 다시금 느낀다.
소수 만들기 문제 설명
주어진 숫자 중 3개의 수를 더했을 때 소수가 되는 경우의 개수를 구하려고 합니다. 숫자들이 들어있는 배열 nums가 매개변수로 주어질 때, nums에 있는 숫자들 중 서로 다른 3개를 골라 더했을 때 소수가 되는 경우의 개수를 return 하도록 solution 함수를 완성해주세요.
제한사항
nums에 들어있는 숫자의 개수는 3개 이상 50개 이하입니다.
nums의 각 원소는 1 이상 1,000 이하의 자연수이며, 중복된 숫자가 들어있지 않습니다.
나의 답 1 2 3 4 5 6 7 8 const isPrime = (num ) => { let cnt = 0 ; for (let i = 1 ; i < num; i++) { if (num % i === 0 ) cnt++; } return cnt === 1 ; };
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 function solution (nums ) { const odd = []; const even = []; for (let i = 0 ; i < nums.length; i++) { if (nums[i] % 2 === 0 ) even.push(nums[i]); else odd.push(nums[i]); } let cnt = 0 ; for (let i = 0 ; i < even.length - 1 ; i++) { for (let j = i + 1 ; j < even.length; j++) { for (let k = 0 ; k < odd.length; k++) { if (isPrime(even[i] + even[j] + odd[k])) cnt++; } } } for (let i = 0 ; i < odd.length - 2 ; i++) { for (let j = i + 1 ; j < odd.length - 1 ; j++) { for (let k = j + 1 ; k < odd.length; k++) { if (isPrime(odd[i] + odd[j] + odd[k])) cnt++; } } } return cnt; } console .log(solution([1 , 2 , 3 , 4 ])); console .log(solution([1 , 2 , 7 , 6 , 4 ]));
2를 제외한 모든 소수는 홀수이다. 본 문제에서는 중복되지 않는 1 이상의 세 수를 더한 소수를 구하기 때문에 발생 가능한 수 중 가장 작은 소수는 7이다.
짝수와 홀수를 구분한 후, 홀수를 만들 수 있는 조합은 짝수 + 짝수 + 홀수
와 홀수 + 홀수 + 홀수
다.
소수 발생 가능성이 있는 세수의 조합만을 계산하여 소수인지 판별한다.
코드의 길이는 길지만 아래 단순 계산하는 코드보다 유의미하게 높은 성능 차이를 보였다. (1부터 100까지의 배열에서 약 2배의 성능 차이)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function solution (nums ) { let answer = 0 ; for (let i = 0 ; i < nums.length - 2 ; i++) { for (let j = i + 1 ; j < nums.length - 1 ; j++) { for (let k = j + 1 ; k < nums.length; k++) { if (isPrime(nums[i] + nums[j] + nums[k])) answer++; } } } return answer; } console .log(solution([1 , 2 , 3 , 4 ])); console .log(solution([1 , 2 , 7 , 6 , 4 ]));
모든 경우의 수를 생성한 후, 소수인지 판별하는 단순한 알고리즘이다.
첫번째 경우보다 깔끔해보이지만 성능은 모든 경우의 수를 계산하기 떄문에 좋지 못하다.
약수의 개수와 덧셈 문제 설명
두 정수 left와 right가 매개변수로 주어집니다. left부터 right까지의 모든 수들 중에서, 약수의 개수가 짝수인 수는 더하고, 약수의 개수가 홀수인 수는 뺀 수를 return 하도록 solution 함수를 완성해주세요.
제한사항
나의 답 1 2 3 4 5 6 7 8 9 10 11 12 13 function solution (left, right ) { let sum = 0 ; for (let i = left; i <= right; i++) { let cnt = 0 ; for (let j = 1 ; j <= i; j++) { if (i % j === 0 ) cnt++; } cnt % 2 === 0 ? (sum += i) : (sum -= i); } return sum; }
삼항 조건 연산자가 편하긴 한데, if..else 문을 쓰는게 더 직관적인지 애매하다.
쉬운 문제
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 function solution (left, right ) { let num = []; for (let i = left; i <= right; i++) { let cnt = 0 ; for (let j = 1 ; j <= i; j++) { if (i % j === 0 ) cnt++; } num.push([i, cnt]); } return num.reduce( (acc, cur) => (cur[1 ] % 2 === 0 ? acc + cur[0 ] : acc - cur[0 ]), 0 ); }
배열 메서드 연습하고 싶어서 꼬아서 놀았다. 의미없다.
감탄한 타인의 답 1 2 3 4 5 6 7 8 9 10 11 function solution (left, right ) { var answer = 0 ; for (let i = left; i <= right; i++) { if (Number .isInteger(Math .sqrt(i))) { answer -= i; } else { answer += i; } } return answer; }
제곱수(자연수를 제곱해 구해지는 수)의 약수 개수는 항상 홀수이다…같은 수를 두번 곱한 경우의 짝이 없는 약수가 1개 생기기 때문…수학 상식도 재밌다..
두 개 뽑아서 더하기 문제 설명
정수 배열 numbers가 주어집니다. numbers에서 서로 다른 인덱스에 있는 두 개의 수를 뽑아 더해서 만들 수 있는 모든 수를 배열에 오름차순으로 담아 return 하도록 solution 함수를 완성해주세요.
제한사항
numbers의 길이는 2 이상 100 이하입니다.
numbers의 모든 수는 0 이상 100 이하입니다
나의 답 1 2 3 4 5 6 7 8 9 10 11 12 13 function solution (numbers ) { const answer = []; for (let i = 0 ; i < numbers.length; i++) { for (let j = i + 1 ; j < numbers.length; j++) { if (i !== j) answer.push(numbers[i] + numbers[j]); } } return [...new Set (answer.sort((a, b ) => a - b))]; } console .log(solution([2 , 1 , 3 , 4 , 1 ]));
처음에는 j = 0
으로 선언하여 불필요한 연산을 했다. 한번만 더 생각하자.
나누어 떨어지는 숫자 배열 문제 설명
array의 각 element 중 divisor로 나누어 떨어지는 값을 오름차순으로 정렬한 배열을 반환하는 함수, solution을 작성해주세요. divisor로 나누어 떨어지는 element가 하나도 없다면 배열에 -1을 담아 반환하세요.
제한사항
arr은 자연수를 담은 배열입니다.
정수 i, j에 대해 i ≠ j 이면 arr[i] ≠ arr[j] 입니다.
divisor는 자연수입니다.
array는 길이 1 이상인 배열입니다.
나의 답 1 2 3 4 5 6 7 function solution (arr, divisor ) { const answer = arr.filter((v ) => v % divisor === 0 ).sort((a, b ) => a - b); return answer.length ? answer : [-1 ]; } console .log(solution([5 , 9 , 7 , 10 ], 5 ));
3진법 뒤집기 문제 설명
자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.
제한사항
n은 1 이상 100,000,000 이하인 자연수입니다.
나의 답 1 2 3 4 5 6 7 8 function solution (n ) { const num = n.toString(3 ).split("" ).reverse().join("" ); return parseInt (num, 3 ); } console .log(solution(45 ));
1 2 3 4 5 6 7 8 function solution (n ) { const answer = []; while (n !== 0 ) { answer.unshift(n % 3 ); n = Math .floor(n / 3 ); } return answer.reduce((acc, v, i ) => acc + v * Math .pow(3 , i), 0 ); }
출처: programmers
Nyong’s GitHub