Files
Data-Structure/Algorithm/IterativeDeepening/Egypt-Fraction-AI.cpp
e2hang 265b8af720 IDS
2025-09-11 15:53:33 +08:00

144 lines
4.9 KiB
C++
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <bits/stdc++.h>
using namespace std;
typedef long long ll; // 使用 long long 以处理较大的分母,避免溢出
// 计算最大公约数GCD用于约分分数
ll gcd(ll a, ll b) {
return b == 0 ? a : gcd(b, a % b); // 辗转相除法
}
// 将分数 n/d 约分为最简形式
pair<ll, ll> reduce(ll n, ll d) {
ll g = gcd(abs(n), d); // 计算 n 和 d 的 GCDabs(n) 防止负数问题
return {n / g, d / g}; // 返回约分后的分子和分母
}
// 表示埃及分数搜索状态的结构体
struct State {
ll num, den; // 当前剩余分数 num/den
vector<ll> dens; // 已选择的单位分数的分母列表
// 计算当前状态的最大分母
ll max_den() const {
if (dens.empty()) return 0; // 如果没有分母,返回 0
return *max_element(dens.begin(), dens.end()); // 返回分母列表中的最大值
}
};
// 哈希函数,用于在 unordered_set 中存储状态(本实现未使用,但保留以备扩展)
struct StateHash {
size_t operator()(const State& s) const {
string str;
for (ll x : s.dens) str += to_string(x) + ",";
str += to_string(s.num) + "/" + to_string(s.den);
return hash<string>()(str);
}
};
// 获取当前状态的所有后继状态
vector<State> get_successors(const State& state, int limit, int depth) {
vector<State> res; // 存储所有可能的后继状态
ll r = state.num, s = state.den; // 当前剩余分数 r/s
if (r == 0) return res; // 如果分子为 0无需继续扩展
// 确定最小分母:必须大于上一个分母(保证分母递增,避免重复),且满足 r/s >= 1/d
ll last_d = state.dens.empty() ? 0 : state.dens.back();
ll min_d = max(last_d + 1, (s + r - 1) / r); // min_d = ceil(s/r)
// 设置分母的上限,防止过大的分母导致计算溢出
const ll MAX_D = 10000000LL; // 合理的安全上限
// 枚举可能的单位分母 d
for (ll d = min_d; d <= MAX_D; ++d) {
// 优化:检查剩余步数是否足够表示剩余分数
int rem_steps = limit - depth - 1; // 剩余可用步数
double max_sum_after = 0.0; // 剩余步数能表示的最大分数和
for (int i = 1; i <= rem_steps; ++i) {
max_sum_after += 1.0 / (d + i); // 假设后续分母递增
}
double this_contrib = 1.0 / d; // 当前单位分数的贡献
double target = (double)r / s; // 目标剩余分数
// 如果当前贡献加上后续最大可能贡献仍小于目标,剪枝
if (this_contrib + max_sum_after < target - 1e-10) break;
// 计算新分数r/s - 1/d = (r*d - s) / (s*d)
ll new_num = r * d - s;
if (new_num < 0) continue; // 新分子为负,非法状态
ll new_den = s * d;
auto p = reduce(new_num, new_den); // 约分新分数
State ns = {p.first, p.second, state.dens}; // 创建新状态
ns.dens.push_back(d); // 添加当前单位分母
res.push_back(ns); // 将新状态加入后继列表
}
return res;
}
// 深度优先搜索DFS在当前深度限制下搜索解
void dfs(const State& state, int depth, int limit, vector<State>& solutions) {
if (depth > limit) return; // 超过深度限制,回溯
if (state.num == 0 && !state.dens.empty()) { // 找到一个解:分子为 0 且有至少一个分母
solutions.push_back(state); // 记录解
return; // 不再扩展此路径
}
// 遍历所有后继状态
for (const auto& next : get_successors(state, limit, depth)) {
dfs(next, depth + 1, limit, solutions); // 递归搜索
}
}
// 迭代加深搜索IDS寻找最短长度且最大分母最小的埃及分数
State find_best_egyptian(ll a, ll b) {
auto p = reduce(a, b); // 约分输入分数
State start = {p.first, p.second, {}}; // 初始状态
State best; // 记录最佳解
ll best_max = LLONG_MAX; // 最佳解的最大分母
int limit = 0; // 当前深度限制
while (true) {
++limit; // 增加深度限制
vector<State> solutions; // 存储当前深度下的所有解
dfs(start, 0, limit, solutions); // 执行 DFS
if (!solutions.empty()) { // 如果找到解
for (const auto& s : solutions) { // 遍历所有解
ll cur_max = s.max_den(); // 当前解的最大分母
if (cur_max < best_max) { // 如果最大分母更小,更新最佳解
best = s;
best_max = cur_max;
}
}
return best; // 返回最佳解
}
if (limit > 100) { // 安全限制,防止无限循环
cout << "No solution found within limit." << endl;
exit(1);
}
}
}
int main() {
ll a, b;
// 输入分子和分母,要求 0 < a < b 且互质
cout << "Enter numerator and denominator (a b, with 0 < a < b, gcd=1): ";
cin >> a >> b;
if (a >= b || a <= 0 || gcd(a, b) != 1) { // 简单输入验证
cout << "Invalid input: require 0 < a < b and gcd(a,b)=1" << endl;
return 1;
}
State result = find_best_egyptian(a, b); // 寻找最佳埃及分数
// 输出结果
cout << a << "/" << b << " = ";
for (size_t i = 0; i < result.dens.size(); ++i) {
if (i > 0) cout << " + ";
cout << "1/" << result.dens[i]; // 打印单位分数
}
cout << endl;
cout << "Length: " << result.dens.size() << endl; // 打印分解长度
cout << "Max denominator: " << result.max_den() << endl; // 打印最大分母
return 0;
}