How Do You Predict A Non-Linear Script's Run Time? - python

I wrote this simple code in python to calculate a given number of primes.
The question I want to ask is whether or not it's possible for me to write a script that calculates how long it will take, in terms of processor cycles, to execute this? If yes then how?
primes = [2]
pstep = 3
count = 1
def ifprime (a):
""" Checking if the passed number is prime or not"""
global primes
for check in primes:
if (a%check) == 0:
return False
return True
while 1000000000>= count:
if ifprime(pstep):
primes.append (pstep)
print pstep
count += 1
pstep += 1
The interesting thing about this problem is that whether or not I find primes after x cycles of incrementation is something nearly impossible to predict. Moreover, there's recursion happening in this scenario since the larger 'prime' list grow the longer it will take to execute this function.
Any tips?

I think you would have to use an approximation of the distribution of primes, a la PNT which (I think) states that between 1 and x you'll have approximately x/ln(x) primes (ln being natural log). So given rough estimates of the time taken for a single iteration, you should be able to create an estimate.
You have approximately x/ln(x) primes in your list. Your main code block (inside the while loop) has constant time (effectively)...so:
t(x) ~ x/ln(x) * a + b + t(x-1)
where t(x) is the time taken up to and including iteration x, a is the time taken to check each prime in the list (modulous operation), and b is the 'constant' time of the main loop. I faintly remember there is a way to convert such recursive functions to linear ones ;)

If you want to predict the time an arbitrary process needs until it is finished, you can't do that, as that is basically the problem behind the Halting Problem. In special cases you can estimate the time your script will take, for example if you know that it is generated in a way that doesn't allow loops.
In your special case of finding primes, it is even harder to guess the time it will take before running the process, as there is only a lower bound for the number of primes within an intervall, but that doesn't help finding them.

Well, if you are on linux you can use 'time' command and then parse it's result.
For your problem I would do the timing for 1000s of large primes of different size and would draw a chart, so it would be easy to analize.

Well, there is a large branch of theoretical computer science -- complexity theory -- dedicated to just this sort of problem. The general problem (of deciding on whether a code will finish for arbitrary input) you have here is what is called "NP-complete" and is therefore very hard.
But in this case you probably have two options.
The first is to use brute force. Run timeit for isprime(a) for a=1, 2, 3, 4, ..., plot the graph of the times, and try to see if it looks like something obvious: a^2, a log a, whatever.
The right -- but harder -- answer is to analyze your algorithm and see if you can work out how many operations it takes for a "typical case".

When you call isprime(pstep) you are looping pstep * ln(pstep) times, if you have a prime, of which the probability is 1/ln(pstep). So the cost of testing the primes is proportional to step. Unknown is the cost of testing the composites, because we don't know the average lowest factor of the composites between 2 and N. If we ignore it, assuming it is dominated by the cost for the primes, we get a total cost of SUM(pstep) for pstep = 3 to N+3, which is about proportional to N**2.
You can reduce this to N**1.5 by cutting off the loop in isprime() when checked > sqrt(a).

Related

Factorial function in python is being limited

I made a simple factorial program:
import sys
sys.set_int_max_str_digits(0)
sys.setrecursionlimit(1000000)
def factorial(x):
if x == 0 | x == 1:
return 1
elif x > 1:
return x * factorial(x - 1)
i = 0
while 1:
print(factorial(i), '\n')
i += 1
But after a while the program halts. I want to know if there's a way to remove the limit on how big it could get.
Recursion is not meant to be infinite. Eventually your program would fail, even on a system with a huge amount of memory.
Also note that the recursion limit given to setrecursionlimit() is not a guarantee that you'll get that recursion depth. To quote from the sys.setrecursionlimit documentation:
The highest possible limit is platform-dependent. A user may need to set the limit higher when they have a program that requires deep recursion and a platform that supports a higher limit. This should be done with care, because a too-high limit can lead to a crash.
I would suggest either limiting the program to calculating a reasonable sized factorial, or not using recursion. Some tasks are much better suited to recursion versus iteration, but factorials is not one of them.

Why does this code crash my computer, not just mine but my friends too?

I was practicing writing python code, and I was trying to generate numbers of the Fibonacci series from 1,1,...
I wrote it and it was quite good, but then something odd happened which I write it down here:
# Generating Fibonacci Series in a List:
N = 1
M = 1
Fibonacci = []
ChoiceMethod = 0
ChoiceNum = 0
BelowNum = 0
ChoiceMethod = input('Choose Your Method of Work (1/2/3): ')
if int(ChoiceMethod) == 1:
ChoiceNum = input('Give your Choice Number: ')
while len(Fibonacci) != int(ChoiceNum):
Fibonacci.append(N)
Fibonacci.append(M)
N += M
M += N
print(Fibonacci)
You know it's the first part of the code, if you Run the code and enter 1 in the input (1st method) and then when it say Give your Choice Number, for example give it an even number like 2, it will work just fine, like any even numbers. as soon as you give it an odd number like 5, it will crash the PC. and I can't figure out why.
Found a Fix for it too, if you change while "!=" in "len(Fibonacci) != int(ChoiceNum)" to "<" it will work fine but idk what's wrong overally.
I'M TELLING YOU, IT WILL MOST PROBABLY CRASH YOUR PC TOO, Be Careful with it.
You are causing an infinite loop.
If you introduce an odd number like 5, see that you are adding two numbers at each iteration which means that the length of your Fibonacci list is never going to be odd, it will always be even. For that, you should check the number that you have to introduce and run the loop while checking that max number for your sequence and probably, breaking the loop or adding just 1 element.
As other answers explain, there is a bug in your program that means that it is an infinite loop. So what it actually does is that it builds an ever growing list of ever larger fibonacci numbers.
An infinitely large list requires an infinite amount of memory. And since the numbers themselves don't have an upper bound, that means even more memory.
But why does it crash your PC?
Well, Python doesn't have any built-in limit on the amount of memory it uses. So, a pathological Python script like your one will just keep asking the OS for more and more memory. This is detrimental to the stability of your PC. A couple of things can happen:
When the OS runs out of physical RAM, it will start allocating virtual memory. But virtual memory works by swapping virtual memory pages between physical RAM and your hard disk or SSD. And the OS typically will grab memory pages from other applications.
If there is too much swapping going, the paging rate exceeds your system's ability to keep up. Eventually some vital system service is impacted and ... the system crashes.
Some operating systems have defenses against programs that generate too much paging activity. For example, on Linux there is a kernel process called the OOM Killer which tries to identify processes that are creating excessive paging. When it finds one, it kills it.
The problem is that the OOM Killer can identify the wrong "culprit". But if the victim is a vital system service, and the OOM Killer kills it .... crash.
Fortunately, this kind of crash doesn't usually do any long term damage.
Try this:
Fibonacci = [1, 1]
...
while len(Fibonacci) != int(ChoiceNum):
Fibonacci.append(Fibonacci[-2] + Fibonacci[-1])
With this loop you will add the sum of last two numbers of your Fibonacci list to the list itself. This way your ChoiceNum can be uneven. !However!, do an extra check to see IF ChoiseNum == 1 return the first value of your Fibonacci list, else do the while loop.

Calculate how much time a method WILL take to finish? python

I know that it is not a specific question, but lets say I have a function:
def compare(firstEntry, secondEntry):
for element in firstEntry:
for element2 in secondEntry:
if(element<element2):
secondEntry.append(element)
this is just an example function but I think you get my problem. Depending on how large those two Entries are the excecution time can differ a lot. So is there anything I could do to calculate the excecution time up front and tell the user that he probalby has to wait for e.g. 10 seconds, 30 seconds, 2 minutes, ...?
I couldn't find a question like this on this platform.
I would appreciate any help. Thanks :-)
If you don't want to spend too much time with this task, here is what I would do.
First compute T, which is how much time it takes to run compare(firstEntry,secondEntry) if firstEntry and secondEntry are lists each containing one element. Let's say T = 0.1 seconds. Then I would print something like this.
T = 0.1
estimated = T * len(firstEntry) * len(secondEntry)
print("This might take %f seconds"%estimated)
I am multiplying the lengths of the lists because you have a nested loop.
First of all, don't edit one of the parameters directly, it's bad practice. Make a new variable to be returned instead, let's say finalEntry.
Assuming both lists are of equal length, this can be written to be faster, in which case you woudln't need to tell the user the execution time in advance.
def compare(firstEntry, secondEntry):
finalEntry = secondEntry
for i in range(len(firstEntry)):
if firstEntry[i] < secondEntry[i]:
finalEntry.append(firstEntry[i])

Why does a function return the error "list index out of range" in Python?

This code was written in Python 3. I am trying to find the 10001st prime number.
#10001st prime number
mylist=[]
def prime_index(n):
for i in range(99**99):
for x in range(2, int(i**1/2)):
if i % x == 0:
return False
return True
mylist.append(i)
n=int(n+1)
print(mylist[n])
break
prime_index(10001)
When I run, it says "list index out of range", referring to print(mylist[n]).
However I have been adding primes to the list along the way in the mylist.append(i). So can someone tell me what is the problem here because I do not understand what is going on. Is 99**99 too small? Or more subtle problem with code?
99**99 is not too small; if you actually print it, you're well beyond what you need (if you tried to run it out, you'd never finish, it's 657 bits of work). But your loop makes no sense; your inner loop will return either True or False immediately if it executes even once.
"Luckily" for you, it never executes even once. The first outer loop sets i to 0 the first time, so the inner loop doesn't run at all (side-note, you probably want i ** (1/2), not i ** 1 / 2; exponentiation has higher precedence than division). Because it doesn't run, you end up indexing into an empty list (and asking for index 10001 no less).
There are far too many problems in this code to address them all; look for other trial division prime finding code to get an idea of what it should look like.
The problem is that you try to print out the 10001st element as soon as you find (or not) the first prime. Also, note that you return from the routine without finding any prime number -- if you were to get that far. There is no way to reach the append statement.
You got to the print statement only because your first iteration has i = 0, so you don't enter the for loop at all.
Please follow the posting guidelines: take the time to research how to generate prime numbers. It can be much faster than you're doing, and will give you a nice, succinct bit of code to put into your program.

maximum recursion depth exceeded and how to make script more efficient

I'm creating a script using this library: https://github.com/oczkers/fut14
However, after some time, I get the error "maximum recursion depth exceeded." Afterwards I did some research and saw that I could increase this, however I would like to know a better way of avoiding this error and making my script more efficient.
def search():
items = fut.searchAuctions('player', level='gold', max_buy=250, start=0, page_size=15)
print (items)
if items == 1:
print (buy_now_price)
print (trade_id)
fut.bid(items[0]['trade_id'], 'buy_now_price')
elif items == 0:
search()
Most python installation only allow you to recurse 1000 levels (Python's self-imposed artificial limit).
A new "recursion level" happens every time your function search calls itself and involves saving some information of each function call on top of the stack. Once this stack has "grown" tall enough, you receive a "maximum recursion depth exceeded." also known as a stack overflow :).
You can modify your recursive algorithm to an iterative one instead to avoid this.
See this question here for an algorithmic way to convert from a recursive function to an iterative one. It involves using a list to simulate a stack (a list can grow much larger than Python's stack).
Recursive to iterative example
Although using the algorithmic way to convert a recursive to iterative function works, it's not the best approach since you are still using a growing list to represent your stack. Sometimes you can analyze your algorithm and find a way rewrite your algorithm without simulating any stack.
In your example, it seems that you simply want a function that will run forever until it finds something. You can rewrite it iteratively as follows
def search():
while True:
items = fut.searchAuctions('player', level='gold', max_buy=250, start=0, page_size=15
print (items)
if items == 1:
break
print (buy_now_price)
print (trade_id)
fut.bid(items[0]['trade_id'], 'buy_now_price')

Categories

Resources