**For everyday problems and programming problems. **

Every day we face all kinds of problems, and some of them are too simple, because we have solved them many times throughout our lives, so the solution becomes intuitive and does not require reasoning beyond the first time we figured it out. Besides these, there are problems that even if they are present every day or not, their solution cannot be seen clearly, and therefore, they always require our intellect to be able to address them and thus be able to reach a solution.

These problems occur in all areas of our lives and of course, it happens to app developers very often in our work. For both everyday problems and programming problems, we regularly use what we popularly call “logic”. *But what is logic? *

Logic in general terms can be defined as a series of steps that make sense in the context in which they are being used, in order to solve a problem. When there is no deep analysis of the situation, we resort to “brute force”, which is solving the problem by trying any order of steps without deeply considering the process. But even if we really get the result we expect, this makes us consume more time and effort solving a problem that could have had simpler solutions.

For this reason, this article **seeks to offer a way to address and solve problems regardless of the context in which they are, but without ruling them out**.

This time I am going to talk about my personal guide to problem-solving, which can be **applied both to everyday problems and to logical programming problems**, with a little more depth in the last topic by adding some ways of thinking to solve them.

Personally, I call it: “Logical problem-solving model”, which contains the “logical” steps to treat the solution to problems as a system, using as a basis the systems approach and the systemic metamodel (General Systems Theory). In other words, the solution will be based on proposing and building a system that solves our problem.

**Logical Problem-Solving Model**

This model helps us to u**nderstand the problem, paying attention to details such as tools, environment, and/or conditions in which it will be solved**. This means that we must consider what are the instruments that we have to solve the problem. It is likely that we won’t have the ideal tools, but the ones that we have immediately available will help us to solve the problem.

It is also necessary to identify the environment in which the solution will be developed and if this solution can be carried out in isolation or if it is integrated into other systems, depending on this, more or fewer aspects will have to be considered and also the conditions in which the solution is developed. That is, if there are factors that will allow me or not to carry out the solution that we are proposing.

**Identify the outputs that the system will have **

It is important to identify what is the expected result of the solution, and what to do in case the result we expected is not consistent with the one we obtained.

In this case, the result is used to analyze the proposed solution and thus refine it, to approach faster to the solution that provides us with results consistent with those expected.

**Identify the inputs that the system will have**

Without the exact system inputs, we will not be able to obtain the results we expect, therefore, we must identify everything that helps us to achieve the expected outputs.

Personally, I do it in this order (after identifying the necessary result), since it is easier for me to identify those inputs that are necessary. For example: *Going to the supermarket with a shopping list and then checking at home if I brought what is necessary to make a cake*. This method would result in a potential repetition of the process of buying supplies, which would not have happened if I had thought about what was first what I needed to do.

**Simplify the solution by developing steps to execute **

This step is the most complex because depending on the situation and the type of problem, we must change the way of thinking and the steps that are going to be defined. This is where the solution is actually designed and the logical steps are taken care of.

Depending on the nature of the problem, we can apply, although it is not strict, one or more of these following approaches:

**Break the big problem down into smaller problems **

It is easier to work with a small problem rather than with a set of problems at the same time. You focus the effort on solving each of these small problems that are born from the general problem, which will help to be more efficient with the solution that each of the stages will become a system in itself (with inputs, outputs, processes, and environment in which it is integrated) and will make them easier to understand and therefore to solve.

**Look for specific patterns **

We will regularly find many problems that have a specific sequence or a fairly defined and easily understood pattern. For example, the Fibonacci sequence. Some others may not be so obvious, but if anything is certain it is that in both cases the pattern can be identified in the outputs by repeating the process and analyzing the results.

Some problems even have a similar pattern of behavior in-depth, that is, if we break the general problem into smaller parts, we see that they behave in a similar way, so by solving one of them we are solving all of them at the same time. This scenario is very common in programming algorithms. A clear example of this is seen when programming the Fibonacci sequence when calculating a number N of the sequence.

**Design the flow in which things will be resolved **

It is important not to forget that sometimes the order in which tasks are performed does matter and can significantly alter the result or render the solution unusable. For this, we can rely on practices such as:

• Design a flowchart

• Design a model

• Explain the sequence of steps to another person who understands the topic

• Write the sequence of steps on a page or digital file

All of the above will help give feedback to our understanding of the problem and make it easier to identify if there is any flaw in our logic.

**Pluses for developers**

Taking into account the above, if we add it up and use it within the world of algorithms, we will find that there are also several approaches to its design, the most general groups that exist are the Greedy Algorithms, Divide and Conquer Principle, and Dynamic Programming, several of the examples to be mentioned can be developed with any of these approaches, but the implementation can be more or less complex depending on the approach that you decide to use:

**Greedy algorithms **

A greedy algorithm is one that, to solve a particular problem, follows a fairly direct method to find a solution, which consists of trying to choose the optimal option at each step. In this way, it is attempted, although not always achieved, to arrive at an optimal general solution.

They are generally very easy algorithms to implement and produce very efficient solutions (although not in all cases). Although it can be thought that this type of algorithm can be used in any situation, not all problems admit this solution strategy. In fact, the search for local optima does not always have to lead to global optima.

As a practical and classic example of a greedy algorithm, the Coin Exchange Algorithm can be considered in this case using the US currency system:

Suppose we have to give change to a person who has $6.35. In this case, the definition of the greedy algorithm to solve this problem would be: **“In each step, find the largest bill that, added to what was previously accumulated, does not exceed the total value ($6.35).”**

With which said algorithm will execute the following steps:

-Take a $5 bill

-Take a $1 coin

-Take a $0.25 coin

-Take a $0.10 coin

As can be seen, in this particular case the algorithm found the optimal solution. However, there may be cases in which it is not found.

**Divide and conquer **

The term divides and conquer refers to one of the most important algorithmic design paradigms.

The method is based on the recursive resolution of a problem by dividing it into two or more subproblems of the same or similar type. The process continues until these become simple enough to be solved directly. In the end, the solutions to each of the subproblems are combined to give a solution to the original problem.

As in induction, it is sometimes necessary to replace the original problem with a more complex one to achieve recursion, and there is no systematic method of generalization.

The name divides and conquer is also sometimes applied to algorithms that reduce each problem to a single subproblem, such as binary search to find an element in an ordered list. These algorithms can be implemented more efficiently than general divide-and-conquer algorithms; in particular, if it’s using a series of recursions that turn them into simple loops. Under this broad definition, however, every algorithm that uses recursion or loops can be thought of as a divide-and-conquer algorithm.

This technique is the basis of efficient algorithms for almost any type of problem, such as sorting algorithms (quicksort, mergesort, among many others), and multiplying large numbers (Karatsuba) among others.

**Dynamic programming **

Dynamic programming is a mathematical technique that is used to solve selected mathematical problems, in which a series of decisions are made sequentially.

It provides a systematic procedure for finding the combination of decisions that maximize overall effectiveness by breaking the problem into stages, which can be completed by one or more forms (states) and linking each stage through recursive computations.

Explaining the resolution of these problems can become too extensive, so I find it more appropriate to mention two names of problems that are used to explain dynamic programming and that are very complete:

• Knapsack problem

• Coin Exchange Algorithm (Dynamic programming way)

At this point you will have realized that the same problem can be solved in several ways, it all depends on the way we see it, the tools we have, and that “logic” will depend on the context in which it is found.

In Applaudo, there are many areas that face different types of problems on a daily basis, with different types of complexity that make each day something entertaining. In a world that is constantly changing, there must be companies with teams that constantly adapt and provide solutions quickly, and that last over time.

The different teams within Applaudo are the result of this problem-solving strategy. That is, **these teams were created to divide the work into smaller parts and each group is in charge of solving a specific part of the process**, thus transforming each team into a system, which has inputs and provides outputs that can serve other teams as input to carry out its own procedure and thus add to the final solution. Taking into account that in the global aspect we will always have the same expected output: deliver products with high value and quality for our customers and global inputs: the needs, vision, data and information that our customers provide.

Alejandro is a Frontend Developer with over 2 years of being a member of Applaudo Studios’ team. He enjoys learning on a daily basis, with a start, growth and preparation in Applaudo’s Angular Trainee Program.