백준 - JAVA/기본 수학 2
[백준] 11653번 : 소인수분해 _ JAVA ( 주석 설명 )
wch_s
2023. 2. 2. 19:22
1번
( ⌜2581번 : 소수⌟에서 사용한 방법 )
→ '에라토스테네스의 채'를 이용한 소수 판별
- 저번 문제에서 사용한 '에라토스테네스의 채'를 한 번 이용해보고 싶어 작성해 보았다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
public static boolean [] num_ary; //solve 함수에서 배열의 값이 정해지므로 static 으로 선언했다.
//시간복잡도 : O(log(logN))
//1. 입력받은 정수 범위까지 소수 판별
//2. 소수만을 따로 추출
//3. 입력받은 수를 소수만으로 따로 나누어, 떨어지는 수를 StringBuilder 에 저장
//4. N이 1이 될 때까지 반복
//5. 소인수분해 값 출력
//공간복잡도 : O(N)
//소수 판별을 위한 범위 정수 배열 + 소수만을 담은 배열
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); //빠른 실행을 위해 버퍼를 이용해 입력을 받는다.
int N = Integer.parseInt(br.readLine()); //소인수분해 할 정수를 입력받는다.
num_ary = new boolean[N+1]; //입력받은 정수까지의 소수를 구하기 위한 범위 정수 배열
solve(N); //소수 판별 함수 실행
int[] prime = new int[N]; //소수만을 담은 배열
int index = 0; //소수만을 집어 넣기 위해 생성한 변수
for(int i=1;i<=N;++i){ //boolean 배열에서 소수인 부분만 따로 뽑아내, prime 배열에 집어넣는다.
if(!num_ary[i]) {
prime[index] = i;
index++;
}
}
StringBuilder sb = new StringBuilder(); //출력을 위한 StringBuilder 선언
int i = 0; //소수만을 담은 배열의 변수
while(N!=1){ //N이 소인수로 완전히 분해되어 1이 될 때까지 반복을 한다.
while(prime[i]!=0){ //prime 배열의 default 값은 0으로, 저장된 소수 이후 값이 나올 때까지 반복한다.
if(N%prime[i]==0){ //입력받은 N 이전의 소수로 나누어질 때
sb.append(prime[i]).append('\n'); //출력 StringBuilder 에 추가해준다.
N /= prime[i]; //N을 해당 소수로 나눈다.
i = 0; //똑같은 소수들의 곱이 나올수도 있으므로, 가장 작은 소수부터 계산을 다시 시작한다.
}
else //나누어지지 않았을 경우 다음 소수로 넘어간다.
i++;
}
}
System.out.println(sb); //소인수분해 값 출력
}
//시간복잡도 : O(log(logN))
//공간복잡도 : O(N)
private static void solve(int num) { //num 까지 소수 찾기
num_ary[0] = true; //0은 소수가 아니다. //입력 범위에 포함되지 않으나, 넣어보았다
num_ary[1] = true; //1은 소수가 아니다.
for(int i=2;i<=Math.sqrt(num);++i){ //sqrt(num)보다 큰 수일 경우 약수가 아니므로 검사할 필요가 없거나, 이전 약수와의 곱으로 소수가 아님을 알아낼 수 있으므로 검사할 필요가 없다.
for(int j=i*i;j<=num;j+=i){ //에라토스테네스의 채 로 각각의 배수를 전부 지워낸다.
num_ary[j] = true;
}
}
}
}
2번
→ N+α 만큼 비교하여 소인수분해
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
static StringBuilder sb = new StringBuilder();
//시간복잡도 : O(N)
//공간복잡도 : 자료구조 x
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine()); //소인수분해 할 정수 N을 입력받는다.
solve(N); //소인수분해 함수 실행
System.out.println(sb); //소인수분해 값 출력
}
//시간복잡도 : O(N)
//공간복잡도 : 자료구조 x
private static void solve(int num) {
while(num>1){ //소인수분해가 완료될 때까지 반복한다.
for(int i=2;i<=num;++i){ //2부터 차례대로 소인수분해를 시작한다.
if(num%i==0) {
num/=i; //소인수를 찾으면 num 을 곧바로 나눠준다.
sb.append(i).append('\n'); //StringBuilder 출력에 더해준다.
i--; //큰 소수의 곱일 경우, 처음으로 돌아가 계산을 하는 반복을 줄여주기 위한 구문이다.
}
}
}
}
}
3번
→ 합성수, 소수의 성질을 이용한 소인수분해
- 이전에 공부했던 O(N)만큼의 반복이 아닌, O(sqrt(N))까지의 계산만으로 소수를 구하는 방식과 유사하다.
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Main {
//전체 시간복잡도 : O(sqrt(N))
//전체 공간복잡도 : 자료구조 x
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
int N = Integer.parseInt(br.readLine()); //소인수분해 할 정수 N을 입력받는다.
solve(N); //소인수분해 함수 실행
}
//시간복잡도 : O(sqrt(N))
//공간복잡도 : 자료구조 x
private static void solve(int num) {
//모든 수 num 은 곱셈으로 분해하면 sqrt(num)*sqrt(num)을 중심으로 대칭을 이루고 있다.
//따라서 그 이후의 값은 앞뒤만 바뀔 뿐 똑같으므로 소인수를 구할 때 계산할 필요가 없다.
//'에라토스테네스의 채'(?)처럼 2부터 차례대로 소수들이 나눌 수 있을 때까지 최대한 나누고 그 소인수들을 출력한다.
for(int i=2;i<=Math.sqrt(num);++i){
while(num%i==0){
num/=i;
System.out.println(i);
}
}
//여기에서 num 이 제곱수가 아니면 나머지가 1로 완벽히 나누어 떨어지지 않는다.
//이 수는 sqrt(num)보다 큰 수이고, 이 수로 num 을 나눈 수는 sqrt(num)보다 작은 수로 앞에서 이미 구한 소인수들의 곱이다.
//따라서 이 수는 num 의 소수가 되므로 출력을 해준다. (num!=1일 경우)
if(num!=1){
System.out.println(num);
}
}
}