简体   繁体   中英

Why is while loop much faster than range in this case?

According to this post , range loop should be faster than while loop in python, but please have a look at the following code. It is simply used to test if a number is prime and return the divisor if n is not a prime.

import time

def isPrimeWhile(n):
    i = 2
    while(i <= n/2+1):
        if(n%i == 0):
            return i
        i += 1
    return n

def isPrimeRange(n):
    for i in range(2,n/2+1):
        if(n%i == 0):
            return i
    return n

n = 353591872901

start = time.time()
isPrimeWhile(n)
print("%s while seconds"%(time.time() - start))

start = time.time()
isPrimeRange(n)
print("%s range seconds"%(time.time() - start))

Run the code and you will find the while loop is much faster than the range loop.I am guessing that the range(0,aLargeNumber) takes a very long time to run. But why in the last post mentioned above, the range method is much quicker? Any answers?

Since you are using Python 2+ ( your code needs to use integer division to work in Python 3+ ) you are running into the fact that Python 2+ range generates a list of all elements and then iterates over them.

This would explain the differences in time that it takes for the while and range functions to run.

Incedentally the code for Python 3+ needs the following change:

def isPrimeRange(n):
    for i in range(2,n//2+1): # integer division
        if(n%i == 0):
            return i
    return n 

This Python Blog Post explains in great detail the differences between range (returns a list) and xrange (returns an iterator) in Python 2+ and how Python 3+ has changed this functionality.

A pick of the most relevent paragraph from that source is here:

When you're using an iterator, every loop of the for statement produces the next number on the fly. Whereas the original range() function produced all numbers instantaneously, before the for loop started executing. The problem with the original range() function was that it used a very large amount of memory when producing a lot of numbers. However it tends to be quicker with a small amount of numbers. Note that in Python 3.x, you can still produce a list by passing the generator returned to the list() function.

As mentioned by others, the main issue appears to be using range instead of range in Python 2.7. Using %timeit , both functions are almost the same if one were to use xrange (Python 2.7).

%timeit isPrimeRange(n)
100000 loops, best of 3: 15.2 µs per loop

%timeit isPrimeWhile(n)
100000 loops, best of 3: 15.8 µs per loop

%load_ext line_profiler
%load_ext memory_profiler

from isPrimeRange import isPrimeRange
from isPrimeWhile import isPrimeWhile

n = 353591872901

isPrimeRange: Line Profiler

%lprun -f isPrimeRange isPrimeRange(n)
Timer unit: 1e-06 s

**Total time: 5.5e-05 s**
File: isPrimeRange.py
Function: isPrimeRange at line 1

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     1                                           def isPrimeRange(n):
     2        82           25      0.3     45.5      for i in xrange(2,n/2+1):
     3        82           29      0.4     52.7          if(n%i == 0):
     4         1            1      1.0      1.8              return i
     5                                               return n

isPrimeWhile: Line Profiler

%lprun -f isPrimeWhile isPrimeWhile(n)
Timer unit: 1e-06 s

**Total time: 9e-05 s**
File: isPrimeWhile.py
Function: isPrimeWhile at line 3

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
     3                                           def isPrimeWhile(n):
     4         1            1      1.0      1.1      i = 2
     5        82           34      0.4     37.8      while(i <= n/2+1):
     6        82           31      0.4     34.4          if(n%i == 0):
     7         1            0      0.0      0.0              return i
     8        81           24      0.3     26.7          i += 1
     9                                               return n

Using iPython's memory profiler, you can see that both use the same amount of memory.

isPrimeRange: Memory Profiler

%mprun -f isPrimeRange isPrimeRange(n)
('',)
Filename: isPrimeRange.py

Line #    Mem usage    Increment   Line Contents
================================================
     1     24.2 MiB      0.0 MiB   def isPrimeRange(n):
     2     24.2 MiB      0.0 MiB       for i in xrange(2,n/2+1):
     3     24.2 MiB      0.0 MiB           if(n%i == 0):
     4     24.2 MiB      0.0 MiB               return i
     5                                 return n

isPrimeWhile: Memory Profiler

%mprun -f isPrimeWhile isPrimeWhile(n)
('',)
Filename: isPrimeWhile.py

Line #    Mem usage    Increment   Line Contents
================================================
     3     24.2 MiB      0.0 MiB   def isPrimeWhile(n):
     4     24.2 MiB      0.0 MiB       i = 2
     5     24.2 MiB      0.0 MiB       while(i <= n/2+1):
     6     24.2 MiB      0.0 MiB           if(n%i == 0):
     7     24.2 MiB      0.0 MiB               return i
     8     24.2 MiB      0.0 MiB           i += 1
     9                                 return n

you might as well try using

import time

def isPrimeWhile(n):
    i = 2
    while(i <= n/2+1):
        if(n%i == 0):
            return i
        i += 1
    return n

def isPrimeRange(n):
    for i in xrange(2,n/2+1):
        if(n%i == 0):
            return i
    return n

n = 353591872901

start = time.time()
isPrimeWhile(n)
print("%s while seconds"%(time.time() - start))

start = time.time()
isPrimeRange(n)
print("%s while seconds"%(time.time() - start))

1.28746032715e-05 while seconds

6.91413879395e-06 while seconds

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM