문제
수학에서 보고 말하기 수열(look-and-say sequence)은 다음과 같이 시작하는 정수 수열입니다.
1, 11, 21, 1211, 111221, 312211, ...
이 수열의 다음 항을 만들려면, 바로 앞 항의 자릿수를 왼쪽부터 읽어가며 같은 숫자가 연속해서 나타나는 그룹별로 '연속 개수 + 해당 숫자' 형태로 적어 나가면 됩니다.
예를 들어, 1211의 다음 항은 다음과 같습니다.
111221
그 이유는 1211을 소리 내어 읽으면 다음과 같기 때문입니다.
1이 한 개, 2가 한 개, 1이 두 개 → 11 12 21 → 111221
즉, 우리는 숫자 n을 입력받아 보고 말하기 수열의 처음 n개 항을 배열로 반환하는 JavaScript 함수를 작성해야 합니다.
접근 방법
핵심 아이디어는 간단합니다. 문자열로 표현된 현재 항을 왼쪽부터 한 글자씩 살펴보면서 연속된 같은 숫자의 개수를 세고, 그룹이 바뀔 때마다 '개수 + 숫자'를 결과 문자열에 이어 붙입니다. 이 변환 과정을 n번 반복하면 원하는 수열을 얻을 수 있습니다.
예제 코드
const num = 12;
const generateSequence = (num = 1) => {
const lookAndSay = (val) => {
let res = '';
let chars = (val + ' ').split('');
let last = chars[0];
let count = 0;
chars.forEach(c => {
if(c === last){
count++;
}else{
res += (count + '') + last;
last = c;
count = 1;
};
});
return res;
}
let start = 1;
const res = [];
for(let i = 0; i < num; i++){
res.push(String(start));
start = lookAndSay(start);
};
return res;
};
console.log(generateSequence(num));코드 설명
- lookAndSay 함수: 하나의 항(문자열)을 받아 다음 항을 계산합니다. 문자열 끝에 공백을 붙여 split하는 것은 마지막 그룹도 누락 없이 처리하기 위한 장치입니다.
- last와 count 변수: 현재 검사 중인 숫자와 그 숫자가 연속으로 나타난 횟수를 추적합니다.
- forEach 루프: 각 문자를 순회하며 이전 문자와 같으면 count를 증가시키고, 다르면 지금까지 세어 온 'count + last'를 결과에 추가한 뒤 새로운 그룹을 시작합니다.
- 메인 루프: 첫 항인 1부터 시작해 lookAndSay를 num번 반복 호출하며 각 항을 결과 배열에 차례로 저장합니다.
실행 결과
위 코드를 실행하면 콘솔에 다음과 같이 출력됩니다.
[ '1', '11', '21', '1211', '111221', '312211', '13112221', '1113213211', '31131211131221', '13211311123113112211', '11131221133112132113212221', '3113112221232112111312211312113211' ]
참고: 수열의 흥미로운 특징
보고 말하기 수열은 몇 가지 재미있는 성질을 가집니다. 수학자 존 호튼 콘웨이(John Horton Conway)의 분석에 따르면, 각 항의 자릿수 증가율은 시간이 지남에 따라 콘웨이 상수(약 1.303577)로 수렴합니다. 또한 1로 시작하는 이 수열에는 1, 2, 3의 숫자만 등장하며, 4 이상의 숫자는 어떤 항에도 절대 나타나지 않습니다.