Mastering Recursion: A Guide to Solving Optimization Problems

Efficient Problem-Solving with Recursion: A Case Study in Game Theory

Wendly Saintil
CodeX
5 min readDec 20, 2022

--

Photo by NisonCo PR and SEO on Unsplash

Imagine you are playing a game with a friend where you take turns picking numbers from a list. On each turn, you can choose one number from either the beginning or the end of the list, and that number will be removed from the list. The game continues until the list is empty. Your goal is to maximize the total sum of the numbers that you have chosen.

To solve this problem, we can use a technique called “recursion”. Recursion is a way of solving a problem by breaking it down into smaller and smaller pieces, until it becomes simple enough to solve directly.

Recursion is a programming technique that involves defining a function in terms of itself. It can be a powerful tool for solving optimization problems, as it allows for the creation of algorithms that break a problem down into smaller subproblems, solve those subproblems, and then combine the solutions to the subproblems to find a solution to the original problem. This process can be repeated until the problem is small enough to be easily solved. Recursion can be an efficient way to solve optimization problems, as it allows for the reuse of code and can often lead to more concise and readable solutions. However, it is important to ensure that the recursion terminates at some point, as infinite recursion can lead to runtime errors and other problems.

In this case, we can use recursion to consider both options (picking from the beginning or the end of the list) and select the one that leads to the maximum total score. To do this, we write a function called optimize_output that takes a list of numbers as input and returns the maximum total score that can be achieved by choosing numbers optimally.

The optimize_output function works by considering three different cases:

  1. If the list has only one element, we simply return that element.
  2. If the list has two elements, we return the maximum of the two.
  3. If the list has more than two elements, we need to consider both options: picking from the beginning or the end of the list. To do this, we use a recursive call to optimize_output to compute the optimal value for both options. We then return the maximum of the two.
def optimize_output(numbers):
# base case: if the list has only one element, return it
if len(numbers) == 1:
return numbers[0]

# if the list has two elements, return the maximum of the two
if len(numbers) == 2:
return max(numbers[0], numbers[1])

# if the list has more than two elements, we need to consider both
# options: picking from the beginning or the end of the list
# we will use a recursive call to compute the optimal value for both
# options and return the maximum of the two
return max(numbers[0] + min(optimize_output(numbers[2:]), optimize_output(numbers[1:-1])),
numbers[-1] + min(optimize_output(numbers[1:-1]), optimize_output(numbers[:-2])))

Using this approach, we can solve the problem by making a series of recursive calls to optimize_output, starting with the original list of numbers. The function will continue making recursive calls until it reaches one of the base cases (when the list has only one or two elements), at which point it will return the optimal value.

To make it easier to use the optimize_output function, we can write a second function called pick_number that takes a list of numbers as input and returns the number that should be picked (either from the beginning or the end of the list). The pick_number function works by computing the optimal value for both options (picking from the beginning or the end of the list) using the optimize_output function, and then returning the option that leads to the maximum total score.

def pick_number(numbers):
# base case: if the list has only one element, return it
if len(numbers) == 1:
return "BEGINNING"

# if the list has two elements, return the maximum of the two
if len(numbers) == 2:
return "BEGINNING" if numbers[0] > numbers[1] else "END"

# compute the optimal value for both options: picking from the beginning
# or the end of the list
beginning_option = numbers[0] + min(optimize_output(numbers[2:]), optimize_output(numbers[1:-1]))
end_option = numbers[-1] + min(optimize_output(numbers[1:-1]), optimize_output(numbers[:-2]))

# return the option that leads to the maximum total score
if beginning_option > end_option:
return "BEGINNING"
else:
return "END"

As an example, consider the list [5, 6, 9, 7]. If we call pick_number with this list as input, it will compute the optimal value for both options: picking the number 5 from the beginning of the list, or picking the number 7 from the end of the list.

In this case, the optimal value for the beginning option is 5 + the maximum of the optimal values for the sublist [6, 9]. Your friend will pick 7 to maximize their chances. The result equals to 5 + 9 = 14. The optimal value for the end option is 7 + the maximum of the optimal value for the sublist [5, 6]. Your friend will pick 7 to maximize their chances. The result equals to 7 + 6 = 13.

Since the optimal value for the beginning option (14) is greater than the optimal value for the end option (13), pick_number will return the number 5 (BEGINNING). This is the optimal choice, as it leads to the maximum total score (14) out of all possible choices.

One of the benefits of using this approach is that it is relatively simple to understand and implement. It is also efficient, as the recursive calls are able to re-use previously computed results, which saves time and avoids unnecessary calculations.

However, there are also some drawbacks to this approach. One potential issue is that the function can take a long time to run if the list of numbers is very large, as it needs to make many recursive calls. Additionally, this approach requires a lot of memory to store the intermediate results of the recursive calls, which can be a problem if the list of numbers is very large.

Overall, the optimize_output and pick_number functions provide a simple and effective solution to the problem of selecting numbers from a list to maximize the total score. While they have some limitations, they are useful tools for solving this type of problem.

Learn more about optimization through play with Numbrail

Numbrail is an optimization puzzle that challenges players to use strategic thinking and optimization skills to collect 61 points in a structured game environment. The game is designed to spark interest in computer science and to remind players that optimization is an essential part of everyday life. Numbrail started as a technical interview question and has evolved into a complex and challenging game.

--

--

Wendly Saintil
CodeX
Writer for

Adventist. Software Engineer @Affirm. Podcaster. Pilgrim made in #Haiti. @UF 15'. Build to #Inspire #Improve #Serve