持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第14天,点击查看活动详情
python 编写简单的算法程序
近似解和对等搜索
想象一下,有人要求你编写一个程序来打印任何非负数的平方根。你该怎么办?
你应该从说你需要一个更好的问题陈述开始。例如,如果要求程序查找 2 的平方根,该怎么办?2 的平方根不是有理数。这意味着没有办法将其值精确表示为有限的数字串(或浮点数),因此无法解决最初陈述的问题。
程序可以做的是找到平方根的近似值,即一个足够接近实际平方根的答案。我们将在本书的后面相当详细地回到这个问题。但就目前而言,让我们把“足够接近”看作是一个答案,它存在于实际答案的某个常数(称之为epsilon)中。
图 3-4 中的代码实现了一种算法,该算法打印出 x 平方根的近似值。
我们再次使用详尽的枚举。请注意,这种查找平方根的方法与使用铅笔查找平方根的方法没有任何共同之处,而铅笔可能在中学时就学过。通常情况下,最好的方法是
用计算机解决问题与手动解决问题的方式完全不同。
如果 x 为 25,则代码将打印.
我们应该对程序没有弄清楚25是一个完美的正方形并打印5感到失望吗?不。该程序做了它打算做的事情。虽然打印 5 是可以的,但这样做并不比打印任何接近 5 的值好。
您认为如果我们设置x = 0.25会发生什么?它会找到接近0.5的根吗?不。唉,它会报告
穷举枚举是一种搜索技术,仅当要搜索的值集包含答案时才有效。在本例中,我们枚举 o 和 x 值之间的值。当 x 介于 o 和 1 之间时,x 的平方根不在此区间内。解决此问题的一种方法是更改 while 循环的第二个操作数和第一行,以获得
当我们在此更改后运行代码时,它报告
现在,让我们考虑一下程序需要多长时间才能运行。迭代次数取决于答案与我们的起点 o 的接近程度,以及步骤的大小。粗略地说,程序将以最多x/步长时间执行 while 循环。
让我们在更大的东西上尝试代码,例如,x= 123456。它将运行相当长的一段时间,然后打印
你认为发生了什么?当然,有一个浮点数,它近似于123456的平方根在0.01以内。
为什么我们的程序没有找到它?问题是我们的步长太大,程序跳过了所有合适的答案。再一次,我们正在详尽地搜索一个不包含解决方案的空间。尝试使步骤等于 epsilon**3 并运行该程序。它最终会找到一个合适的答案,但你可能没有耐心等待它这样做。
大概需要多少次猜测?步长将为 0.000001,123456的平方根约为 351.36。这意味着该程序将不得不在351, 000,000个猜测附近进行猜测才能找到满意的答案。我们可以尝试通过从更接近答案开始来加快速度,但这假设我们知道答案的邻居。
现在是寻找一种不同的方法来解决这个问题的时候了。我们需要选择一个更好的算法,而不是微调当前的算法。但在这样做之前,让我们看一个问题,乍一看,它似乎与根源发现完全不同。考虑一下发现以给定字母序列开头的单词是否出现在英语的硬拷贝词典22中的问题。原则上,详尽无遗的列举是可行的。你可以从第一个单词开始,检查每个单词,直到你找到一个以字母序列开头的单词,或者你用完了要检查的单词。如果字典包含 n 个单词,则平均需要 n/2 次探测才能找到该单词。如果这个词不在字典中,则需要n个探测器。当然,那些有幸在实体(而不是在线)字典中查找单词的人永远不会以这种方式进行。
幸运的是,出版硬拷贝词典的人会费尽心思地将这些单词按词典顺序排列。这允许我们将书打开到我们认为单词可能所在的页面(例如,对于以字母m开头的单词,靠近中间)。如果字母序列在字典上先于页面上的第一个单词,我们知道要倒退。如果字母序列跟在页面上的最后一个单词之后,我们知道要继续前进。否则,我们检查字母序列是否与页面上的单词匹配。
现在让我们采用相同的想法,并将其应用于查找x的平方根的问题。假设我们知道 x 的平方根的良好近似值位于 o 和 max 之间。我们可以利用数字完全有序的事实。那对于任何一对不同的数字 n1 和 n2,n1 < n2 或 n1 > n2。因此,我们可以将x的平方根视为位于线上的某个位置
并开始搜索该间隔。由于我们不一定知道从哪里开始搜索,让我们从中间开始。
如果这不是正确的答案(大多数时候不会是正确的),问问它是太大还是太小。如果它太大,我们知道答案一定在左边。如果它太小,我们知道答案一定是右边的。然后,我们以较小的间隔重复该过程。Figure_3=5 包含此算法的实现和测试。
使用对分搜索近似平方根当运行 x=25 时,它打印
请注意,它找到的答案与我们之前的算法不同。这完全没问题,因为它仍然符合问题的规范。更重要的是,请注意,在循环的每次迭代中,要搜索的空间的大小都会减少一半。因此,该算法称为对等搜索。对等搜索是我们早期算法的巨大改进,该算法在每次迭代时仅减少了少量搜索空间。
让我们再次尝试 x = 123456。这一次,程序只需要30次猜测就可以找到一个可以接受的答案。x= 123456789怎么样?它只需要45个猜测。
使用此算法查找平方根没有什么特别之处。例如,通过将几个2更改为3,我们可以使用它来近似非负数的立方根。在第4章中,我们介绍了一种语言机制,它允许我们推广此代码以查找任何根。
平分搜索是一种广泛有用的技术,除了查找根之外的许多事情。例如,图 3-6 中的代码使用对等搜索来查找 x 的对数基数 2 的近似值(即,一个数字、ans,使得 2**ans 接近 x)。它的结构与用于查找平方根近似值的代码完全相同。它首先找到一个包含合适答案的区间,然后使用对分搜索来有效地探索该区间。
平分搜索是逐次逼近方法的一个例子。这些方法的工作原理是使用属性进行一系列猜测,即每个猜测都比以前的猜测更接近正确答案。我们将在本章后面介绍一个重要的逐次逼近算法,牛顿方法。手指练习:图 3-5 中的代码会执行哪些 ifx=-25?手指练习:要使图 3-5 中的代码能够找到负数和正数的立方根的近似值,必须进行哪些更改?提示:考虑更改1ow以确保答案位于正在搜索的区域内。
手指练习:帝国大厦高102层。一个男人想知道他可以从哪个最高楼层掉落一个鸡蛋而不会打破鸡蛋。他提议从顶楼扔一个鸡蛋。如果它坏了,他会掉下一层楼,再试一次。他会这样做,直到鸡蛋没有破裂。在最坏的情况下,这种方法需要102个卵子。实施一种在最坏的情况下使用七个鸡蛋的方法。