Study/Baekjoon

Baekjoon9020*: 골드바흐의 추측, 여러 방법으로 풀기

devyoseph 2021. 10. 14. 03:51

문제

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<T; i++) {
		 int diff = 10000;
		 int diffR = 10000;
	  n = Integer.parseInt(br.readLine());
	  for(int j =2; j<=n/2; j++) {
		  if(prime[j] == false && prime[n-j]==false) {
//값이 동시에 소수라면 저장된다
			  small = j;
			  large = n-j;
			  diff = large - small;
		  }
		  if(diff < diffR) {
			  smallR = small;
			  largeR = large;
	          diffR = diff;
		  }
//현재 계산해준 차이가 최소값보다 작으면 출력할 변수들에 기록한다 
	  }
	 System.out.println(smallR+" "+largeR);
//출력 		 
		 }
}
}

 

다양한 시도들
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<T; i++) {
		 int diffmin =10000;
		 int diff = 10000;
		 n = Integer.parseInt(br.readLine());
	 for(int j = 0; j<669; j++) {
		 small = net[j];
		 large = n - small;
		 if(small<=large) {
			 for(int k =0; k<1229; k++) {
				 if(large == net[k]) {
					 diff = large -small;
				 }
				 if( diff < diffmin) {
					 diffmin = diff;
					 smallR = small;
					 largeR = large;
				 }
			 }
		 }
		 
	 }
	 System.out.println(smallR+" "+largeR);
	 }
}
}

 

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<T; i++) {
	  n = Integer.parseInt(br.readLine());
	  int middle_left = n/2;
	  int middle_right = n/2;
//중앙값부터 왼쪽, 오른쪽으로 나아갈 변수 선언
	  while(true) {
		  if(prime[middle_left] == false && prime[middle_right]==false) {
			 small = middle_left;
			 large = middle_right;
			 
			 break;
//최소값을 구했으므로 즉시 나온다
		  }
		 middle_left--;
		 middle_right++;
//검사를 마치면 각각의 방향으로 이동
		  }
	  System.out.println(small+" "+large);
	  }
}
}

* 코드는 짧아졌지만 메모리가 커진 모습

'Study > Baekjoon' 카테고리의 다른 글

Baekjoon3009: 네 번째 점  (0) 2021.10.14
Baekjoon1085: 직사각형에서 탈출  (0) 2021.10.14
Baekjoon4948: 베르트랑 공준  (0) 2021.10.13
Baekjoon1929: 소수 구하기  (0) 2021.10.13
Baekjoon11653: 소인수분해  (0) 2021.10.13