程序计算从点到线,再到第二点的最短路径。另外我需要说明从线的起点到点的交叉点有多长。到目前为止我的代码:
from math import sqrt
numbers_total = []
numbers_canal = []
first_triangle_longest_side_length = 300 #defining the variable
def first_triangle(first_triangle_longest_side_length):
calculation_one = sqrt(first_triangle_longest_side_length)
first_triangle_bottom_side_length = calculation_one - sqrt(300)
def second_triangle(canal_length_left):
canal_length_left = canal_length_left ** 2
calculation_two = canal_length_left + 500 ** 2
second_triangle_longest_side_length = sqrt(calculation_two)
while True:
first_triangle_longest_side_length = first_triangle_longest_side_length + 0.01
first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - first_triangle_bottom_side_length
second_triangle(canal_length_left)
if first_triangle_longest_side_length == 1044.03:
break
total_distance = first_triangle_longest_side_length + second_triangle_longest_side_length
numbers_total.append(total_distance)
numbers_canal.append(first_trangle_bottom_side_length)
minimal_total_distance = min(numbers_total)
number_on_the_list = numbers_total.index(minimal_total_distance)
print "The distance of the shortest route is: " + "%.1f" % minimal_total_distance
print "The distance of the canal from point A is: " + "%.1f" % numbers_canal[number_on_the_list]
然而,它给出了错误:
line 19, in <module>
canal_length_left = 1000 - first_triangle_bottom_side_length
NameError: name 'first_triangle_bottom_side_length' is not defined
有什么想法吗?
答案 0 :(得分:1)
对于那些感兴趣的人,问题是:Cambridge February Computing Competition
你正在采取一种非常强力的方法; 它有效,但可以更有效率 编辑:我把它拿回来;你的数学也不正确。我将在新帖子中更正。
这是一种更具分析性的方法:
TOWER_DIST = 300
CANAL_LEN = 1000
FIRE_DIST = 500
#
# express the problem as a function
#
def path_length(dist_a):
"""
Given distance in meters from A along the canal,
return total path length
"""
leg_a = (TOWER_DIST**2 + dist_a**2) ** 0.5
leg_b = ((CANAL_LEN - dist_a)**2 + FIRE_DIST**2) ** 0.5
return leg_a + leg_b
#
# find x such that path_length(x) is minimized
#
# (the easy way:
# import scipy.optimize
# result = scipy.optimize.minimize(path_length, CANAL_LEN * 0.5)
# best_dist = result.x[0] # => 375.00092001
# best_length = path_length(best_dist) # => 1280.6248
#
# but because this is a competition we will avoid add-on modules)
def deriv(f, delta=0.01):
"""
Return a function which is a numerical first derivative of f
"""
def df(x):
a, b = f(x - delta), f(x + delta)
return (b - a) / (2. * delta)
return df
def newton_root(f, initial_x, tolerance=0.01, max_tries=1000):
"""
Use Newton-Raphson method to find x such that abs(f(x)) <= tolerance
"""
df = deriv(f)
x = initial_x
for try_ in range(max_tries):
err = f(x)
if abs(err) <= tolerance:
# found a solution
return x
else:
# find next value for x
x -= err / df(x)
else:
raise ValueError(
"newton_root fails to converge for initial_guess = {}"
.format(initial_x)
)
# By inspection, path_length is a smooth upward curve (ie a U-shape)
# on 0 .. CANAL_LEN; minimum occurs where first derivative is 0
best_dist = newton_root(deriv(path_length), CANAL_LEN * 0.5, 0.00001) # => 374.9999
best_length = path_length(best_dist) # => 1280.62484
# return results to nearest 0.1 meter
print("{:0.1f} {:0.1f}".format(best_dist, best_length))
如果你玩这个并考虑一下,你应该意识到最短的路径总是这样,leg_a和运河之间以及运河和leg_b之间的角度是相同的;如果你认为运河是一面镜子,那么最短的路径是直接射向镜子里的火焰反射。
这允许我们将问题简化为一对简单的相似三角形,
# TOWER_DIST / best_dist == (TOWER_DIST + FIRE_DIST) / CANAL_LEN
best_dist = TOWER_DIST * CANAL_LEN / (TOWER_DIST + FIRE_DIST)
best_length = ((TOWER_DIST + FIRE_DIST)**2 + CANAL_LEN**2) ** 0.5
答案 1 :(得分:0)
这正是错误所说的。 Python逐行工作。在你的功能里面看它。你永远不会定义`first_triangle_bottom_side_length&#34;。在函数中定义它,错误就会消失。
答案 2 :(得分:0)
变量first_triangle_bottom_side_length可能在函数内被声明为local,为了解决这个问题,你返回变量并将其设置在函数之外。
(在第一个三角形内):
Return first_triangle_bottom_side_length
...
firsttrianglebottomsidelength = first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - firsttrianglebottomsidelength
答案 3 :(得分:0)
就像Tim Castelijns写的那样 - 这是一个范围问题。
first_triangle_bottom_side_length
在函数first_triangle
中定义,但您永远不会将其返回到主程序。
你应该退货:
def first_triangle(first_triangle_longest_side_length):
calculation_one = sqrt(first_triangle_longest_side_length)
return calculation_one - sqrt(300)
[...]
[...]
while True:
first_triangle_longest_side_length = first_triangle_longest_side_length + 0.01
first_triangle_bottom_side_length = first_triangle(first_triangle_longest_side_length)
canal_length_left = 1000 - first_triangle_bottom_side_length
了解范围,但更重要的是 - 了解如何使用功能。
祝你好运!答案 4 :(得分:0)
再看看你的答案后,我意识到你的数学也错了。我会指出你的语法和数学错误,并在此过程中添加一些免费的建议; - )
单步执行原始代码:
from math import sqrt
numbers_total = []
numbers_canal = []
first_triangle_longest_side_length = 300 #defining the variable
真的? where_on_earth_did_you_learn_variable_naming
?试试tower_dist = 300
。
def first_triangle(tower_hypot):
calculation_one = sqrt(tower_hypot)
first_triangle_bottom_side_length = calculation_one - sqrt(300)
第一个错误:为first_triangle_bottom_side_length
分配值并没有完成任何事情。变量属于局部范围(即它只存在于此函数中),因此只要函数结束,它就会消失。
你可能完全是邪恶的并使其成为一个全局变量,或者你只能return calculation_one - sqrt(300)
。
第二个错误:数学错误。我马上回过头来看看。
def second_triangle(canal_length_left):
canal_length_left = canal_length_left ** 2
calculation_two = canal_length_left + 500 ** 2
second_triangle_longest_side_length = sqrt(calculation_two)
再次出现相同的错误;改为return sqrt(calculation_two)
。
重要说明:命名约定! first_triangle
返回基本长度,second_triangle
返回斜边。这有点令人困惑。此外,两个函数都有常量硬连接,这意味着函数不能重用于其他任何东西。
如果您改为:
def side(adjacent, hypotenuse):
"""
Given the side and hypotenuse of a right triangle,
return the other side
"""
return (hypotenuse**2 - adjacent**2) ** 0.5
def hypotenuse(adjacent, opposite):
"""
Given two sides of a right triangle,
return the hypotenuse
"""
return (adjacent**2 + opposite**2) ** 0.5
def tower_base(tower_hypot, tower_height=300):
return side(tower_height, tower_hypot)
def fire_hypot(fire_base, fire_height=500):
return hypotenuse(fire_base, fire_height)
这通常足以重复使用,具有有意义但不可笑的长变量名称,并且可以轻松地进行正确性测试:
assert side(3, 5) == 4
assert side(5, 13) == 12
assert hypotenuse(3, 4) == 5
assert hypotenuse(5, 12) == 13
# a (300, 400, 500) triangle
assert tower_base(500) == 400
# a (500, 1200, 1300) triangle
assert fire_hypot(1200) == 1300
测试您之前的代码:
# should make a (300, 400, 500) triangle
assert first_triangle(500) == 400 # 5.04017 ?! FAIL
并且存在数学错误:first_triangle
返回tower_hypot**0.5 - 300**0.5
这是无意义的数量;它应该是(tower_hypot**2 - 300**2)**0.5
。
单位分析为我们提供了一种快速检查的方法;如果tower_hypot
以米为单位,则第一个等式返回米的根(这没有意义),而第二个等式返回米(这是我们所期望的)。这并不一定使它正确 - 但它使显然不正确,这是一个好的开始!
while True:
tower_hypot += 0.01
first_triangle(first_triangle_longest_side_length)
您正确调用了该函数,但未将结果保留在任何位置;试试a_dist = tower_base(tower_hypot)
。
canal_length_left = 1000 - a_dist
second_triangle(canal_length_left)
你还没有保留功能结果;试试fire_dist = fire_hypot(1000 - a_dist)
。
if first_triangle_longest_side_length == 1044.03:
break
魔术数字!真有趣!
目前尚不清楚这个值的含义((300**2 + 1000**2)**0.5
是tower_hypot
的最大可能值,但乍看之下还不清楚
浮点数学意味着==
几乎永远不会起作用;您想要>
或>=
你已经对tower_height,canal_length,fire_height的值进行了硬编码 - 这是不好的做法 - 但你现在还在派生自这些值的值中硬编码,使你的程序很难维护(下周会发生什么事情,当游侠看到距离运河200米远的地方有火灾?他是否必须从头开始重写程序以找出去哪里?)
然后
total_distance = first_triangle_longest_side_length + second_triangle_longest_side_length
numbers_total.append(total_distance)
numbers_canal.append(first_trangle_bottom_side_length)
minimal_total_distance = min(numbers_total)
number_on_the_list = numbers_total.index(minimal_total_distance)
print "The distance of the shortest route is: " + "%.1f" % minimal_total_distance
print "The distance of the canal from point A is: " + "%.1f" % numbers_canal[number_on_the_list]
通过一些修改,代码看起来像
# use named variables!
# - makes it easy to modify the program later
# - makes it easier to figure out what's going on
TOWER_HEIGHT = 300
CANAL_LENGTH = 1000
FIRE_HEIGHT = 500
# generic functions which can be reused!
def side(adjacent, hypotenuse):
"""
Given the side and hypotenuse of a right triangle,
return the other side
"""
return (hypotenuse**2 - adjacent**2) ** 0.5
def hypotenuse(adjacent, opposite):
"""
Given two sides of a right triangle,
return the hypotenuse
"""
return (adjacent**2 + opposite**2) ** 0.5
# work from the most obvious control value
def total_dist(a_dist):
tower_hypot = hypotenuse(a_dist, TOWER_HEIGHT)
b_dist = CANAL_LENGTH - a_dist
fire_hypot = hypotenuse(b_dist, FIRE_HEIGHT)
return tower_hypot + fire_hypot
def main():
step_size = 0.1
# equivalent of
# a_dists = numpy.arange(0.0, CANAL_LENGTH, step_size)
num_steps = int(CANAL_LENGTH / step_size)
a_dists = (k*step_size for k in range(num_steps + 1))
# find minimum distance
best_a_dist = min(a_dists, key=total_dist)
best_total_dist = total_dist(best_a_dist)
print("The distance of the shortest route is: {:0.1f}".format(best_total_dist))
print("The distance of the canal from point A is: {:0.1f}".format(best_a_dist))
main()