알고리즘 문제를 푸는 도중, Math.pow() 함수에 대해 깊이 생각해보지 못한 부분을 발견하여 이를 정리해보려 합니다.
Math.pow(base, exponent)는 JavaScript에서 제공하는 내장 함수로, 특정 숫자 base를 exponent 거듭제곱한 값을 반환합니다.
MDN 공식 문서
예시:
console.log(Math.pow(2, 3)); // 8
console.log(Math.pow(5, -1)); // 0.2
console.log(Math.pow(10, 0)); // 1
이 함수를 직접 구현한다면 어떻게 해야 할까요?
단순한 방법으로는 다음과 같이 반복문을 사용하는 방식이 떠오릅니다.
function naivePow(base: number, exponent: number): number {
let result = 1;
let exp = exponent;
while (exp > 0) {
result *= base;
exp--;
}
return result;
}
위와 같은 방식은 간단하지만, 다음과 같은 한계가 존재합니다:
exponent의 크기에 비례하여 반복 횟수가 증가하므로, 시간 복잡도가 O(n)입니다.exponent가 큰 경우, 특히 알고리즘 문제에서 다루는 큰 수 연산에서는 비효율적입니다.Math.pow()의 연산을 최적화하려면 이진 거듭제곱(Binary Exponentiation) 알고리즘을 사용할 수 있습니다.
이 방법을 사용하면 시간 복잡도를 O(log n)으로 줄일 수 있습니다.
exponent를 이진수로 표현하면, 거듭제곱 계산을 반복문이 아닌 분할 정복 방식으로 처리할 수 있습니다.exponent가 짝수인지, 홀수인지에 따라 다음과 같이 나누어 처리합니다:exponent % 2 === 0): exponent % 2 === 1): ( 13 )을 이진수로 표현하면 ( 1101 ) (2진법)입니다.
이는
로 표현할 수 있습니다.
이진 거듭제곱 과정
최종 결과: result = 1594323
function binaryExponentiation(base: number, exponent: number): number {
let result = 1;
let currentBase = base;
let exp = exponent;
while (exp > 0) {
// 홀수인 경우
if (exp % 2 === 1) {
result *= currentBase;
}
// base^exponent/2 계산
currentBase *= currentBase;
exp = Math.floor(exp / 2);
}
return result;
}
exponent의 비트 수에 비례합니다.알고리즘 문제에서는 보통 결과를 특정 수로 나눈 나머지 값을 구하는 모듈러 연산이 필요합니다.
이진 거듭제곱은 모듈러 연산과도 잘 결합할 수 있습니다.
function modularExponentiation(base: number, exponent: number, mod: number): number {
let result = 1;
let currentBase = base % mod; // base를 미리 mod로 나눔
let exp = exponent;
while (exp > 0) {
if (exp % 2 === 1) {
result = (result * currentBase) % mod;
}
currentBase = (currentBase * currentBase) % mod;
exp = Math.floor(exp / 2);
}
return result;
}
Math.pow() 이진 거듭제곱을 이해하고 활용하면 도움이 될것같습니다.