三分法

原文地址 点击打开链接



   二分法作为分治中最常见的方法,适用于单调函数,逼近求解某点的值。但当函数是凸性函数时,二分法就无法适用,这时三分法就可以“大显身手”~~


       如图,类似二分的定义Left和Right,mid = (Left + Right) / 2,midmid = (mid + Right) / 2; 如果mid靠近极值点,则Right = midmid;否则(即midmid靠近极值点),则Left = mid;

程序模版如下:

double Calc(Type a)
{
    /* 根据题目的意思计算 */
}

void Solve(void)
{
    double Left, Right;
    double mid, midmid;
    double mid_value, midmid_value;
    Left = MIN; Right = MAX;
    while (Left + EPS < Right)
    {
        mid = (Left + Right) / 2;
        midmid = (mid + Right) / 2;
        mid_area = Calc(mid);
        midmid_area = Calc(midmid);
        // 假设求解最大极值.
        if (mid_area >= midmid_area) Right = midmid;
        else Left = mid;
    }
}

现根据几道的OJ题目来分析三分法的具体实现。

buaa 1033 Easy Problem 
http://acm.buaa.edu.cn/oj/problem_show.php?c=0&p=1033

题意为在一条线段上找到一点,与给定的P点距离最小。很明显的凸性函数,用三分法来解决。
Calc函数即为求某点到P点的距离。

ZOJ 3203 Light Bulb
http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3203


如图,人左右走动,求影子L的最长长度。
根据图,很容易发现当灯,人的头部和墙角成一条直线时(假设此时人站在A点),此时的长度是影子全在地上的最长长度。当人再向右走时,影子开始投影到墙上,当人贴着墙,影子长度即为人的高度。所以当人从A点走到墙,函数是先递增再递减,为凸性函数,所以我们可以用三分法来求解。

下面只给出Calc函数,其他直接套模版即可。
double Calc(double x)
{
    return (h * D - H * x) / (D - x) + x;
}

heru 5081 Turn the corner 08年哈尔滨regional网络赛
http://acm.hrbeu.edu.cn/index.php?act=problem&id=1280


汽车拐弯问题,给定X, Y, l, d判断是否能够拐弯。首先当X或者Y小于d,那么一定不能。
其次我们发现随着角度θ的增大,最大高度h先增长后减小,即为凸性函数,可以用三分法来求解。

这里的Calc函数需要比较繁琐的推倒公式:
s = l * cos(θ) + w * sin(θ) - x;
h = s * tan(θ) + w * cos(θ);
其中s为汽车最右边的点离拐角的水平距离, h为里拐点最高的距离, θ范围从0到90。

POJ 3301 Texas Trip
http://acm.pku.edu.cn/JudgeOnline/problem?id=3301

题意为给定n(n <= 30)个点,求出饱含这些点的面积最小的正方形。

有两种解法,一种为逼近法,就是每次m分角度,求出最符合的角度,再继续m分,如此进行times次,即可求出较为精确的解。(m 大概取10, times取30即可)

第二种解法即为三分法,首先旋转的角度只要在0到180度即可,超过180度跟前面的相同的。坐标轴旋转后,坐标变换为:
X’ = x * cosa - y * sina;
y’ = y * cosa + x * sina;

至于这题的函数是否是凸性的,为什么是凸性的,我也无法给出准确的证明,希望哪位路过的大牛指点一下~~

例题更新(2010.5.5)
hdu 3400 Line belt
http://acm.hdu.edu.cn/showproblem.php?pid=3400
典型的三分法,先三分第一条线段,找到一个点,然后根据这个点再三分第二条线段即可,想出三分的思路基本就可以过了。

对于求解一些实际问题,当公式难以推导出来时,二分、三分法可以较为精确地求解出一些临界值,且效率也是令人满意的。

/* czyuan原创,转载请注明出处。*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
三分法是一种数值计算方法,用于求解一元函数的极值或者方程的根。对于假币问题,可以使用三分法来判断一个硬币是否为真币。 以下是一个基于三分法的假币检测的 C 语言程序示例: ```c #include <stdio.h> // 模拟硬币重量函数 double Weight(int n) { if (n <= 0) return 0; if (n == 1) return 1.0; if (n % 2 == 0) return Weight(n / 2) + Weight(n / 2); else return Weight(n / 2) + Weight(n / 2 + 1); } // 三分法求解假币问题 int FindFakeCoin(int left, int right) { int mid1, mid2; double w1, w2, w3; while (left < right) { mid1 = left + (right - left) / 3; mid2 = right - (right - left) / 3; w1 = Weight(left); w2 = Weight(mid1); w3 = Weight(mid2); if (w1 == w2 && w2 == w3) return -1; // 三个硬币重量相等,无法判断 else if (w1 < w2 && w2 < w3) left = mid1 + 1; // 假币在 mid2 的右边 else if (w1 > w2 && w2 > w3) right = mid2 - 1; // 假币在 mid1 的左边 else { left = mid1 + 1; right = mid2 - 1; } // 假币在 mid1 和 mid2 之间 } return left; } int main() { int n; printf("请输入硬币数量:"); scanf("%d", &n); int fake = FindFakeCoin(1, n); if (fake == -1) printf("无法判断硬币真假!\n"); else printf("第 %d 枚硬币为假币!\n", fake); return 0; } ``` 在程序中,我们定义了一个模拟硬币重量的函数 `Weight(int n)`,用于计算第 n 枚硬币的重量。对于假币问题,我们可以假设真币和假币的重量相差 1,然后使用递归的方式模拟硬币的重量。 在 `FindFakeCoin(int left, int right)` 函数中,我们使用三分法对硬币进行判断。首先,我们将硬币分成三个部分,分别为 `left` 到 `mid1`、`mid1` 到 `mid2`、`mid2` 到 `right`。然后,我们分别计算这三个部分的重量,并进行比较。如果三个部分的重量相等,说明无法判断,返回 -1;如果重量递增,说明假币在 `mid2` 的右边;如果重量递减,说明假币在 `mid1` 的左边;否则,假币在 `mid1` 和 `mid2` 之间。最后,我们将 `left` 和 `right` 更新到新的位置,并继续进行判断,直到 `left` 和 `right` 相等。 在 `main()` 函数中,我们输入硬币的数量,然后调用 `FindFakeCoin()` 函数进行判断。如果返回值为 -1,说明无法判断;否则,返回的值就是假币的位置。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值