拯救湖心的风筝(一道趣味平面几何问题)
原题出自:http://topic.csdn.net/u/20110617/10/f9b370a2-dad8-42ad-9202-86e23cd84dea.html
问题描述:小华和小明的风筝不小心断了线,掉到了一个圆形湖圆心处的孤岛上。两人都不会游泳,幸好湖边有一条小船,不过可惜没有桨,也没有其他的动力工具。船上放着 一根绳子,两人决定利用这根绳子,将一头栓在船上,小华上船,小明在岸边拉绳子使船移动的方法,靠近小岛,由小华取回风筝,再由小明把船拉回岸边。如果他们最后成功了,请问这根绳子最少要有多长(栓在船上的部分不计)?他们是怎么做到的?
分析:设湖的圆心为O,湖的半径为r。显然绳子长度>=r, 问题是能不能达到r。下面用逼近的思想,说明绳子长度理论上应该可以做到最短为湖的半径r,具体做法是(参考下图):首先船位于A点不动(小华在船上), 另一个人(即小明)手中拿着绳子沿着湖边走,直到B点,这时刚好把绳子拉直(可以做到这一点,因为设|AB|表示线段AB的长度,则A到沿途中任何点的弦 都小于|AB|),设该绳子对应的线段为AB(A是船开始所在位置,B是人所在位置),然后小明停下来开始把船拉到AB的中心位置(即O到AB的垂点位置 P)让船静止。接下来,保持船静止,小明拿着绳子沿着湖边走到B',这时他停下来刚好把绳子拉直,设该绳子对应的线段为A'B'(或PB'),然后开始把 船拉到P'位置让它停下来(小明的动作必须温柔,否则由于惯性,船会移动),这里P'是O到线段A'B'的垂点,显然P'比上一次 的位置P更接近圆心O点(因为|OP'|<|OP|),按这种方式进行下去,每次得到船的停止位置都比上一次停止位置更接近O点。到此为止讨论的绳 子长度可以是一个任意长度s(甚至s可以小于r),显然如果要让船到达湖心O点,绳子的极限值是湖的半径r。
图中绿色实线是船经过的路径,这里只显示两步。上面第二步操作中因为OB'=r>OA',所以P'比上一次的P点更靠近A',即每一次拉船的 行程比上一次的行程要小。如果要精确计算的话,|OP|=sqrt(3)/2 * r, |OP'| = sqrt(39)/8 * r。一般情况下,设上一次操作中O到垂点P的高度为x,则下一次操作中O到垂点P的高度则为x * sqrt(r^2 - x^2 / 4) / r。
如何计算小明走过的距离?
第二步操作中小明走过的一段距离为B到B'的弧长,如果知道BOB'的夹角就可以算出该弧长。设BOB'的夹角为theta,设|OP|=x,夹角 OBA为alpha,可以通过sin(alpha) = x/r算出alpha,这样可以算出夹角POB = PI/2 - alpha,由于三角形OPB'是腰长等于r的等腰三角形,如果设夹角POB'等于beta,可以得到:cos(beta) = (x/2)/r=x/(2*r),这样算出beta之后,就可以算出theta = beta - 夹角POB = beta - (PI/2 - alpha) = beta + alpha - PI/2。所以B到B'的弧长 = r*theta = r*(beta + alpha - PI/2)。
程序:(计算小明和船走过的路程)
public class KiteSalvage { //return the boat's distance and xiaoming's distance in total public static double[] solve(double r){ double epsilon = 0.5; //unit: meter ( = r's unit) //set initial values double x = Math.sqrt(3)/2.0 * r; double xiaoming = Math.PI/3.0 * r; double boat = r/2.0; while(true){ double x_prime = x * Math.sqrt(r*r - x*x/4.0) / r; boat += Math.sqrt(x*x - x_prime*x_prime); double alpha = Math.asin(x/r); double beta = Math.acos(x/2.0/r); double theta = beta + alpha - Math.PI/2.0; xiaoming += r*theta; if(Math.abs(x_prime - x)< epsilon){ break; } x = x_prime; } return new double[]{xiaoming,boat}; } public static void main(String[] args) { double r = 100; //100 meter for the lake double[] result = KiteSalvage.solve(r); System.out.format("For lake radius %.2f, Xiaoming'd total distance = %.2f, Boat's total distance = %.2f%n",r,result[0],result[1]); } }
测试:
For lake radius 100.00, Xiaoming'd total distance = 885.87, Boat's total distance = 416.45