You have a couple of problems:
- First and most crucially, your loop never changes the values of your variables, so it will never exit (because it's just performing the same test indefinitely.
- Second and nearly as importantly, you shouldn't loop on a comparison of floating-point numbers if you want your program to terminate. This is because floating-point representations are inherently inaccurate, and as a result your comparisons will evaluate as
False
when you might expect them to be True
(e.g. eumiro's example in the comments of .3 + .3 + .3 == .9
evaluating to False
).
The solution for the first problem is simple: you simply have to make sure you're recalculating your variables each time through the loop. The second one is just as simple: instead of testing for equality, you need to test whether the value is "close enough". So pick a tolerance value and compare your error to that, instead of asking for perfection. Both of these are shown in the code following (I've split out some pieces into their own functions for clarity). I've also made it so c
is only printed every 5000 iterations, which actually 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))
Note on line 26 where the while loop compares abs(volumetotal - 187.2)
(the amount that the result differs from the expected result) to 0.00001
(the tolerance). You can set this tolerance to whatever you like, but the lower the tolerance, the longer the program will take to run.
Just for fun, what you may really want is to select a precision for your output (h1
, h2
, and so on). Then instead of using random numbers, you can increment your variables predictably until you get within the tolerance. This is probably the way I'd do it, instead of using randoms, because it does at least guarantee that your loop will terminate, and it provides a bit more control over your inputs/outputs.
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))
Your biggest speedup (by far) actually comes from cutting down on the print
statements. My machine runs the above code (at tolerance of 0.00001 and precision of 5 decimal places for h1, h2, h3, and t) in less than a second (finds a nearly exact solution at ~431,000 iterations). The same calculations take about 40 seconds while printing every line.