on
Baekjoon9020*: 골드바흐의 추측, 여러 방법으로 풀기
Baekjoon9020*: 골드바흐의 추측, 여러 방법으로 풀기
문제
1보다 큰 자연수 중에서 1과 자기 자신을 제외한 약수가 없는 자연수를 소수라고 한다. 예를 들어, 5는 1과 5를 제외한 약수가 없기 때문에 소수이다. 하지만, 6은 6 = 2 × 3 이기 때문에 소수가 아니다.
골드바흐의 추측은 유명한 정수론의 미해결 문제로, 2보다 큰 모든 짝수는 두 소수의 합으로 나타낼 수 있다는 것이다. 이러한 수를 골드바흐 수라고 한다. 또, 짝수를 두 소수의 합으로 나타내는 표현을 그 수의 골드바흐 파티션이라고 한다. 예를 들면, 4 = 2 + 2, 6 = 3 + 3, 8 = 3 + 5, 10 = 5 + 5, 12 = 5 + 7, 14 = 3 + 11, 14 = 7 + 7이다. 10000보다 작거나 같은 모든 짝수 n에 대한 골드바흐 파티션은 존재한다.
2보다 큰 짝수 n이 주어졌을 때, n의 골드바흐 파티션을 출력하는 프로그램을 작성하시오. 만약 가능한 n의 골드바흐 파티션이 여러 가지인 경우에는 두 소수의 차이가 가장 작은 것을 출력한다.
입력
첫째 줄에 테스트 케이스의 개수 T가 주어진다. 각 테스트 케이스는 한 줄로 이루어져 있고 짝수 n이 주어진다.
출력
각 테스트 케이스에 대해서 주어진 n의 골드바흐 파티션을 출력한다. 출력하는 소수는 작은 것부터 먼저 출력하며, 공백으로 구분한다.
제한
4 ≤ n ≤ 10,000
추상화
1) 문제이해: 골드바흐의 파티션: 짝수를 두 소수의 합으로 나타낼 수 있다. n이 주어지면 차이가 가장 작은 두 소수를 출력한다
2) n <= 10,000인 제한조건을 활용하여 소수 배열을 만든다
3) n보다 작은 소수를 고르고 small이라고 한다. n-small을 계산하고 large라고 한다
4) 문제의 조건에 알맞은 small과 large값을 찾아 출력한다
<주의사항>
→ small은 끝까지 탐색할 필요가 없다: 4 + 17이나 17 + 4나 같기 때문이다.
이번 풀이에서는 10000개의 숫자에서 소수만 따로 뺀 배열을 만들었다.
small은 두 소수 중 작은 숫자기 때문에 늘 n의 절반 크기보다 작거나 같다
smalll의 크기는 10000의 절반인 5000을 넘을 수 없다
→ large가 소수인지 아닌지 판별해야한다. 소수가 맞다면 둘의 차이를 계산해 그 차이를 계산해주고 판별해야한다
<단계별 풀이>
1) 데이터 크기가 10000인 논리배열에서 소수들만 false로 만든다
2) small은 5000보다 작거나 같으므로 탐색범위를 좁힌다
3) 받은 n값에 대해 탐색을 시작한다. for내부에서 증가하는 j와 n-j index가 false(소수)라면 값을 저장한다
4) 차이를 계산하고 그 차이가 최소값인지 판별한다
5) 최소값을 갱신할 때마다 출력할 변수(smallR, largeR)에 담는다
5) 반복문이 끝날 때 smallR과 largeR을 출력한다
import java.io.*; public class Main { public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); boolean[] prime = new boolean[10001]; int T, n; int small = 0, large=0, smallR=0, largeR=0; //아래에서 사용할 변수들을 미리 정의해준다 int devide =0; for(int i=2; i<10001; i++) { devide = 10000/i; for(int j=2; j<=devide; j++) { prime[i*j] = true; } } prime[0] = true; prime[1] = true; // 소수는 false의 값을 가지고 있다 T = Integer.parseInt(br.readLine()); for(int i =0; i
다양한 시도들
1) 소수로만 이루어진 배열을 만들어서 탐색하면 더 빨라지지 않을까 시도해보았다
10,000까지의 숫자 중 소수의 개수는 1229개여서 이 크기의 배열을 만들고 5000보다 커지는 지점의 index를 구했다(669)
하지만 입력값으로 주어진 n을 효율적으로 사용할 수 없다는 단점이 있었다
위의 경우 j, n-j를 통해 바로 소수인지 판별할 수 있었지만 소수들을 따로 모아두는 경우 일일히 대조해야하기 때문이었다
import java.io.*; public class Main { public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); boolean[] prime = new boolean[10001]; int[] net = new int[1229]; int T, n; int index = 0; int small, large, smallR=0, largeR=0; int devide =0; for(int i=2; i<10001; i++) { devide = 10000/i; for(int j=2; j<=devide; j++) { prime[i*j] = true; } } prime[0] = true; prime[1] = true; for(int i = 0; i<10001; i++) { if(prime[i]==false) { net[index] = i; index++; } } T = Integer.parseInt(br.readLine()); for(int i =0; i
2) 차이가 최소값인 소수를 출력한다에 집중한다
탐색할 때 처음부터 끝까지 탐색하곤 한다. 하지만 조건을 풀어서 해석하면 다음과 같다
i) n이 주어지는데 서로 더해서 n이 되는 숫자 a와 b를 구하여라
ii) 숫자 a와 b의 차이가 최소인 것을 구하라 = a와 b의 값이 가까운 것을 구하여라
→ 가장 이상적인 경우: a = b = n/2
iii) 중앙에서 탐색을 시작해서 먼저 만들어지는 a와 b가 곧 최소의 차이를 가지는 소수이다
Red: 일반적인 탐색 방향 / Blue: 중앙부터 탐색
import java.io.*; public class Main { public static void main(String[] args) throws IOException{ BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); boolean[] prime = new boolean[10001]; int T, n; int small = 0,large=0; int devide =0; for(int i=2; i<10001; i++) { devide = 10000/i; for(int j=2; j<=devide; j++) { prime[i*j] = true; } } prime[0] = true; prime[1] = true; T = Integer.parseInt(br.readLine()); for(int i =0; i
* 코드는 짧아졌지만 메모리가 커진 모습
from http://devyoseph.tistory.com/64 by ccl(A) rewrite - 2021-10-14 04:02:07