You are on page 1of 6

오른편 절단 가능 소수

수학자들에게 소수란 매우 흥미 있는 연구 주제이다. 소수(prime number)란 약수


가 1과 자기 자신밖에 없는 1보다 큰 자연수를 말한다. 수학자들은 소수를 연구하면
서 특이한 소수들을 발견하여 이름을 명명하였다. 메르센 소수, 페르마 소수, 쌍둥이
소수 등이 그 예이다.

우리에게는 생소하지만 오른편 절단 가능 소수가 있다. 이 소수는 오른쪽부터 하


나씩 제거해도 계속 소수가 되는 소수이다.

크기가 네 자리인 7193을 예로 들어보자. 7193은 소수이고, 7193의 오른편 숫자 3


을 제거하여 남은 719도 소수이다. 719의 오른편 숫자 9를 제거하여 남은 71도 소수
이다. 71의 오른편 숫자 1을 제거하여 남은 7도 소수이다. 따라서 7193은 오른편 절
단 가능 소수이다.

입력
자릿수 n이 정수로 입력된다.(1 <= n <= 10)

출력
1. n자리로 이루어진 오른편 절단 가능 소수들을 한 줄에 하나씩 오름차순으로 출
력한다.
2. 마지막 줄에 출력된 오른편 절단 가능 소수들의 개수를 출력한다.

입력 예 출력 예
23
29
31
37
53
2
59
71
73
79
9

절단소수 1
풀이

이 문제는 n자리의 숫자들 중 오른편 절단 가능 소수를 찾고 그 개수를 출력하는 문제이


다. 먼저 길이가 n인 순열을 생성하고, 소수인지 판별해보는 전체탐색법으로 문제를 해결
해 보자.
줄 코드 참고
1 #include<stdio.h>
2
3 int n, cnt;
4 int isprime(int x)
5 {
6 if(x<2) return 0;
7 for(int i=2; i*i<=x; i++)
8 if(x%i==0)
9 return 0;
10 return 1;
11 }
12
13 void f(int num, int len)
14 {
15 if(len==n)
16 {
17 if(num==0) return ;
18
19 int flag=1;
20 int temp=num;
21 while(temp)
22 {
23 if(!isprime(temp))
24 return ;
25 temp /= 10;
26 }
27 cnt++;
28 printf("%d\n", num);
29 return ;
30 }
31 else
32 {
33 for(int i=1; i<=9; i++)

절단소수 2
줄 코드 참고
34 f(num*10+i, len+1);
35 }
36 }
37
38 int main()
39 {
40 scanf("%d", &n);
41 f(0, 0);
42 printf("%d", cnt);
43 }

4~11행의 isprime(x)함수는 정수 x가 소수이면 1을 리턴, 소수가 아니면 0을 리턴하는


함수이다.

33~34행에서 1~9를 뒤에 오는 숫자로 추가하여 자릿수를 늘려간다. 여기서 0은 미리


제외 시켰다. 이 방법은 수학적 탐색영역의 배제의 한 방법으로 0으로 끝나는 수는 짝
수이므로 소수가 될 수 없으므로 미리 제외시켰다.

백트래킹 함수의 의미는 다음과 같다.

f(num, len) = 현재 숫자 num과 그 길이 len을 의미

메인 함수에서 f(0, 0)으로 호출하면, 다음 구조로 호출이 이루어진다.

절단소수 3
이런 구조로 길이가 n인 순열이 생성되고, 생성된 수를 4~11행의 소수 판별함수로 오른
쪽부터 하나씩 절단하면서 소수인지 판단한다. 이 때 n/10으로 수를 분리하면 된다. 만약
오른쪽을 절단하면서 체크하는 과정에서 하나라도 소수가 아니면 바로 취소하고, 다음 숫
자로 넘어간다. 만약 오른편 절단 가능 소수임이 판단되면 전체 개수를 저장하는 변수 cnt
값을 1증가시키고, 그 수를 화면에 바로 출력한다.

이 함수의 계산량은 순열을 생성하는 부분에    이 되고, 생성된 길이가 n인 각 수


x에 대해  
  의 시간이 걸리므로, 전체 계산량은   ∙
  이다. n이 6이상만
되어도 속도가 점점 느려짐을 알 수 있다.

이보다 더 탐색영역을 배제할 수 있는 부분을 생각해보자. 앞에서 0을 배제시켰듯이


수학적 배제 방법을 생각해보자.

1. 한 자릿수 중 소수는 2, 3, 5, 7 밖에 없다. 따라서 제일 높은 자릿수의 값은 2, 3, 5, 7만


될 수 있다. (∵오른편 절단 가능 소수이므로)
2. 두 자릿수 이상 넘어가면서 마지막 자릿수 값은 짝수가 될 수 없고(∵2의 배수), 마찬가지로
5도 될 수 없다(∵5의 배수). 따라서 남는 숫자는 1, 3, 7, 9만 남게 된다. 10개의 자릿수를
모두 다 탐색하는 것에 비해 가짓수가 현저히 줄어들게 됨으로 탐색이 빨라진다. (가지치기)
3. 자릿값을 늘려갈 때 현재 숫자가 소수인지 판별하여 소수인 숫자에만 뒤에 1, 3, 7, 9를
붙여가며 늘려간다. 숫자가 커지면 소수 판별에도 시간이 많이 걸리므로 시간을 줄이기
위해  
  소수 판별 알고리즘을 사용한다.

이 배제 방법을 토대로 소스를 개선시켜보자.


줄 코드 참고
1 #include<stdio.h> 26: 두 번째 자
2 릿수부터는 1, 3,
7, 9
3 int n, cnt;
39: 시작 수는
4 2, 3, 5, 7
5 int isprime(int x)
6 {
7 for(int i=2; i*i<=x; i++)
8 if(x%i==0)
9 return 0;
10 return 1;
11 }
12

절단소수 4
줄 코드 참고
13 void f(int num, int len)
14 {
15 if(len==n)
16 {
17 if(isprime(num))
18 {
19 cnt++;
20 printf("%d\n", num);
21 }
22 return ;
23 }
24 else
25 {
26 if(isprime(num))
27 {
28 f(num*10+1, len+1);
29 f(num*10+3, len+1);
30 f(num*10+7, len+1);
31 f(num*10+9, len+1);
32 }
33 }
34 }
35
36 int main()
37 {
38 scanf("%d", &n);
39 f(2,1); f(3,1); f(5,1); f(7,1);
40 printf("%d", cnt);
41 }

이 배제 방법을 토대로 n이 1, 2, 3일 때 결과를 살펴보자.

< N의 크기에 따른 상태 공간 >

n = 1일 때,

n = 2일 때,

절단소수 5
n = 3일 때,

이 소스의 계산량은  


  이며 가지치기 전략에 의해 실질적으로는 수행시간을 더
욱 단축할 수 있다.

절단소수 6

You might also like