How to change the function being timed? - python

I'm trying to time all the different sorting algorithms to see which is fastest but every time I do that I need to rewrite the bottom half of the code again (under #####) except I have to change all the variable names (and instead of selectionsort(mylist) I do bubblesort(mylist) etc). I guess it's not the end of the world but I can't help but imagine it can be written much better. I know there are other options for timing it that may be better but I've been told I have to use perf_count.
def selectionsort(mylist):
sortedlist=[]
while len(mylist) > 0:
lowest = mylist[0]
for i in mylist:
if i < lowest:
lowest=i
sortedlist.append(lowest)
mylist.remove(lowest)
return sortedlist
ivalues = [2,4,8,16,32,64,128,256,512,1024]
#####
sorttimelist = []
for i in range(1,11):
mylist=[]
for j in range(2**i):
mylist.append(random.random())
start_time=time.perf_counter()
selectionsort(mylist)
end_time=time.perf_counter()
sorttime=end_time-start_time
sorttimelist.append(sorttime)

You can use a for loop to go through different functions to use in your code. Since functions are essentially variables, you can assign one to func in the for loop, and call it like func(my_list)
#####
for func in [selectionsort, bubblesort]:
for i in range(1,11):
mylist=[]
for j in range(2**i):
mylist.append(random.random())
start_time = time.perf_counter()
func(mylist) # use func instead of selectionsort
end_time = time.perf_counter()
sorttime = end_time - start_time
sorttimelist.append(sorttime)

You can iterate over collection of functions you are going to benchmark. See example:
def selection_sort(my_list):
pass
def bubble_sort(my_list):
pass
functions = [selection_sort, bubble_sort]
for func in functions:
func(list_to_sort)

Use a decorator, put #timit above your function.
import time
def timit(func):
'''
A Decorator that times how long it takes to return the function. Added time.sleep because some functions run under a seconds and would return 0 seconds.
'''
def inner(*args, **kwargs):
start = float(time.time())
time.sleep(1)
test = func(*args, **kwargs)
end = float(time.time())
print(f'Funtion {func.__name__} took {end-start-1} seconds to complete')
return test
return inner
#timit
def bubble_sort(array):
for last_idx in range(1,len(array)):
is_sorted = True
for idx in range(len(array)-last_idx-1):
if array[idx] > array[idx+1]:
is_sorted = False
swap(array, idx, idx+1)
if is_sorted is True:
break
return array
#timit
def selection_sort(array):
for first_idx in range(len(array)-1):
smallest = array[first_idx]
for idx in range(first_idx+1, len(array)):
if array[idx] < smallest:
smallest = array[idx]
swap(array, idx, first_idx)
return array

Related

How to print something only if a function called is true ten times python

Hi :) my question is basically what the title says. How do i create a function that calls another function ten times and only if all those times the function2 returns True function 1 prints “good job”
For example:
def function_one(s) #returns true if string is increasing
x = all(x <= y for x, y in itertools.pairwise(seq))
return x
Function 2 should only print good job if function one returns true when called ten times.
I tried:
s = random.randint(0,10)
for i in range (10):
if function_one(s) == True:
print (“Good job”)
But this prints it the second function one is true, no matter how many other falses there were prior. Please help :)
Just use all as you did in function_one.
Here is an example with a different function_one:
import random
# make example reproducible
random.seed(0)
# dummy function that returns True with p=0.9
# and also prints an indicator ./× for True/False
def function_one():
# return True with a probability of 9/10
n = random.random() > 0.1
print('⋅' if n else '×', end='')
return n
def function_two():
if all(function_one() for _ in range(10)): # important stuff here
print(' Good job')
else:
print(' failed!')
# repeat the experiment a few times
for i in range(5):
function_two()
output:
⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ Good job
⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ Good job
⋅⋅⋅⋅⋅⋅⋅⋅⋅⋅ Good job
⋅⋅⋅⋅⋅× failed!
⋅⋅⋅⋅× failed!
You can use a list and then min:
lst=[]
for i in range (10):
s = random.randint(0,10)
lst.append(function_one(s))
if min(lst) == True:
print (“Good job”)
Note that the random.randint(0,10) should be inside the loop otherwise you use the same value all the time
You need to "link" all 10 calls to function 1. For example this should do what you want (I left the s assignment at its original position since I don't know what it's for. Perhaps it should be in the for loop):
def func2 ():
s = random.randint(0,10)
flag = True
for i in range (10):
newtry = function_one(s)
flag = flag and newtry
if flag:
print (“Good job”)

Reset counter to zero if different day - Python

New to python, so I have this setup where I file gets created, and I have to add an extension number. The first file will have an extension number of 1 since being the first. A second file gets created and the extension number will increment, so it will be 2. So each files gets created, the extension number will increment.
Now, if it's a different day then that extension number will reset to 1, and it will increment if new files are created. So each day, the extension number needs to be reset to 1
def get_counter(date):
counter = 1
now = datetime.datetime.utcnow().strftime('%Y-%m-%d')
if date != now:
now = date
counter = 1
return counter
counter += 1
return counter
I have set up this function but it will not work because the now and counter variable will get overwritten. So will need these variables somewhere else. Just wondering if there is a work around this process or is there a python library that can handle this type of situation. Your suggestions will be appreciated!
You could assign the counter outside of that function and send it as a parameter, that way you don't overwrite it every single time you call your function, like so:
counter = 1
for file_to_be_writen in file_collection:
counter = get_counter(date, counter)
and leave your function like this:
def get_counter(date, counter):
now = datetime.datetime.utcnow().strftime('%Y-%m-%d')
if date == now:
counter += 1
return counter
return counter
When you need to preserve state across function calls that is a hint that you need a custom object. You could use global variables as well but encapsulating the state inside an object is usually better.
Here I implement a class Counter that takes care of everything. It has a __next__ method that returns the next number so the calling code only needs to call next(counter). It also has an __iter__ method so it can be used in for loops.
You need to provide a function to get the current (date_getter) time when creating an instance. Besides making the code more testable this allows you to decide if you want to use utc time, local time, the first day of the week so the counter resets each week, etc.
import datetime
class TimeArrowReversedError(Exception):
pass
class Counter:
def __init__(self, date_getter):
self._date_getter = date_getter
self._current_date = date_getter()
self._current_value = 0
def _check_date(self):
current_date = self._date_getter()
if self._current_date > current_date:
message = 'Time arrow got reversed. Abandon all hope.'
raise TimeArrowReversedError(message)
if self._current_date < current_date:
self._current_date = current_date
self._current_value = 0
def __next__(self):
self._check_date()
self._current_value += 1
return self._current_value
def __iter__(self):
return self
This is the code I used to test it. Note that I am using as date_getter a function that actually returns whatever date I want. I do not want to wait until 23:59 to run the test. Instead I tell the function which date to return (including going backwards in time) and see how the counter behaves.
current_date = datetime.date(year=2018, month=5, day=9)
get_current_date = lambda: current_date
counter = Counter(get_current_date)
n = next(counter)
assert n == 1
n = next(counter)
assert n == 2
for expected, result in zip([3, 4], counter):
assert expected == result
current_date = current_date + datetime.timedelta(days=1)
n = next(counter)
assert n == 1
n = next(counter)
assert n == 2
current_date = current_date - datetime.timedelta(days=2)
try:
n = next(counter)
except TimeArrowReversedError:
pass
else:
raise AssertionError('"TimeArrowReversedError" expected.')
Here is a more realistic way in which yo could use this class:
def create_counter():
return Counter(datetime.datetime.utcnow().date)
counter = create_counter()
Print the first couple of numbers:
print(next(counter))
print(next(counter))
Output:
1
2
Using a loop to add numbers to names in a list:
names = ['foo', 'bar']
for name, n in zip(names, counter):
print('{}_{}'.format(name, n))
Output:
foo_3
bar_4
Now I realize that Counter is a really bad choice because there is already a completely unrelated Counter class in the standard library. But I cannot think of a better name right now so I will leave it as is.

Why my using memo didn't raise the efficiency at all?

I am doing exercise in Think in Python, using Memo to calculate Fibonacci sequence is far more efficiency than not using one. But when implemented it and testing the time consumed, I find the running time is not reduced at all. I know there are certainly something wrong with my program, could someone please tell me where I went wrong. Many thanks.
import time
known = {0:0,1:1}
def fibonacci_memo(n):
"""return the nth number of fibonacci sequence
using memo to raise efficiency"""
if n in known:
return known[n]
res = fibonacci(n-1) + fibonacci(n-2)
known[n] = res
return res
def fibonacci(n):
"""return the nth number of fibonacci sequence
without using memo"""
if n == 0:
return 0
if n == 1:
return 1
return fibonacci(n-1) + fibonacci(n-2)
if __name__ == '__main__':
start = time.clock()
print fibonacci_memo(32)
elaspsed = time.clock() - start
print 'using memo time used: ' + str(elaspsed)
start = time.clock()
print fibonacci(32)
elaspsed = time.clock() - start
print 'without using memo time used: ' + str(elaspsed)
The Output is something like:
2178309
using memo time used: 1.83040345779
2178309
without using memo time used: 1.792043347
Your fibonacci_memo function isn't calling itself recursively, it's calling the original (non-memoized) fibonacci function.
The recursion of your memoized function is calling a different function. Try replacing fibonacci_memo with this:
def fibonacci_memo(n):
"""return the nth number of fibonacci sequence
using memo to raise efficiency"""
if n in known:
return known[n]
res = fibonacci_memo(n-1) + fibonacci_memo(n-2)
known[n] = res
return res

Is there better way to swallow StopIteration exception generated by yield in python?

Now I plan to learn more about yield in python. And I found some codes about yield, which implemented the algorithm Reservoir Sampling as following:
def RandomSelect(knum, rand=None):
''' (int, func) -> list
Reservoir Sampling implementation
'''
selection = None
k_elems_list = []
count = 0
if rand is None:
rand = Random()
while True:
item = yield selection
if len(k_elems_list) < knum:
k_elems_list.append(item)
else:
# Randomly replace elements in the reservoir with a decreasing probability
r = rand.randint(0, count)
if r < knum:
k_elems_list[r] = item
count += 1
print k_elems_list
In order to break the while loop, I just add some codes after item = yield selection
if item == -1: # reach to the end of data, just break
break
Question 1, Is there any better way to break out the while loop?
To call the function RandomSelect,
myList = [1,2,3,4,5,6,7,8,-1]
cr = RandomSelect(3);
cr.next() # advance to the yield statement, otherwise I can't call send
try:
for val in myList:
cr.send(val)
except StopIteration:
pass
finally:
del cr
I have to catch the StopIteration exception explicitly.
Question 2, is there any better way to swallow the StopIteration in the codes?
I think a slightly cleaner way to accomplish what is being done — which addresses both your questions — would be to explicitly close the generator by calling itsclose()method to terminate it and break out of the loop. Doing so also means aStopIterationdoesn't need to be "swallowed". Another benefit is it's no longer necessary to add the -1 sentinel value at the end of the list.
def RandomSelect(knum, rand=None):
''' (int, func) -> list
Reservoir Sampling implementation
'''
selection = None
k_elems_list = []
count = 0
if rand is None:
rand = Random()
while True:
try:
item = yield selection
except GeneratorExit:
break
if len(k_elems_list) < knum:
k_elems_list.append(item)
else:
# Randomly replace elements in the reservoir with a decreasing probability
r = rand.randint(0, count)
if r < knum:
k_elems_list[r] = item
count += 1
print k_elems_list
myList = [1,2,3,4,5,6,7,8]
cr = RandomSelect(3)
cr.next() # advance to the yield statement, otherwise I can't call send
for val in myList:
cr.send(val)
cr.close()
del cr
A minor additional enhancement (about something you didn't ask about) would be to make it so it wasn't necessary to manually advance to theyieldstatement before callingsend(). A good way to accomplish that would be with a decorator function similar to the one namedconsumer()David Beazley described in his Generator Tricks
For Systems Programmers presentation at PyCon 2008:
def coroutine(func):
""" Decorator that takes care of starting a coroutine automatically. """
def start(*args, **kwargs):
cr = func(*args, **kwargs)
cr.next()
return cr
return start
#coroutine
def RandomSelect(knum, rand=None):
.
.
.
print k_elems_list
myList = [1,2,3,4,5,6,7,8]
cr = RandomSelect(3)
#cr.next() # NO LONGER NECESSARY
for val in myList:
cr.send(val)
cr.close()
del cr

Instead of continue, rerun function

I'm wondering how to do the following in Python.
If I have a function with a for loop, it is possible to with an if statement to skip certain numbers.
This is an implementation of fisher-yates d got from activestate.com.
import random
def shuffle(ary):
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
continue
ary[d],ary[e]=ary[e],ary[d]
return ary
Now continue simply goes to the next value for d. How can I, instead of doing continue, rerun the function with the original parameter ary?
Note that the function is just some example code, I'm curious on how to do this in general.
Also, maintaining a copy of the array might not be possible if the list is big, so thats not really a solution imo.
This is a common recursive pattern. However, your case is a little different than usual because here you need to make a copy of your input list to use when you recurse if the shuffling fails.:
import random
def shuffle(ary):
initial = ary[:]
a=len(ary)
b=a-1
for d in range(b,0,-1):
e=random.randint(0,d)
if e == d:
return shuffle(initial)
ary[d],ary[e]=ary[e],ary[d]
return ary
ary = [1,2,3,4,5,6]
print shuffle(ary)
Also note that Wikipedia gives a (non-recursive) python implementation of the very similar Sattolo's algorithm.
from random import randrange
def sattoloCycle(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i) # 0 <= j <= i-1
items[j], items[i] = items[i], items[j]
return
If I read the article correctly, to re-acquire Fisher-Yates, you'd just do one simple change:
from random import randrange
def FisherYates(items):
i = len(items)
while i > 1:
i = i - 1
j = randrange(i+1) # 0 <= j <= i
items[j], items[i] = items[i], items[j]
return
def function(list):
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
while e > i:
e= randint(0,i)
"do something to the list"
return array
?
def function(list):
for i in (a for a in range(len(list)-1,0,-1) if randint(0,a) > a):
#do something with list
#do something else with remainder.
Not exactly what you asked for. Just wanted to remind you of this possibility.
you can copy the parameter to a temp variable. then call the function with the temp variable and use return;
def function(list):
listCopy = list;
len(list)-1
for i in range(len(list)-1,0,-1):
e= randint(0,i)
if e > i:
return function(listCopy)
else
"do something with the list"
return array

Categories