Python algorithm: feel the small charm of the algorithm and the calculation of complexity

1. The charm of small algorithms

This is a very common small example, but it allows us to appreciate the powerful charm of the improved algorithm.

Known a+b+c = 1000, and a^2+b^2=c^2find all natural number solutions of a, b, c.
This is very simple, that is, to assign values ​​to a, b, and c through code, and then return the solution that matches a+b+c = 1000and a^2+b^2=c^2.
Here are two sections of reference code as follows:
The first section is to traverse 1~1000 and assign values ​​to a, b, and c according to the general trial-and-error logic, and then if a + b + c == 1000 and a**2 + b**2 == c**2take out the three values ​​that meet the conditions.

# 代码1:
import time
start_time = time.time()
for a in range(1,1001):
    for b in range(1,1001):
        for c in range(1,1001):
            if a + b + c == 1000 and a**2 + b**2 == c**2:
                print('a:%d, b:%d, c:%d'%(a,b,c))
end_time = time.time()
print('程序总用时:%f'%(end_time-start_time))

The execution result of code 1 is shown in the figure below, and the execution time this time is 951.8 seconds.
image.png

Let’s take a look at code 2 again. A small change has been made on the basis of code 1. It only traverses 1~1000 twice, and the third number is obtained by subtracting the first two numbers from 1000. The code is as follows:

# 代码2:
import time
start_time = time.time()
for a in range(1,1001):
    for b in range(1,1001):
        if a**2 + b**2 == (1000-a-b)**2:
            print('a:%d, b:%d, c:%d'%(a,b,1000-a-b))
end_time = time.time()
print('程序总用时:%f'%(end_time-start_time))

The execution result of code 2 is shown in the figure below, and it can be seen that it only takes 1.4 seconds to execute the code this time.
image.png

From the execution results of the above two pieces of code, we can see that the time difference between the two is about 700 times! Replace code 1 with code 2, and wait about 16 minutes less for each execution, which is a particularly good experience.

After feeling the little charm of the algorithm, let's briefly talk about the complexity of the algorithm.

2. Complexity

Complexity is generally divided into two types: time complexity and space complexity.
Time complexity mainly measures the running speed of an algorithm, while space complexity mainly measures the extra space required by an algorithm. In the early days of computer development, the storage capacity of computers was very small. So I am very concerned about the space complexity. However, with the rapid development of the computer industry, the storage capacity of computers has reached a very high level, so we no longer need to pay special attention to the space complexity of an algorithm, but pay more attention to its time complexity.

2.1 Time Complexity

Time complexity, also known as asymptotic time complexity, is officially defined as follows:
If there is a function f(n) such that when n approaches infinity, T ( n ) / f ( n ) T(n) /f(n)The limit value of T ( n ) / f ( n ) is a constant not equal to zero, then it is calledf ( n ) f(n)f ( n ) isT ( n ) T(n)Functions of the same order of magnitude as T ( n ) . WriteT ( n ) = O ( f ( n ) ) T(n) = O(f(n))T(n)=O ( f ( n ) ) , abbreviatedO ( f ( n ) ) O(f(n))O ( f ( n ) ) is the asymptotic time complexity of the algorithm, referred to as time complexity.
The asymptotic time complexity is expressed with a capital O, so it is also called the big O notation. , common representation methods such asO ( 1 ) O(1)O(1) O ( l o g n ) O(logn) O(logn) O ( n ) O(n) O(n) O ( n l o g n ) O(nlogn) O ( n l o g n )O ( n 2 ) O(n^2)O ( n2 )O ( n 3 ) O(n^3)O ( n3 )O ( 2 n ) O(2^n)O(2n )etc.
In general, the smaller the complexity, the better your code. The complexity is as follows:
O ( 1 ) < O ( logn ) < O ( n ) < O ( nlogn ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( nn ) O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O (n^3)<O(2^n)<O(n!)<O(n^n)O(1)<O(logn)<O ( n )<O(nlogn)<O ( n2)<O ( n3)<O(2n)<O ( n ! )<O ( nn)

When calculating the time complexity, theoretically, all time-consuming operations need to be taken into account. For example, traversing a list whose elements are all long strings to find equal strings requires consideration of characters in addition to the execution time of traversing the strings. String comparison (example below).

for i in ['abc','bcd','cde']:
    if i == 'cde':
        print(i)

nThe general basic code, the code without any calls to other objects is a constant item, and it will be executed several times if there are several lines ; the traversal loop is defined according to the length of the sequence n. is 2 ∗ n 2*n2n , nested traversal 2 times isn 2 n^2n2

However, in the actual application process, the time complexity is generally simplified, and more often it is just to look at the most influential factors.

2.2 Simplification of time complexity

When comparing time complexity, the following methods are usually used to simplify and compare complexity:

  • The complexity is constant, using O(1)the representation , such as O ( 23 ) O(23)O ( 2 3 )O ( 9999 ) O(9999)O(9999)
  • When the complexity includes n, omit coefficients and constant terms , such as: O ( 2 n + 45 ) ⟶ O ( n ) O(2n+45) \longrightarrow O(n)O ( 2 n+45)O ( n )
  • When the complexity is logarithmic, it is O(logn)represented by: log 5 n log_5 nlog5n l o g 2 n log_2n log2n
  • Ignore the low order and only take the high order , such as O ( 4 n 3 + 6 n 2 + n ) ⟶ O ( n 3 ) O(4n^3+6n^2+n) \longrightarrow O(n^3)O ( 4 n3+6 n2+n)O ( n3)

For example: logn + nlogn logn+nlognl o g n+n l o g n is expressed asO ( nlogn ) O(nlogn)O(nlogn)

When comparing the time complexity, the coefficients of constant terms, low-order terms, high-order terms, and the base of log are generally ignored. Say O ( 2 n + 1 ) O(2n+1)O ( 2 n+1 ) Simplify to O(n) ,O ( 2 ∗ n 3 + 5 n 2 + 5 ) O(2*n^3+5n^2+5)O(2n3+5 n2+5 ) Simplifies toO(n 3 ) O(n^3)O ( n3 )O ( log 2n ) O(log_2n)O(log2n ) simplifies toO ( logn ) O(logn)O ( log n ) . _ _ The complexity of the two strings of codes at the beginning of the article isO ( n 2 ) O(n^2)O ( n2 )sumO(n3)O(n^3)O ( n3)

2 questions:
1. Why are constant terms, low-order terms and coefficients ignored?

  • Because the big O is actually the time complexity shown when the data level breaks through a point and the data level is very large, the constant term, low-order term and coefficient of this point no longer play a decisive role.

2. Why does O(logn) not distinguish the base of logarithms?

  • Because a logarithm can be transformed into a constant multiplied by a logarithm of other bases, that is, log 2 16 = log 10 16 / log 10 2 ⟺ log 10 16 = log 10 2 ∗ log 2 16 log_2 16=log_{10} 16 /log_{10} 2 \iff log_{10} 16 = log_{10} 2 * log_2 16log216=log1016/log102log1016=log102log21 6 (It is more helpful to understand 16 as n), soO ( log 2 n ) = log 2 10 ∗ O ( log 10 n ) ⟺ O ( login ) = logij ∗ O ( logjn ) O \ left(log_2n \right)=log_2 10*O \left(log_{10}n \right) \iff O \left(log_i n \right)=log_i j*O \left(log_jn \right)O(log2n)=log210O(log10n)O(login)=logijO(logjn) O ( l o g i n )    ⟹    O ( l o g j n )    ⟹    O ( l o g n ) O \left(log_i n \right) \implies O \left(log_jn \right) \implies O \left(logn \right) O(login)O(logjn)O( l o g n ) .

2.3 Complexity of Common Functions

common functions Big-O
[n], index O(1)
append() O(1)
pop() O(n)
insert() O(n)
of the O(n)
iteration O(n)
contain() O(n)
[m,n] slice O(m)
of the slice O(n)
set slice O(n+m)
reverse O(n)
concaterate O(m)
sort O(n log n)
multiply O(n*m)
copy O(n)
get items O(1)
set items O(1)
del items O(1)
contains(n) O(1)
iteration O(n)

3. Summary

1. Generally, when comparing time complexity, the following methods are used for simplification and comparison:

  • The complexity is constant, using O(1)the representation , such as O ( 23 ) O(23)O ( 2 3 )O ( 9999 ) O(9999)O(9999)
  • When the complexity includes n, omit coefficients and constant terms , such as: O ( 2 n + 45 ) ⟶ O ( n ) O(2n+45) \longrightarrow O(n)O ( 2 n+45)O ( n )
  • When the complexity is logarithmic, it is O(logn)represented by: log 5 n log_5 nlog5n l o g 2 n log_2n log2n
  • Ignore the low order and only take the high order , such as O ( 4 n 3 + 6 n 2 + n ) ⟶ O ( n 3 ) O(4n^3+6n^2+n) \longrightarrow O(n^3)O ( 4 n3+6 n2+n)O ( n3)

2. The smaller the complexity, the better your code is. The complexity is as follows:
O ( 1 ) < O ( logn ) < O ( n ) < O ( nlogn ) < O ( n 2 ) < O ( n 3 ) < O ( 2 n ) < O ( n ! ) < O ( nn ) O(1)<O(logn)<O(n)<O(nlogn)<O(n^2)<O(n ^3)<O(2^n)<O(n!)<O(n^n)O(1)<O(logn)<O ( n )<O(nlogn)<O ( n2)<O ( n3)<O(2n)<O ( n ! )<O ( nn)

Guess you like

Origin blog.csdn.net/qq_45476428/article/details/128124552