2022. 7. 18. 18:34ㆍ알고리즘/programmers
여러분들 정말 자바스크립트의 기묘한 걸 찾아냈습니다.
혹시 왜이런지 아시는 분 있으면 알려주시면 너무 좋을 것 같아요.
오늘 풀어볼 문제는 프로그래머스 Level 2의 소수 찾기 입니다.
일단 같이 한번 볼까요?
문제 요약
numbers | return |
"17" | 3 |
"011" | 2 |
한자리 숫자가 적힌 종이 조각이 흩어져있습니다. 흩어진 종이 조각을 붙여 소수를 몇 개 만들 수 있는지 알아내려 합니다.
각 종이 조각에 적힌 숫자가 적힌 문자열 numbers가 주어졌을 때, 종이 조각으로 만들 수 있는 소수가 몇 개인지 return 하도록 solution 함수를 완성해주세요.
제한사항
- numbers는 길이 1 이상 7 이하인 문자열입니다.
- numbers는 0~9까지 숫자만으로 이루어져 있습니다.
- "013"은 0, 1, 3 숫자가 적힌 종이 조각이 흩어져있다는 의미입니다.
문제 해석
자 일단 소수를 구하는 알고리즘은 너무너무 유명한 에라스토테네스의 체를 써볼까 합니다.
문자열의 최대 크기가 7자리이고 최대 들어갈 수 있는 수가 9이기 때문에 numbers의 최대는 "9,999,999"입니다.
그러니 에라토스테네스의 체를 최적화한 제곱근 기준으로 루프를 돌리면 배열 크기를 천 만의 제곱근인 3162안으로 끊어낼 수 있습니다.
또 중요한 건 이제 숫자카드의 조합을 모조리 찾아내는게 중요한데요.
여기서 이제 기묘한 모험 시작입니다.
일단 코드 먼저 보여드릴게요.
정답 코드?
function allItems(numbers) {
if (numbers.length === 1) return numbers;
const result = new Set();
numbers.forEach((fixed, i, arr) => {
const rest = arr.filter((_, ai) => i !== ai);
const combi = allItems(rest);
result.add(fixed);
const item = combi.forEach(v => result.add(fixed + v));
});
return [...result];
}
function solution(numbers) {
const targets = allItems(numbers.split(""));
const max = Math.max(...targets);
const primes = Array.from({length: max + 1}, () => true);
primes[0] = false;
primes[1] = false;
for (let i = 2; i < Math.sqrt(max); i++) {
if (primes[i]) {
for (let j = 2; j < max; j++) {
if (i * j > max) break;
primes[i * j] = false;
}
}
}
return targets.reduce((acc, val) => primes[val] ? acc + 1 : acc, 0);
}
팁 1. 순열 함수를 변형시킨 allItmes()에 대하여..
음.. 중복제거를 위해 Set()을 사용했습니다.
또, 원래 제가 쓰던 순열함수는 level을 인자로 넣어주면서 계속 재귀를 돌려줬었는데 모든 요소를 그냥 다 넣으면 되니까 안넣어도 됩니다.
일단 이렇게 하면 결과가 어떻게 나오냐면..
입력값 | 출력 |
"17" | ['1', '17', '7', '71'] |
"011" | [ '0', '01', '011', '1', '10', '101', '11', '110' ] |
"7492" | [ '7', '74', '749', '7492', '742', '7429', '79', '794', '7942', '792', '7924', '72', '724', '7249', '729', '7294', '4', '47', '479', '4792', '472', '4729', '49', '497', '4972', '492', '4927', '42', '427', '4279', '429', '4297', '9', '97', '974', '9742', '972', '9724', '94', '947', '9472', '942', '9427', '92', '927', '9274', '924', '9247', '2', '27', '274', '2749', '279', '2794', '24', '247', '2479', '249', '2497', '29', '297', '2974', '294', '2947' ] |
일단 모든 경우를 다 string 형태로 내보내줍니다.
왜 Number를 allItems에 안넣어줬냐면 예시 2번처럼 0이 있는 경우는 재귀를 돌다가 01이 1이 되면서 101이라는 케이스를 잡아내지 못하더라구요. 그냥 11로 나와요. 하위분기에서 01이 1이되니까
근데 웃긴게 이 값이 아래에 설명할 소수인지 아닌지 정의한 배열의 index로 사용하는데 그냥 string으로 넣어도 인식을 한다는게 진짜 이해가 안되버린다는 거에요.
일단 아래도 보고 다시 이야기해요 우리
팁 2. 에라토스테네스의 체(소수 구하기)
const max = Math.max(...targets); //일단 여기부터 이해 안됨.. 이거 string인데 왜 이거 되는거죠..?
const primes = Array.from({length: max + 1}, () => true); // 여기도 2222, 일단 각 값을 index로 쓸거라서 max + 1해줬습니다.
primes[0] = false;
primes[1] = false; // 0, 1은 일단 소수아니니까 false
for (let i = 2; i < Math.sqrt(max); i++) { // 합성수는 무조건 그 수의 제곱근 이하에서 약수가 있습니다. ex 1, 3, 9
if (primes[i]) {
for (let j = 2; j < max; j++) {
if (i * j > max) break;
primes[i * j] = false;
}
}
}
일단 가장 이해안되는건 왜.. 도대체 javascript는 이 string을 숫자로 인식하는건가? 입니다.
알아서 그냥 치환되는건지... 잘 모르겠어요.
아래 reduce문에서도 그냥 prime의 index에 접근할 때 string 형식으로 접근했을 텐데.. (정의할 땐 위에 보시는 것 처럼 숫자로 넣어줬습니다.) 그냥 숫자로 인식해줍니다.
javascript의 배열은 object 객체라 index에 뭘 넣어도 인식가능하다고는 알고 있었는데 '01'과 1은 달라야하지 않나요...?
일단 좀 더 알아봐야할 것 같습니다.
아무튼 이렇게 최댓값까지 소수인지 아닌지 판별해주는 primes를 잘 설정해줍니다.
그 뒤로는 적절히 targets를 순회하면서 소수라면 카운터를 1올려주면 정답이 나오네요.
흠... 진짜 자바스크립트는 신기하네요.
js 객체에 대해서 좀 더 공부해야할 것 같습니다.
더 알아내는 대로 포스트에 추가해서 공유드릴게요.
그럼 이만!
'알고리즘 > programmers' 카테고리의 다른 글
[JS] 후보키 - every를 아시나요? (0) | 2022.07.20 |
---|---|
[JS] 게임맵 최단거리 - 미뤄뒀던 BFS에 대해 (0) | 2022.07.19 |
[JS] 빛의 경로 사이클 (0) | 2022.07.17 |
[JS] 프린터 (0) | 2022.07.16 |
[JS] 수식 최대화 (0) | 2022.07.15 |