Definition and application example of Python divide and conquer

  • 2020-06-12 09:58:54
  • OfStack

This paper introduces the definition and application of Python divide-and-conquer method. To share for your reference, specific as follows:

The problems that divide and conquer can solve generally have the following characteristics:

The problem can be easily solved by reducing it to a certain size
2) The problem can be decomposed into several similar problems of smaller scale, that is, the problem has optimal substructure property.
3) The solutions of the subproblems decomposed by the problem can be merged into the solutions of the problem;
4) Each sub-problem decomposed by this problem is independent of each other, that is, there are no common sub-problems among sub-problems.

The first feature is that most problems can be satisfied, because the computational complexity of the problem generally increases with the increase of the problem size.

The second feature is the premise of divide-and-conquer and it satisfies most problems, and it reflects the application of recursion;

The third feature is the key. The use of divide and conquer depends entirely on whether the problem has the third feature. If the first and second feature is present and the third feature is not, then greedy method or dynamic programming method can be considered.

The fourth feature relates to the efficiency of divide-and-conquer. If each subproblem is not independent, divide-and-conquer will do a lot of unnecessary work and repeatedly solve common subproblems. Although divide-and-conquer can be used, dynamic programming is generally better.

Title 1. Given a sequence table, write a divide-and-conquer algorithm to find its maximum value.


#  Basic subalgorithm (subproblem size is less than or equal to  2  When) 
def get_max(max_list):
  return max(max_list) #  Be lazy here! 
#  Divide and conquer method   version 1
def solve(init_list):
  n = len(init_list)
  if n <= 2: #  If the problem is less than or equal to  2 , the final settlement 
    return get_max(init_list)
  #  Decompose (subproblem size is  2 And the last 1 A possible for  1 ) 
  temp_list=(init_list[i:i+2] for i in range(0, n, 2))
  #  Divide and conquer, merge 
  max_list = list(map(get_max, temp_list))
  #  Recursion (tree) 
  solve(max_list)
#  Divide and conquer method   version 2
def solve2(init_list):
  n = len(init_list)
  if n <= 2: #  If the problem is less than or equal to  2 To solve 
    return get_max(init_list)
  #  Decompose (subproblem size is  n/2 ) 
  left_list, right_list = init_list[:n//2], init_list[n//2:]
  #  Recursion (tree), divide and conquer 
  left_max, right_max = solve2(left_list), solve2(right_list)
  #  merge 
  return get_max([left_max, right_max])
if __name__ == "__main__":
  #  The test data 
  test_list = [12,2,23,45,67,3,2,4,45,63,24,23]
  #  For maximum 
  print(solve(test_list)) # 67
  print(solve2(test_list)) # 67

Title 2. Given a sequence table, determine if an element is in it.


#  Subproblem algorithm (subproblem size is  1 ) 
def is_in_list(init_list, el):
  return [False, True][init_list[0] == el]
#  Divide and conquer method 
def solve(init_list, el):
  n = len(init_list)
  if n == 1: #  If the size of the problem is equal to  1 , solve directly 
    return is_in_list(init_list, el)
  #  Decompose (subproblem size is  n/2 ) 
  left_list, right_list = init_list[:n//2], init_list[n//2:]
  #  Recursion (tree), divide and conquer, merge 
  res = solve(left_list, el) or solve(right_list, el)
  return res
if __name__ == "__main__":
  #  The test data 
  test_list = [12,2,23,45,67,3,2,4,45,63,24,23]
  #  To find the 
  print(solve2(test_list, 45)) # True
  print(solve2(test_list, 5)) # False

Problem 3. Find the element of k 1 in the sequence, and find the linear time


#  Partition (based on pivot  pivot ), note: non-local division 
def partition(seq):
  pi = seq[0]              #  Select principal component 
  lo = [x for x in seq[1:] if x <= pi] #  All the little elements 
  hi = [x for x in seq[1:] if x > pi]  #  All the big elements 
  return lo, pi, hi
#  Find the first  k  Small element 
def select(seq, k):
  #  decomposition 
  lo, pi, hi = partition(seq)
  m = len(lo)
  if m == k:
    return pi        #  Solve! 
  elif m < k:
    return select(hi, k-m-1) #  Recursion (tree), divide and conquer 
  else:
    return select(lo, k)   #  Recursion (tree), divide and conquer 
if __name__ == '__main__':
  seq = [3, 4, 1, 6, 3, 7, 9, 13, 93, 0, 100, 1, 2, 2, 3, 3, 2]
  print(select(seq, 3)) #2
  print(select(seq, 5)) #2

Title 4. Quicksort


#  Partition (based on pivot  pivot ), note: non-local division 
def partition(seq):
  pi = seq[0]              #  Select principal component 
  lo = [x for x in seq[1:] if x <= pi] #  All the little elements 
  hi = [x for x in seq[1:] if x > pi]  #  All the big elements 
  return lo, pi, hi
#  Quick sort 
def quicksort(seq):
  #  If the problem is less than or equal to 1 To solve 
  if len(seq) <= 1: return seq
  #  decomposition 
  lo, pi, hi = partition(seq)
  #  Recursion (tree), divide and conquer, merge 
  return quicksort(lo) + [pi] + quicksort(hi)
seq = [7, 5, 0, 6, 3, 4, 1, 9, 8, 2]
print(quicksort(seq)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Title 5. Merge sort (2 sorted)


#  Merge sort 
def mergesort(seq):
  #  Decomposition (based on midpoint) 
  mid = len(seq) // 2
  left_seq, right_seq = seq[:mid], seq[mid:]
  #  Recursion (tree), divide and conquer 
  if len(left_seq) > 1: left_seq = mergesort(left_seq)
  if len(right_seq) > 1: right_seq = mergesort(right_seq)
  #  merge 
  res = []
  while left_seq and right_seq:     #  As long as neither is empty 
    if left_seq[-1] >= right_seq[-1]: #  Both tail larger, pop out 
      res.append(left_seq.pop())
    else:
      res.append(right_seq.pop())
  res.reverse()             #  Reverse order 
  return (left_seq or right_seq) + res  #  And then we're going to add what's left over seq
seq = [7, 5, 0, 6, 3, 4, 1, 9, 8, 2]
print(mergesort(seq)) #[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

Title 6. The Tower of Hannot


#  Hanoi 
def move(n, a, buffer, c):
  if n == 1:
    print(a,"->",c)
    #return
  else:
    #  Recursion (linear) 
    move(n-1, a, c, buffer)
    move(1, a, buffer, c) #  Or: print(a,"->",c)
    move(n-1, buffer, a, c)
move(3, "a", "b", "c")

Question 7. Take the stairs

Suppose you're climbing the stairs and need n steps to get to the top. But you can only climb one or two steps at a time. How many different ways can you climb to the top of the building?


#  Climb the stairs 
def climb(n=7):
  if n <= 2:
    return n
  return climb(n-1) + climb(n-2) #  Equivalent to the Fibonacci sequence! 
print(climb(5)) # 8
print(climb(7)) # 21

Question 8. Find one pair of n points on a given plane, so that the distance of the pair of n points is the smallest of all the pairs. (Nearest point problem)


from math import sqrt
#  A brute force method 
def solve(points):
  n = len(points)
  min_d = float("inf") #  Minimum distance: infinite 
  min_ps = None    #  Point to recent 
  for i in range(n-1):
    for j in range(i+1, n):
      d = sqrt((points[i][0] - points[j][0])**2 + (points[i][1] - points[j][1])**2) #  Two distance 
      if d < min_d:
        min_d = d            #  Modify the minimum distance 
        min_ps = [points[i], points[j]] #  Save the nearest pair 
  return min_ps
#  Nearest point right (error!) 
def nearest_dot(seq):
  #  Note: seq Prior to x Coordinate ordering 
  n = len(seq)
  if n <= 2: return seq #  If the size of the problem is equal to  2 , solve directly 
  #  Decomposition (subproblem size) n/2 ) 
  left, right = seq[0:n//2], seq[n//2:]
  print(left, right)
  mid_x = (left[-1][0] + right[0][0])/2.0
  #  Recursion, divide and conquer 
  lmin = (left, nearest_dot(left))[len(left) > 2]  #  Nearest point pair on the left 
  rmin = (right, nearest_dot(right))[len(right) > 2] #  Closest pair on the right 
  #  merge 
  dis_l = (float("inf"), get_distance(lmin))[len(lmin) > 1]
  dis_r = (float("inf"), get_distance(rmin))[len(rmin) > 1]
  d = min(dis_l, dis_r)  #  Nearest point pair distance 
  #  Dealing with banded areas near midline (approximate brute force) 
  left = list(filter(lambda p:mid_x - p[0] <= d, left))  # The distance to the left of the center line <=d The point of 
  right = list(filter(lambda p:p[0] - mid_x <= d, right)) # The distance to the right of the median line <=d The point of 
  mid_min = []
  for p in left:
    for q in right:
      if abs(p[0]-q[0])<=d and abs(p[1]-q[1]) <= d:   # If the right-hand side is at theta p Some of the (d,2d) between 
        td = get_distance((p,q))
        if td <= d:
          mid_min = [p,q]  #  record p . q Point to 
          d = td      #  Modify the minimum distance 
  if mid_min:
    return mid_min
  elif dis_l>dis_r:
    return rmin
  else:
    return lmin
#  Two distance 
def get_distance(min):
  return sqrt((min[0][0]-min[1][0])**2 + (min[0][1]-min[1][1])**2)
def divide_conquer(seq):
  seq.sort(key=lambda x:x[0])
  res = nearest_dot(seq)
  return res
#  test 
seq=[(0,1),(3,2),(4,3),(5,1),(1,2),(2,1),(6,2),(7,2),(8,3),(4,5),(9,0),(6,4)]
print(solve(seq)) # [(6, 2), (7, 2)]
#print(divide_conquer(seq)) # [(6, 2), (7, 2)]

Question 9. How many possible combinations of values are found from the array seq and for s


'''
 o 1 An algorithm is: N Number. Use one of them M The sum of any combination is equal to 1 A known number X . It is concluded that the M What are the Numbers. 
 Such as: 
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 #  and 
 All possible combinations of Numbers are: 
5+9, 6+8
1+4+9, 1+5+8, 1+6+7, 2+3+9, 2+4+8, 2+5+7, 3+4+7, 3+5+6
1+2+5+6, 1+3+4+6, 1+2+4+7, 1+2+3+8, 2+3+4+5
 A total of 15 Kind of 
'''
#  version 1 (Pure count) 
def find(seq, s):
  n = len(seq)
  if n==1:
    return [0, 1][seq[0]==s]
  if seq[0]==s:
    return 1 + find(seq[1:], s)
  else:
    return find(seq[1:], s-seq[0]) + find(seq[1:], s)
#  test 
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 #  and 
print(find(seq, s)) # 15
seq = [11,23,6,31,8,9,15,20,24,14]
s = 40 #  and 
print(find(seq, s)) #8
#  version 2  (print) 
def find2(seq, s, tmp=''):
  if len(seq)==0:  #  Termination conditions 
    return
  if seq[0] == s:        #  find 1 Kind of, 
    print(tmp + str(seq[0])) #  print 
  find2(seq[1:], s, tmp)               #  Tail recursion  --- Do not contain  seq[0]  In the case 
  find2(seq[1:], s-seq[0], str(seq[0]) + '+' + tmp)  #  Tail recursion  --- containing  seq[0]  In the case 
#  test 
seq = [1, 2, 3, 4, 5, 6, 7, 8, 9]
s = 14 #  and 
find2(seq, s)
print()
seq = [11,23,6,31,8,9,15,20,24,14]
s = 40 #  and 
find2(seq, s)

For more information about Python, you can see the following topics: Python Data Structure and Algorithm Tutorial, Python Socket Programming Skills Summary, Python Function Use Skills Summary, Python String Manipulation Skills Summary, Python Introduction and Advanced Classic Tutorial and Python File and Directory Operation Skills Summary.

I hope this article has been helpful in Python programming.


Related articles: