이 문제는 수학적 규칙을 따르는 점열(sequence)을 생성하는 다항식(polynomial)과 관련이 있습니다. 문제를 해결하기 위해 주어진 점열을 분석하고, 다항식의 특성을 활용하여 최적의 다항식을 찾는 것이 핵심입니다.
문제 이해
1. 점열의 생성
문제는 특정한 다항식
여기서
예를 들어,
2. 부분 점열
문제는
예를 들어, 주어진 점열의 첫 3개의 값이
3. 근사 다항식 (Fitting Polynomial)
k-차 다항식을 사용하여 점열을 근사하는 다항식을
• k = 1 일 때는 선형 다항식,
• k = 2 일 때는 2차 다항식,
• k = 3 일 때는 3차 다항식,
… 등의 형태를 갖습니다.
점열의 일부를 사용해 최적화된
4. BEST (BOP) 값
1. 1차 다항식의 경우라면,
• 점열:
• k = 1 로 가정하고
2. 2차 다항식
3. 오차 계산
이 문제의 핵심은 점열을 분석하여 점진적으로

문제 출처
https://projecteuler.net/problem=101
제가 작성한 소스는 다음과 같습니다.
//------------------------------------------------
// Project Euler #101 - Optimum Polynomial
// - by Aubrey Choi
// - created at 2015-03-27
//------------------------------------------------
#include <stdio.h>
#include <stdint.h>
struct FN
{
int n; // highest degree
int64_t poly[20]; // lowest : 0 ~ n
};
void SimpleGuess(FN *t, FN *s, int n, int fs[], int fact);
int64_t FindFIT(FN *g, FN *s, int n);
int64_t GetValue(FN *f, int n);
void PrintFn(FN *f);
int main()
{
FN s = { 10, { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 } };
//FN s = { 3, { 0, 0, 0, 1 } };
FN g = { 0, { 0 } };
int64_t ans = 0;
int n = 1;
int fs[20] = { 1 };
int fact = 1;
for( int n = 1 ; n <= s.n+1 ; n++ )
{
SimpleGuess(&g, &s, n, fs, fact);
PrintFn(&g);
if( g.n == s.n ) break;
fact *= n;
for( int i = n ; i > 0 ; i-- )
fs[i] = fs[i-1];
fs[0] = 0;
for( int i = 0 ; i < n ; i++ )
fs[i] -= n*fs[i+1];
if( g.poly[n-1] ) ans += FindFIT(&g, &s, n);
}
printf("ans = %jd\n", ans);
}
void SimpleGuess(FN *t, FN *s, int n, int fs[], int fact)
{
int64_t v = GetValue(s, n);
v -= GetValue(t, n);
if( v == 0 ) return;
t->n = n-1;
for( int i = 0 ; i < n ; i++ ) t->poly[i] += v*fs[i]/fact;
}
int64_t FindFIT(FN *g, FN *s, int n)
{
for( int i = n+1 ; ; i++ )
{
int64_t v = GetValue(g, i);
int64_t u = GetValue(s, i);
if( v != u ) return v;
}
}
int64_t GetValue(FN *f, int n)
{
int64_t v = 0;
for( int i = f->n ; i >= 0 ; i-- )
{
v *= n;
v += f->poly[i];
}
return v;
}
void PrintFn(FN *f)
{
bool needOp = false;
printf("u = ");
if( f->poly[0] != 0 ) { printf("%jd", f->poly[0]); needOp = true; }
for( int i = 1 ; i <= f->n ; i++ )
{
if( f->poly[i] == 0 ) continue;
if( f->poly[i] > 0 ) { if( needOp ) printf("+"); }
else printf("-");
needOp = true;
if( f->poly[i] > 1 ) printf("%jd", f->poly[i]);
else if( f->poly[i] < -1 ) printf("%jd", -f->poly[i]);
printf("n^%d", i);
}
putchar('\n');
}
소스 코드의 핵심 알고리즘과 설명
주어진 다항식을 기반으로 생성된 점열을 통해 “최적화된 다항식 근사”를 계산하고, “잘못된 예측 값(Best OP, BOP)“을 합산하는 과정이 포함되어 있습니다.
점열의 일부 값만을 사용하여 n-차 다항식을 추정합니다. 이 추정값은 점열의 나머지 값을 근사합니다.
근사된 다항식
점진적으로 k-차 다항식을 계산하며, 점열의 일부 항만 사용하여 근사합니다. 이전 단계에서의 결과를 다음 단계 계산에 활용합니다.
(1) 주요 구조체와 함수 개요
1. 구조체 FN
• FN은 다항식을 표현합니다.
• n: 다항식의 차수 (highest degree).
• poly[20]: 다항식의 계수 배열 (차수별 계수를 저장).
2. SimpleGuess(FN *t, FN *s, int n, int fs[], int fact)
• n-차 다항식 근사를 업데이트합니다.
• 현재까지 계산된 다항식 t 에 새로운 점 n 에서의 값을 반영하여 계수를 조정합니다.
3. FindFIT(FN *g, FN *s, int n)
• 근사된 다항식 g 와 실제 점열 s 를 비교하여 n+1 부터 최초로 불일치하는 값을 찾아 반환합니다.
4. GetValue(FN *f, int n)
• 다항식 f 를 사용해 특정 n 에서의 값을 계산합니다.
5. PrintFn(FN *f)
• 다항식을 사람이 읽을 수 있는 형태로 출력합니다.
(2) 메인 함수
int main()
{
FN s = { 10, { 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1 } };
FN g = { 0, { 0 } };
int64_t ans = 0;
int n = 1;
int fs[20] = { 1 };
int fact = 1;
for( int n = 1 ; n <= s.n+1 ; n++ )
{
SimpleGuess(&g, &s, n, fs, fact);
PrintFn(&g);
if( g.n == s.n ) break;
fact *= n;
for( int i = n ; i > 0 ; i-- )
fs[i] = fs[i-1];
fs[0] = 0;
for( int i = 0 ; i < n ; i++ )
fs[i] -= n*fs[i+1];
if( g.poly[n-1] ) ans += FindFIT(&g, &s, n);
}
printf("ans = %jd\n", ans);
}
1. 다항식 정의
• s는 실제 점열을 생성하는
2. 초기화
• g: 근사 다항식
• fs: 다항식 근사를 위한 계수 업데이트 배열.
• fact: 팩토리얼 계산 (다항식 계수 조정을 위해 사용).
3. 루프 구조
• n 을 증가시키며
• SimpleGuess: 점열의 n-번째 항을 반영하여
• FindFIT: 근사 다항식과 실제 점열 간의 불일치 값을 계산.
• fs와 fact를 갱신하여 다음 단계 계산에 활용.
1. SimpleGuess 함수
void SimpleGuess(FN *t, FN *s, int n, int fs[], int fact)
{
int64_t v = GetValue(s, n);
v -= GetValue(t, n);
if( v == 0 ) return;
t->n = n-1;
for( int i = 0 ; i < n ; i++ ) t->poly[i] += v*fs[i]/fact;
}
• t: 현재까지의 근사 다항식.
• s: 실제 점열.
• fs: 다항식 계수 업데이트용 배열.
• fact: 계수 정규화를 위한 팩토리얼 값.
작동 원리:
• n-번째 점열 값에서 기존 다항식의 예측 값을 뺀 오차를 계산 (v).
• 이 오차를 다항식에 반영하여 계수 배열을 업데이트.
2. FindFIT 함수
int64_t FindFIT(FN *g, FN *s, int n)
{
for( int i = n+1 ; ; i++ )
{
int64_t v = GetValue(g, i);
int64_t u = GetValue(s, i);
if( v != u ) return v;
}
}
• g: 근사 다항식.
• s: 실제 점열.
작동 원리:
• n+1 부터 시작하여 g(i) 와 s(i) 를 비교.
• 최초로 값이 다른 경우, g(i) 값을 반환.
3. GetValue 함수
int64_t GetValue(FN *f, int n)
{
int64_t v = 0;
for( int i = f->n ; i >= 0 ; i-- )
{
v *= n;
v += f->poly[i];
}
return v;
}
작동 원리:
• 다항식 f의 계수를 사용해 n에서의 값을 계산.
• 계수 배열과 n-제곱을 활용해 결과를 도출.
'Programming > Project Euler' 카테고리의 다른 글
[C/C++] 프로젝트 오일러 #103 Special Subset Sums: Optimum(백트래킹) (0) | 2024.11.24 |
---|---|
[C/C++] 프로젝트 오일러 #102 Triangle Containment(수학) (0) | 2024.11.23 |
[C/C++] 프로젝트 오일러 #100 Arranged Probability(수학) (0) | 2024.11.21 |
[C/C++] 프로젝트 오일러 #99 Largest Exponential(수학) (0) | 2024.11.21 |
[C/C++] 프로젝트 오일러 #98 Anagramic Squares(완전 탐색) (0) | 2024.11.20 |
댓글