(详细揭秘)怎么把优选法改造为整数三分?
问题:给定函数
我们知道黄金三分(也称优选法)只需要求
考虑
- 找到最小的
k 使得F_k\ge r-l ,设m_0=l+F_{k-2} ,m_1=l+F_{k-1} 。 - 比较
G(m_0) 与G(m_1) :- 若
G(m_0)>G(m_1) ,则说明(m_1,r] 内的点值不可能成为极值,我们将r 移动到m_1 。此时r-l=F_{k-1} ,因此k 需要减一,随后我们发现m_1'=m_0 故点值可以复用,只需要重新计算m_0' 处的点值。 - 若
G(m_0)\le G(m_1) ,则说明[l,m_0) 内的点值不可能成为极值,我们将l 移动到m_0 。此时只有F_{k-3}<r-l\le F_{k-1} ,我们还要讨论它和F_{k-2} 的大小关系。- 若
r-l\le F_{k-2} ,则k 需要减二,m_1'=m_1 故点值可以复用,只需要重新计算G(m_0') ; - 若
r-l>F_{k-2} ,则k 只需要减一,m_0'=m_1 故点值可以复用,只需要重新计算G(m_1') 。
- 若
- 若
再处理一些 corner case 就好了,显然每次迭代都会使得
实现的好像有点丑,有没有大手子写一份漂亮一点的。
struct fib_search {
using u64 = unsigned long long;
static constexpr int kL = 93;
u64 f[kL];
constexpr fib_search() {
f[0] = f[1] = 1;
for (int i = 2; i < kL; ++i) {
f[i] = f[i - 1] + f[i - 2];
}
}
template <typename U, typename T>
auto find_max(U l, U r, T g) const {
if (r - l <= 2) {
auto ans = make_pair(g(l), l);
for (U i = l + 1; i <= r; ++i) {
ans = max(ans, make_pair(g(i), i));
}
return ans;
}
int k = lower_bound(f, f + kL, r - l) - f;
U m0 = l + f[k - 2], m1 = l + f[k - 1];
for (auto v0 = g(m0), v1 = g(m1);;) {
if (v0 > v1) {
r = m1;
if (r - l <= 2) {
return max(make_pair(g(l), l), make_pair(v0, m0));
}
m1 = m0, v1 = v0, --k, v0 = g(m0 = l + f[k - 2]);
} else {
l = m0;
if (r - l <= 2) {
return max(make_pair(g(r), r), make_pair(v1, m1));
}
if (r - l <= f[k - 2]) {
k -= 2, v0 = g(m0 = l + f[k - 2]);
} else {
m0 = m1, v0 = v1, --k, v1 = g(m1 = l + f[k - 1]);
}
}
}
}
};
constexpr fib_search fib;