这个python脚本会按预期工作吗?我希望它将随机数插入一个等式直到它达到187.2

时间:2013-05-07 15:04:05

标签: python

print("Getting possible numbers")
import random

h1 = random.uniform(0,30)
h2 = random.uniform(0,30)
h3 = random.uniform(0,30)
t = random.uniform(0,30)

baset = .5 * t * h3
volumeti = baset * h2
baser = t * h2
volumer = h1 * baser
volumetotal = volumeti + volumer
c = 1

if volumetotal == 187.2:
  print("h1=", h1, "h2=", h2, "h3=", h3, "t=", t)

while volumetotal != 187.2:
 c += 1
 print("Wrong, trying again", c)

它会找到六角棱镜的体积,但需要很长时间,而我还没有输出。

6 个答案:

答案 0 :(得分:4)

循环:

while volumetotal != 187.2:
 c += 1
 print("Wrong, trying again", c)

是一个陷阱。一旦进入循环,你永远不会离开,因为你没有在循环体中改变volumetotal

另请注意,检查浮点数的相等性可能很棘手。两个数字可以非常接近(10 ** 16中的1个部分)并且仍然不同 - 并且这个大小的数字错误一直在发生。

答案 1 :(得分:1)

你可能永远不会得到输出。

  1. 第一次失败的循环永远不会让你再次生成数字
  2. 插入随机数以获得精确解决方案并不十分有效,并且可能永远不会返回结果。
  3. 比较浮点数的相等性可能不是你想要的。阅读浮点数相等比较。
  4. 更好的解决方案是解决方程,以便您可以随机选择3个数字并解决第四个问题。也许像:

    187.2 = .5 * t * h3 * h2 + h1 * t * h2
    187.2 = h2(.5 * t * h3 + h1 * t)
    h2 = 187.2 / (.5 * t * h3 + h1 * t)
    

    然后生成th3h1并计算h2

    import random
    
    print("Getting possible numbers")
    h1 = random.uniform(0,30)
    h3 = random.uniform(0,30)
    t = random.uniform(0,30)
    h2 = 187.2 / ((.5 * t * h3) + (h1 * t))
    print("h1=", h1, "h2=", h2, "h3=", h3, "t=", t)
    

答案 2 :(得分:1)

你也必须循环获取新的随机变量。此外,在处理浮点变量时,您必须接受一些小错误。看看这个:

print("Getting possible numbers")
import random

volumetotal = 0
c = 0

while abs(volumetotal - 187.2) >= 0.1:
  c += 1
  print("Attempt ", c)

  h1 = random.uniform(0,30)
  h2 = random.uniform(0,30)
  h3 = random.uniform(0,30)
  t = random.uniform(0,30)

  baset = .5 * t * h3
  volumeti = baset * h2
  baser = t * h2
  volumer = h1 * baser
  volumetotal = volumeti + volumer

print("h1=", h1, "h2=", h2, "h3=", h3, "t=", t)

答案 3 :(得分:0)

修复无限循环问题后,您可以使用以下检查:

error = 0.00001
expected = 187.2
if expected - error <= volumetotal <= expected + error:
    print("Yay, this is roughly correct.") 

另一个技巧,但是这个减少运行时间的方法是跟踪先前选择的随机变量并在尝试失败后将它们设置为上限/下限。因此:

# Pseudo-code
# if expected > desired:
#    set previously chosen random variables as the upper bound on the next 
#    iteration's random ranges.
# else:
#    set previously chosen random variables as the lower bound on the next
#    iteration's random ranges.
# Be sure to only shrink the range of the random selection until you converge on 
# a solution.

答案 4 :(得分:0)

你无法测试两个浮点之间的相等性,因为浮点艺术品的准确性较差。您必须设置一个限制解决方案精度的epsilon值:

print("Getting possible numbers")
import random
import math

epsilon = 0.001
c = 1
h1 = 0
h2 = 0
h3 =0
t = 0

def solve():

    h1 = random.uniform(0,30)
    h2 = random.uniform(0,30)
    h3 = random.uniform(0,30)
    t = random.uniform(0,30)

    baset = .5 * t * h3

    volumeti = baset * h2

    baser = t * h2

    volumer = h1 * baser

    volumetotal = volumeti + volumer

    return volumetotal, h1, h2, h3, t




while math.fabs(volumetotal - 187.2) > epsilon: 
 global h1 
 global h2 
 global h3 
 global t

 c += 1
 volumetotal, h1, h2, h3, t = solve()
 print("Wrong, trying again", c)

print("found a solution : ")
print("h1=", h1, "h2=", h2, "h3=", h3, "t=", t)

编辑:添加输出全局变量。

然而,这是解决方程式的一种非常差的方法。我建议您查找gradient descent确定性算法或genetic algorithms,如果您想以随机方式保留。

答案 5 :(得分:0)

你有几个问题:

  • 首先也是最关键的是,你的循环永远不会改变变量的值,所以它永远不会退出(因为它只是无限期地执行相同的测试。
  • 第二,几乎同样重要的是,如果希望程序终止,则不应循环浮点数的比较。这是因为浮点表示是inherently inaccurate,因此当您可能期望它们为False时,您的比较将评估为True(例如,.3 + .3 + .3 == .9的评论中的eumiro示例1}}评估为False)。

第一个问题的解决方案很简单:您只需要确保每次循环都重新计算变量。第二个就是这么简单:你需要测试值是否“足够接近”,而不是测试相等性。因此,选择一个公差值并将您的错误与之比较,而不是要求完美。这两个都显示在下面的代码中(为了清楚起见,我将一些部分拆分成了它们自己的函数)。我也这样做了c每5000次迭代只打印一次,实际上是makes the whole thing run many times faster

print("Getting possible numbers")
import random

def get_variables():
    '''Initialize some random values.'''
    h1 = random.uniform(0,30)
    h2 = random.uniform(0,30)
    h3 = random.uniform(0,30)
    t = random.uniform(0,30)
    return h1, h2, h3, t

def calculate_volume(h1, h2, h3, t):
    '''Calculate the volume based on the given values.'''
    baset = .5 * t * h3
    volumeti = baset * h2
    baser = t * h2
    volumer = h1 * baser
    volumetotal = volumeti + volumer
    return volumetotal


volumetotal = 0
c = 0
tolerance = 0.00001 # Set the tolerance here!
h1 = h2 = h3 = t = None

while abs(volumetotal - 187.2) >= tolerance: 
    c += 1
    if c % 5000 == 0:
        print("Attempt ", c)

    h1, h2, h3, t = get_variables()
    volumetotal = calculate_volume(h1, h2, h3, t)

print ('h1 = {}\nh2 = {}\nh3 = {}\nt = {}\nc = {}\nv = {}'.format(
        h1, h2, h3, t, c, volumetotal))

注意第26行,其中while循环将abs(volumetotal - 187.2)(结果与预期结果的差异)与0.00001(公差)进行比较。您可以将此容差设置为您喜欢的任何值,但容差越小,程序运行的时间就越长。

只是为了好玩,您真正想要的是为输出h1h2等选择精确度,等等)。然后,您可以预测性地增加变量,直到达到容差范围内,而不是使用随机数。这可能是我这样做的方式,而不是使用randoms,因为它至少可以保证你的循环终止,并且它可以更好地控制你的输入/输出。

print("Getting possible numbers")
import itertools

def get_variable_permutations(precision):
    '''Get a permutation generator. The precision is # of decimal places.'''
    stepsize = 0.1**precision
    all_values = (v*stepsize for v in xrange(int(30/stepsize)))
    return itertools.permutations(all_values, 4)

def calculate_volume(h1, h2, h3, t):
    '''Calculate the volume based on the given values.'''
    baset = .5 * t * h3
    volumeti = baset * h2
    baser = t * h2
    volumer = h1 * baser
    volumetotal = volumeti + volumer
    return volumetotal


volumetotal = 0
c = 0
tolerance = 0.00001
precision = 5 # decimal place precision for h1, h2, h3, and t

for h1, h2, h3, t in get_variable_permutations(precision):
    c += 1
    if c % 5000 == 0: # So much faster!
        print("Attempt ", c)
    volumetotal = calculate_volume(h1, h2, h3, t)
    if abs(volumetotal - 187.2) <= tolerance:
        break

print ('h1 = {}\nh2 = {}\nh3 = {}\nt = {}\nc = {}\nv = {}'.format(
        h1, h2, h3, t, c, volumetotal))

你最大的加速(到目前为止)实际上来自减少print陈述。我的机器在不到一秒的时间内运行上面的代码(在0.00001的公差和h1,h2,h3和t的5位小数的精度)(在~431,000次迭代时找到几乎精确的解)。打印每一行时,相同的计算大约需要40秒。