Outputing time once in python - python

I'm trying to make the program output the time it took to complete fib(n) but during the time it's calculating, it continuously posts minute amounts of time. How do I get the program to just output the time once. Here if my program:
import time
def fib(n):
if n <= 1:
return 1
else:
start_time = time.time()
answer = fib(n-1) + fib(n-2)
end_time = time.time()
total_time = end_time - start_time
print(total_time)
return answer

Since your function is recursive, each call will print out its own time. If you want to know how much time the function took, I would suggest wrapping the main call to fib in a time statement, rather than putting the timing in the actual function code.

Instead of placing the code which calculates the time inside the fib() function, place it outside the function, like so:
import time
def fib(n):
if n <= 1:
return 1
else:
answer = fib(n-1) + fib(n-2)
return answer
#Place it all here
start_time = time.time()
fib(90) #Or some other number
end_time = time.time()
total_time = end_time - start_time
print(total_time)

You use my timing program that I wrote.
#!python3
import timeit
from os import system
system('cls')
# % % % % % % % % % % % % % % % % %
# times the code 100 times
runs = 100
totalTime = 0.0; average = 0.0
testTimes = []
for i in range(runs):
startTimer = timeit.default_timer()
# % % % % % % % % % % % % % % % %
# >>>>> code to be tested goes here <<<<<
def fib(n):
if n <= 1:
return 1
else:
answer = fib(n - 1) + fib(n - 2)
return answer
r = fib(26)
print('fib result is:', r)
# % % % % % % % % % % % % % % % %
endTimer = timeit.default_timer()
timeInterval = endTimer - startTimer
testTimes.append(timeInterval)
totalTime += timeInterval
print('\n', '{} {:.4f} {}'.format("This run's time is", timeInterval,
'seconds' + '\n'))
# print the results
print('{} {:.4f} {}'.format(' Total time:', totalTime, 'seconds'))
print('{} {:.4f} {}'.format('Shortest time:', min(testTimes), 'seconds'))
print('{} {:.4f} {}'.format(' Longest time:', max(testTimes), 'seconds'))
print('{} {:.4f} {}'.format(' Average time:', (totalTime / runs), 'seconds'))

As others noted, to time a recursive function, place the timings around the call to the function, not in the function. Here some additional code to time computing the first 30 number of the sequence.
import time
import numpy as np
def fib(n):
if n <= 1:
answer = 1
else:
answer = fib(n-1) + fib(n-2)
return answer
for i in np.arange(1,30):
start = time.time()
f = fib(i)
end = time.time()
total = end - start
print(i, fib(i), total)

Related

Correct way to append to string in python

I've read this reply which explains that CPython has an optimization to do an in-place append without copy when appending to a string using a = a + b or a += b. I've also read this PEP8 recommendation:
Code should be written in a way that does not disadvantage other
implementations of Python (PyPy, Jython, IronPython, Cython, Psyco,
and such). For example, do not rely on CPython’s efficient
implementation of in-place string concatenation for statements in the
form a += b or a = a + b. This optimization is fragile even in CPython
(it only works for some types) and isn’t present at all in
implementations that don’t use refcounting. In performance sensitive
parts of the library, the ''.join() form should be used instead. This
will ensure that concatenation occurs in linear time across various
implementations.
So if I understand correctly, instead of doing a += b + c in order to trigger this CPython optimization which does the replacement in-place, the proper way is to call a = ''.join([a, b, c]) ?
But then why is this form with join significantly slower than the form in += in this example (In loop1 I'm using a = a + b + c on purpose in order to not trigger the CPython optimization)?
import os
import time
if __name__ == "__main__":
start_time = time.time()
print("begin: %s " % (start_time))
s = ""
for i in range(100000):
s = s + str(i) + '3'
time1 = time.time()
print("end loop1: %s " % (time1 - start_time))
s2 = ""
for i in range(100000):
s2 += str(i) + '3'
time2 = time.time()
print("end loop2: %s " % (time2 - time1))
s3 = ""
for i in range(100000):
s3 = ''.join([s3, str(i), '3'])
time3 = time.time()
print("end loop3: %s " % (time3 - time2))
The results show join is significantly slower in this case:
~/testdir$ python --version
Python 3.10.6
~/testdir$ python concatenate.py
begin: 1675268345.0761461
end loop1: 3.9019
end loop2: 0.0260
end loop3: 0.9289
Is my version with join wrong?
In "loop3" you bypass a lot of the gain of join() by continuously calling it in an unneeded way. It would be better to build up the full list of characters then join() once.
Check out:
import time
iterations = 100_000
##----------------
s = ""
start_time = time.time()
for i in range(iterations):
s = s + "." + '3'
end_time = time.time()
print("end loop1: %s " % (end_time - start_time))
##----------------
##----------------
s = ""
start_time = time.time()
for i in range(iterations):
s += "." + '3'
end_time = time.time()
print("end loop2: %s " % (end_time - start_time))
##----------------
##----------------
s = ""
start_time = time.time()
for i in range(iterations):
s = ''.join([s, ".", '3'])
end_time = time.time()
print("end loop3: %s " % (end_time - start_time))
##----------------
##----------------
s = []
start_time = time.time()
for i in range(iterations):
s.append(".")
s.append("3")
s = "".join(s)
end_time = time.time()
print("end loop4: %s " % (end_time - start_time))
##----------------
##----------------
s = []
start_time = time.time()
for i in range(iterations):
s.extend((".", "3"))
s = "".join(s)
end_time = time.time()
print("end loop5: %s " % (end_time - start_time))
##----------------
Just to be clear, you can run this with:
iterations = 10_000_000
If you like, just be sure to remove "loop1" and "loop3" as they get dramatically slower after about 300k.
When I run this with 10 million iterations I see:
end loop2: 16.977502584457397
end loop4: 1.6301295757293701
end loop5: 1.0435805320739746
So, clearly there is a way to use join() that is fast :-)
ADDENDUM:
#Étienne has suggested that making the string to append longer reverses the findings and that optimization of loop2 does not happen unless it is in a function. I do not see the same.
import time
iterations = 10_000_000
string_to_append = "345678912"
def loop2(iterations):
s = ""
for i in range(iterations):
s += "." + string_to_append
return s
def loop4(iterations):
s = []
for i in range(iterations):
s.append(".")
s.append(string_to_append)
return "".join(s)
def loop5(iterations):
s = []
for i in range(iterations):
s.extend((".", string_to_append))
return "".join(s)
##----------------
start_time = time.time()
s = loop2(iterations)
end_time = time.time()
print("end loop2: %s " % (end_time - start_time))
##----------------
##----------------
start_time = time.time()
s = loop4(iterations)
end_time = time.time()
print("end loop4: %s " % (end_time - start_time))
##----------------
##----------------
start_time = time.time()
s = loop5(iterations)
end_time = time.time()
print("end loop5: %s " % (end_time - start_time))
##----------------
On python 3.10 and 3.11 the results are similar. I get results like the following:
end loop2: 336.98531889915466
end loop4: 1.0211727619171143
end loop5: 1.1640543937683105
that continue to suggest to me that join() is overwhelmingly faster.
This is just to add the results from #JonSG answer with different python implementations I have available, posted as an answer, because cannot use formatting in an comment.
The only modification is that I was using 1M iterations and for "local" I've wrapped whole test in test() function, doing it inside 'if name == "main":' block, doesn't seem to help with 3.11 regression Étienne mentioned. With 3.12.0a5 I'm seeing similar difference between local and global s variable, but it's a lot faster.
loop
3.10.10
3.10.10
3.11.2
3.11.2
3.12.0a5
3.12.0a5
pypy 3.9.16
pypy 3.9.16
global
local
global
local
global
local
global
local
a = a + b + c
71.04
71.76
92.55
90.57
91.24
92.08
120.05
97.94
a += b + c
0.38
0.20
26.57
0.21
24.06
0.03
108.98
89.62
a = ''.join(a, b, c)
23.26
21.96
25.31
24.60
23.94
23.79
94.04
90.88
a.append(b);a.append(c)
0.50
0.38
0.35
0.23
0.0692
0.0334
0.12
0.12
a.extend((b, c))
0.35
0.27
0.29
0.19
0.0684
0.0343
0.10
0.10

How to Remove the last few characters from console Python?

I am making a little math game, similar to zeta mac. Everything seems to be working well. Ideally I would like this console output to erase incorrect answers entered by the user, without reprinting the math problem again for them to solve. Is something like this possible?
For example, I may prompt the user to answer "57 + 37 = " in the console, then if they type 24 (console would look like this "57 + 37 = 24", I would like the 24 to be erased, and for the "57 + 37 = " to remain, allowing the user to guess again, without the same equation having to be printed again on a line below.
Here is the source code (sorry if its messy, I just started learning python):
import random
import time
def play(seconds):
start_time = time.time()
score = 0
while True:
current_time = time.time()
elapsed_time = current_time - start_time
a = random.randint(2, 100)
b = random.randint(2, 100)
d = random.randint(2, 12)
asmd = random.choice([1, 2, 3, 4])
if (asmd == 1):
solve = a + b
answer = input("%d + %d = " % (a, b))
elif (asmd == 2):
if (a > b):
solve = a - b
answer = input("%d - %d = " % (a, b))
else:
solve = b - a
answer = input("%d - %d = " % (b, a))
elif (asmd == 3):
solve = a * d
answer = input("%d * %d = " % (a, d))
else:
solve = d
c = a * d
answer = input("%d / %d = " % (c, a))
while (solve != int(answer)):
answer = input("= ")
score += 1
if elapsed_time > seconds:
print("Time\'s up! Your score was %d." % (score))
break
play(10)
Just use add these two lines after answer = input("= "):
sys.stdout.write("\033[F") #back to previous line
sys.stdout.write("\033[K") #clear line
import random
import time
import sys
def play(seconds):
start_time = time.time()
score = 0
while True:
current_time = time.time()
elapsed_time = current_time - start_time
a = random.randint(2, 100)
b = random.randint(2, 100)
d = random.randint(2, 12)
asmd = random.choice([1, 2, 3, 4])
if (asmd == 1):
solve = a + b
answer = input("%d + %d = " % (a, b))
elif (asmd == 2):
if (a > b):
solve = a - b
answer = input("%d - %d = " % (a, b))
else:
solve = b - a
answer = input("%d - %d = " % (b, a))
elif (asmd == 3):
solve = a * d
answer = input("%d * %d = " % (a, d))
else:
solve = d
c = a * d
answer = input("%d / %d = " % (c, a))
while (solve != int(answer)):
answer = input("= ")
if solve != int(answer):
sys.stdout.write("\033[F") #back to previous line
sys.stdout.write("\033[K") #clear line
score += 1
if elapsed_time > seconds:
print("Time\'s up! Your score was %d." % (score))
break
play(10)
Removing characters from the end of the input is probably not the best approach as you aren't quite sure how many characters there will be (depends on the number).
The answer from #QualidSai does clear the terminal but it doesn't show your formula string again. To solve this I'd store the formula string that you print as part of your input as a string variable, then you can use that when looping. For example:
import random
import time
import sys
def play(seconds):
start_time = time.time()
score = 0
while True:
current_time = time.time()
elapsed_time = current_time - start_time
a = random.randint(2, 100)
b = random.randint(2, 100)
d = random.randint(2, 12)
asmd = random.choice([1, 2, 3, 4])
if (asmd == 1):
solve = a + b
question = "%d + %d = " % (a, b)
elif (asmd == 2):
if (a > b):
solve = a - b
question = "%d - %d = " % (a, b)
else:
solve = b - a
question = "%d - %d = " % (b, a)
elif (asmd == 3):
solve = a * d
question = "%d * %d = " % (a, d)
else:
solve = d
c = a * d
question = "%d / %d = " % (c, a)
answer = False
while (solve != int(answer)):
answer = input(question)
sys.stdout.write("\033[F")
sys.stdout.write("\033[K")
score += 1
if elapsed_time > seconds:
print("Time\'s up! Your score was %d." % (score))
break
play(10)

Multiprocessing in python is the same as sequentially processing

I want to print the numbers from 0 to 100000 sequentially and also using a processing pool of 12 processes.
def print1(x):
print(x)
def print2(x):
i = 0
while(i < x):
print(i)
i+=1
if __name__ == '__main__':
start_time = time.time()
p = Pool(12)
p.map(print1, [i for i in range(100000)])
print("--- %s seconds ---" % (time.time() - start_time))
start_time = time.time()
print2(100000)
print("--- %s seconds ---" % (time.time() - start_time))
But it takes almost the same time and if you look at the cpu utilization is also the same.
On the left with multiprcessing and on the right sequentially
Why the CPU is using the same resources ?

Error finding current time in Python

I am trying to implement this time class to find the current time. I am getting wrong time. Can someone please take a look what am I doing wrong here?
import time
import datetime
class Time():
def __init__(self):
currentTime = time.time()
totalSeconds = int(currentTime)
self.__second = totalSeconds % 60
totalMinutes = totalSeconds // 60
self.__minute = totalMinutes %60
totalHours = totalMinutes// 60
self.__hour = totalHours % 12
def getSecond(self):
return self.__second
def getMinute(self):
return self.__minute
def getHour(self):
return self.__hour
def main():
t = Time()
print("Current Time is : ", t.getHour(), ":", t.getMinute(), ":", t.getSecond())
print(datetime.datetime.now().time())
print(time.ctime())
main()
Your code always deals with UTC times, but you are comparing the result with time in your time zone. Try this:
def main():
t = Time()
print("Current Time is : ", t.getHour(), ":", t.getMinute(), ":", t.getSecond())
print(datetime.datetime.utcnow().time())
print(time.asctime(time.gmtime()))

tkMessageBox has extra window in timer program

I'm doing a timer in Python, but my code ends up showing two dialog boxes instead of one when the timer stops. What can I do to avoid having the extra window appear?
Code for timer.py below:
#!/usr/bin/python
import time
start = time.time()
interval=1
import sys
import tkMessageBox
def showmessage():
tkMessageBox.showinfo("timer.py", "Timer Complete!")
if __name__ == "__main__":
if len(sys.argv) > 1:
ns = {'__builtins__': None}
seconds=int(eval(str(sys.argv[1]), ns))
if len(sys.argv) > 2:
if sys.argv[2]=="mins" or sys.argv[2]=="min":
seconds=seconds*60
interval = 1 if seconds < interval else interval
second=seconds - (time.time() - start) + 0.5
while second >= 0:
#In Python 3: print('string', end='\r')
if int(second/3600) > 0:
print "\rTime =%3d hrs %02d mins %02d sec" % \
(second/3600, (second % 3600)/60, second % 60),
else:
print "\rTime =%3d mins %02d sec" % \
(second/60, second % 60),
sys.stdout.flush()
time.sleep(interval)
if int(second % 60+0.5) == 0:
print #or sys.stdout.write("\n")
second=seconds - (time.time() - start) + 0.5
showmessage()
else:
print ("Usage: %s [seconds]" % str(sys.argv[0]))
print (" %s [minutes] mins" % str(sys.argv[0]))
that happens because the messagebox wants a parent window if you change showmessage to the following:
def showmessage():
root = tk.Tk()
root.withdraw()
tkMessageBox.showinfo("timer.py", "Timer Complete!")
root.destroy()
and add:
import Tkinter as tk
to your imports then you will only get the messagebox

Categories