프로젝트 오일러 #95: Amicable Chains (우애 체인)
이 문제는 숫자들 사이의 관계를 탐구하는 것으로, 특정 수의 약수의 합을 계산하여 순환하는 우애 체인(amicable chain)을 찾는 것입니다. 다음과 같은 과정을 따릅니다:
1. 정의
• 어떤 수
예를 들자면
• 두 수
예를 들어,
• 우애 체인(amicable chain)은 이러한 약수의 합 계산이 여러 수를 통해 이어지며 다시 원래 수로 돌아오는 경우를 말합니다.
예를 들어,
2. 목표
1.
2. 가장 긴 우애 체인을 구성하는 수열을 찾습니다.
3. 이 체인의 최소값을 구합니다.
3. 예제
•
•
•
•
•
•
이 체인은

제가 작성한 소스입니다.
//------------------------------------------------
// Project Euler #95 - Amicable Chains
// - by Aubrey Choi
// - created at 2015-10-23
//------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <time.h>
#include <math.h>
#define LIMIT 1000000
#define RLIMIT 1000
int primes[RLIMIT];
int pcount = 0;
int sigma1(int n)
{
int v = 1;
for( int i = 0 ; i < pcount && n > 1 ; i++ )
{
if( n%primes[i] ) continue;
int s = 1;
while( n%primes[i] == 0 ) { s = s*primes[i]+1; n /= primes[i]; }
v *= s;
}
return (n>1)?v*(1+n):v;
}
void solve1()
{
primes[pcount++] = 2;
primes[pcount++] = 3;
for( int p = 5 ; p <= RLIMIT ; p++ )
{
bool isPrime = true;
for( int i = 1 ; primes[i]*primes[i] <= p ; i++ )
if( p%primes[i] == 0 ) { isPrime = false; break; }
if( isPrime ) primes[pcount++] = p;
}
static char flag[LIMIT+1];
int value = 1, length = 1;
for( int n = 2 ; n < LIMIT ; n++ )
{
memset(flag, 0, sizeof(flag));
int q = n, t = 0;
do
{
flag[q] = 1;
q = sigma1(q)-q;
t++;
} while( q <= LIMIT && flag[q] == 0 );
if( q == n && t > length ) { value = q; length = t; printf("%d(%d)\n", q, t); }
}
printf("Ans = %d(%d)\n", value, length);
}
int main()
{
clock_t t;
t = clock();
solve1();
printf("Elapsed time is %.3f seconds \n", (float)(clock() - t) / CLOCKS_PER_SEC);
}
코드의 각 부분을 설명하겠습니다.
1. 매크로 정의
#define LIMIT 1000000
#define RLIMIT 1000
• LIMIT: 문제에서 요구하는 범위의 상한 .
• RLIMIT: 소수 계산에 사용할 상한  (최적화를 위한 제한).
2. 전역 변수
int primes[RLIMIT];
int pcount = 0;
• primes: 소수를 저장하는 배열.  이하의 모든 소수가 이 배열에 저장됩니다.
• pcount: 소수의 개수를 추적하는 변수.
3. 약수의 합 계산 함수: sigma1
int sigma1(int n)
{
int v = 1;
for( int i = 0 ; i < pcount && n > 1 ; i++ )
{
if( n%primes[i] ) continue;
int s = 1;
while( n%primes[i] == 0 ) { s = s*primes[i]+1; n /= primes[i]; }
v *= s;
}
return (n>1)?v*(1+n):v;
}
• 약수의 합
• 주어진 숫자
• 반환값은
예를 들어,
• 약수:
•
•
4. 소수 생성 함수: solve1
primes[pcount++] = 2;
primes[pcount++] = 3;
for( int p = 5 ; p <= RLIMIT ; p++ )
{
bool isPrime = true;
for( int i = 1 ; primes[i]*primes[i] <= p ; i++ )
if( p%primes[i] == 0 ) { isPrime = false; break; }
if( isPrime ) primes[pcount++] = p;
}
• RLIMIT 이하의 모든 소수를 생성하고 primes 배열에 저장합니다.
• 소수를 찾기 위해 에라토스테네스의 체 대신 간단한 나눗셈 검사를 사용하였습니다.
5. 우애 체인 탐색
static char flag[LIMIT+1];
• flag 배열은 특정 숫자를 체인 내에서 방문했는지 여부를 기록합니다.
• 매번 새로운 숫자로 시작할 때 flag 배열을 초기화(memset)합니다.
int q = n, t = 0;
do
{
flag[q] = 1;
q = sigma1(q)-q; // 약수의 합에서 자신을 제외
t++;
} while( q <= LIMIT && flag[q] == 0 );
• n 에서 시작하여 약수의 합을 반복적으로 계산하여 체인을 생성합니다.
• 만약 이전에 방문한 숫자가 나오면 순환이 발생합니다.
if( q == n && t > length )
{
value = q;
length = t;
printf("%d(%d)\n", q, t);
}
• 순환이 시작점으로 돌아온다면 q == n이고, 체인의 길이가 이전보다 길다면 업데이트합니다.
'Programming > Project Euler' 카테고리의 다른 글
[C/C++] 프로젝트 오일러 #97 Large Non-Mersenne Prime(수학) (5) | 2024.11.19 |
---|---|
[C/C++] 프로젝트 오일러 #96 Su Doku(백트래킹) (0) | 2024.11.18 |
[C/C++] 프로젝트 오일러 #94 Almost Equilateral Triangles(수학) (4) | 2024.11.16 |
[C/C++] 프로젝트 오일러 #93 Arithmetic Expressions(백트래킹) (0) | 2024.11.14 |
[C/C++] 프로젝트 오일러 #91 Right Triangles with Integer Coordinates(전체 탐색) (0) | 2024.11.11 |
댓글