Using Timeit within a while loop and increasing input size - python

I'm stumped. Basically, I have two functions that take an input (which is a list of randomly generated numbers).
Then I wanted to figure out the size of the input list in which the time it takes both functions to execute is equal.
Here's what I have:
def function1(inputlist):
#does something to inputlist
def function2(inputlist):
#does same thing to inputlist as before but in different way
time1 = timeit.timeit(stmt="function1(random.sample([x for x in range(0, 20000), 10)",setup = "from file import function1,input",number=100)
time2 = timeit.timeit(stmt="function2(random.sample([x for x in range(0, 20000), 10)",setup = "from file import function2,input",number=100)
i=0
while time1<time2:
time1 = timeit.timeit(stmt="function1(random.sample([x for x in range(0, 20000), i)",setup = "from file import function1",number=100)
time2 = timeit.timeit(stmt="function2(random.sample([x for x in range(0, 20000), i)",setup = "from file import function2",number=100)
i = i+1
print(i)
I can't figure this out. I've tried everything I could find. I know you can't reference outside variables when using timeit, so I tried working around that too. I basically need any sort of method where I can increase the input side of the function and calculate each time.
Thanks so much in advance. I've been struggling on this for over a day now.

I think the easiest way here, would be to format the string accordingly:
Instead of
stmt="function1(random.sample([x for x in range(0, 20000)], i))"
write
stmt=f"function1(random.sample([x for x in range(0, 20000)], {i}))"
This will put the current value of i where {i} stands. See the docs for further information.

Related

How do I get random numbers that fully cover a range?

I'm trying to have random integers between (1,5) but the catch is displaying all of the values from the random integer in a for loop ranging of 10 loops. I only have access to randint() and random() methods in 'random class'.
from random import randint
eventList = []
taskList = []
dayList = []
def getEventList():
eventList.sort()
return eventList
def getTaskList():
return taskList
def getDayList():
return dayList
def generateData():
while len(getTaskList()) < 10:
# Need to implement a way to stretch the random int while having all the integers present
randomEvent = randint(1, 5)
randomTask = randint(10, 30)
randomDay = randint(1, 9)
eventList.append(randomEvent)
dayList.append(randomDay)
if randomTask not in getTaskList():
taskList.append(randomTask)
Based on clarifications in my other answer, I think you meant to ask "how to I get random numbers that fully cover a range?"
You are using randint, and just calling it extra times hoping to get all the values. But depending on random chance, that can take a while.
It would be better to just take all the values you want, e.g. list(range(1,6))
and then just rearrange that with random.shuffle
https://docs.python.org/3/library/random.html#random.shuffle
import random
values = list(range(1, 6))
random.shuffle(values)
print(values)
Obviously, if this is what you want to do, but your prof says you can't use that function, then you should use that function, get working code, and only THEN write your own replacement for the standard library. (so you only have to debug one or the other)
So write your own version of shuffle. I'll leave the details to you, but it should be pretty easy to use randint to pick one of the available entries, removing it, and so on.
In order to sort things, you need "things" to sort. Therefore you must group together all three numbers in a single entity, and then sort those entities. I suggest you use a Task class, and let it handle things like generating a unique task number. Avoid global variables.
Here is a minimal reproduction (based on your code) of just taking your three numbers in a tuple, combining them with the builtin "zip" and sorting that:
from random import randint
def generateData():
eventList = []
taskList = []
dayList = []
for _ in range(10):
randomEvent = randint(1, 5)
randomTask = randint(10, 30)
randomDay = randint(1, 9)
eventList.append(randomEvent)
taskList.append(randomTask)
dayList.append(randomDay)
return sorted(zip(eventList, taskList, dayList))
for task in generateData():
print(task)
Also note that python convention for variable names is a little different, but I left that alone and used your names.

Python random.randrange producing everything but the step

When using this following code:
import random
sticks = 100
randomstep = random.randint(1, 6)
expertmarbles = random.randrange(1, sticks, 2**randomstep)
the output is producing everything excluding the step, so for example i would like this to output a random from these numbers: 2,4,8,16,32,64. However it will output everything but these numbers. Can anyone offer any advice, the reason i'm using variables here is because the amount of sticks will decrease.
Instead of using random.randrange you could use random.choice (docs):
import random
min_power = 1
max_power = 6
print(random.choice([2**i for i in range(min_power, max_power + 1)]))
You can try this
def myRand(pow_min,pow_max):
return 2**random.randint(pow_min,pow_max)
I would suggest you to use this instead of random.choice, which requires you to generate a list, which is unnecessary.

Python script: creating functioning loop

I am currently trying to program a script for my school project. It is supposed to take one pixel from the camera feed and measure at that spot multiple times (for noise cancellation). The part of my code that I am not satisfied with currently looks like this:
canon.ScanXY(x,y)
img_1 = canon.FetchImage(0, 1 * 1)
canon.StopScan()
canon.ScanXY(x,y)
img_2 = canon.FetchImage(0, 1 * 1)
canon.StopScan()
...
canon.ScanXY(x,y)
img_xxx = canon.FetchImage(0, 1 * 1)
canon.StopScan()
The code above will return a string with name img_xxx where xxx stands for a specific number. I was wondering, if there is an easier way to do this. I was trying to loop the function, but was unable to do it so that I have different result from every iteration. And at the end I need to add all of those together in order to create one string from all that were generated before:
img_final = (img_1 + img_2 + img_3 + ... + img_xxx)
and finally to print a Picture out of the result, using
img = np.fromstring(img_final, dtype="uint8").reshape(8,8)
fig, ax = plt.subplots()
ax.xaxis.set_visible(False)
ax.yaxis.set_visible(False)
ax.set_xticklabels((0,1))
x=ax.imshow(img,cmap="gray")
The code works, but if I want to change anything, it really takes a lot of time. So far I was working with 64 or less strings, but I want to refine the results, so I would need much more iterations. Any help would be appreciated.
img_final is supposed to be the sum of a sequence of individual img's. The analogy to adding a sequence of integers read from the console would be:
final = int(input("First num: "))
for x in range(xxx-1):
num = int(input("Next num: "))
final += num

trying to find out which is unpack and format is faster results in sporadic behavior, why?

I'm trying to accurately determine which method of unpacking binary data into a view-able format is faster. I'm attempting to use the time module to do so. I'm working with the bitstring module as I found this the easiest way when unpacking i'm working with bit aligned data. This is a small test case to see which way is faster as i'm processing millions of lines. It needs to be displayed in a specific way which is why the formatting is there.
from bitstring import BitArray
import time
s = BitArray('0x0081')
start = time.time()
for i in range(100000):
test = s.unpack('uintle:16')
temp = hex(test[0]).lstrip('0x').zfill(4)
end = time.time()
ttime = end-start
print("uintle " + str(ttime))
start = time.time()
for i in range(100000):
hex_val = s.unpack('hex:16')
temp = hex_val[0][2:]+hex_val[0][0:2]
end = time.time()
ttime = end-start
print("hex " + str(ttime))
when testing the condition on 1 million loops this is the output:
uintle 32.51800322532654
uintle 46.38693380355835
hex 131.79687571525574
It doesn't seem valid as it prints one output twice and I can't figure out why that happens.
When testing with 100,000 loops this is the output:
uintle 2.705230951309204
hex 6.699380159378052
only two outputs just as expected. Any ideas on why it is behaving as such?

What's wrong with my python multiprocessing code?

I am an almost new programmer learning python for a few months. For the last 2 weeks, I had been coding to make a script to search permutations of numbers that make magic squares.
Finally I succeeded in searching the whole 880 4x4 magic square numbers sets within 30 seconds. After that I made some different Perimeter Magic Square program. It finds out more than 10,000,000 permutations so that I want to store them part by part to files. The problem is that my program doesn't use all my processes that while it is working to store some partial data to a file, it stops searching new number sets. I hope I could make one process of my CPU keep searching on and the others store the searched data to files.
The following is of the similar structure to my magic square program.
while True:
print('How many digits do you want? (more than 20): ', end='')
ansr = input()
if ansr.isdigit() and int(ansr) > 20:
ansr = int(ansr)
break
else:
continue
fileNum = 0
itemCount = 0
def fileMaker():
global fileNum, itemCount
tempStr = ''
for i in permutationList:
itemCount += 1
tempStr += str(sum(i[:3])) + ' : ' + str(i) + ' : ' + str(itemCount) + '\n'
fileNum += 1
file = open('{0} Permutations {1:03}.txt'.format(ansr, fileNum), 'w')
file.write(tempStr)
file.close()
numList = [i for i in range(1, ansr+1)]
permutationList = []
itemCount = 0
def makePermutList(numList, ansr):
global permutationList
for i in numList:
numList1 = numList[:]
numList1.remove(i)
for ii in numList1:
numList2 = numList1[:]
numList2.remove(ii)
for iii in numList2:
numList3 = numList2[:]
numList3.remove(iii)
for iiii in numList3:
numList4 = numList3[:]
numList4.remove(iiii)
for v in numList4:
permutationList.append([i, ii, iii, iiii, v])
if len(permutationList) == 200000:
print(permutationList[-1])
fileMaker()
permutationList = []
fileMaker()
makePermutList(numList, ansr)
I added from multiprocessing import Pool at the top. And I replaced two 'fileMaker()' parts at the end with the following.
if __name__ == '__main__':
workers = Pool(processes=2)
workers.map(fileMaker, ())
The result? Oh no. It just works awkwardly. For now, multiprocessing looks too difficult for me.
Anybody, please, teach me something. How should my code be modified?
Well, addressing some things that are bugging me before getting to your asked question.
numList = [i for i in range(1, ansr+1)]
I know list comprehensions are cool, but please just do list(range(1, ansr+1)) if you need the iterable to be a list (which you probably don't need, but I digress).
def makePermutList(numList, ansr):
...
This is quite the hack. Is there a reason you can't use itertools.permutations(numList,n)? It's certainly going to be faster, and friendlier on memory.
Lastly, answering your question: if you are looking to improve i/o performance, the last thing you should do is make it multithreaded. I don't mean you shouldn't do it, I mean that it should literally be the last thing you do. Refactor/improve other things first.
You need to take all of that top-level code that uses globals, apply the backspace key to it, and rewrite functions that pass data around properly. Then you can think about using threads. I would personally use from threading import Thread and manually spawn Threads to do each unit of I/O rather than using multiprocessing.

Categories