Cookies are used for the best experience on my website.

Accept Cookie Policy

No internet detected

Check your connection and try again.

Logo Image

No match found

Buy a coffee

I launched this blog in 1995. Since then, we have published 1603 articles. It's all free and means a lot of work in my spare time. I enjoy sharing knowledge and experiences with you.

Your support

Have you learned something new by reading, listening, or watching my content? With your help, I can spend enough time to keep publishing great content in the future.

Or, select an option below:

A small slice of my data processing time each month

It's ongoing work running this site and what's really great is ongoing support. Here's a sense of what goes into this site: research topics for discussion. Manage the Tech stuff: website, SEO, graphics, email, back-end servers, DNS routing, edge servers. Create advertisements and load the campaigns in Google Ads. Manage the social media forums (Facebook, Reddit, Twitter). Write updates to the blog. Keep GitHub up-to-date.

$4.50 — A large cappuccino at my local

Things just work better with coffee! I like to take the kids to school then grab a cappuccino from my local on the way home before beginning the things that take mental energy.

$8.99 — A month of Netflix for some quiet nights in

A lot of the work on this happens after hours when I should be relaxing on the couch. Help me make it so and I promise to keep off the devices for a bit!

$11.50 — Fund a month of email delivery

This site sends out thousands of emails every month. For that volume and to ensure deliverability, I need to pay MailChimp.

$20 — Pay for one month of AWS storage fees

Websites are not free. The storage alone takes some cash. If you are willing to lighten the burden, we can keep this site up online.

$30 — One hour's pay for a graphics artist

Art doesn't create itself without a hand to guide it. I can't draw, so I need to pay others to help.

$45 — Pay a full-stack web developer for one hour

Much of the work on this site happens on weekends which means giving up time with the kids. Help me pay the developers so I can give my kids more time.

Solving Coding Problems With PEDAC

There are many ways to solve coding problems, and PEDAC ⋯

Author

Launch School


  • 3435

  • 17426

  • 2

  • 0

  • 5

This article outlines how to solve programming problems by using Launch School’s PEDAC process. There are many ways to solve coding problems, and PEDAC is but one. The purpose of this article isn’t to proclaim PEDAC as the best or only approach but provide it as one of the tools you can turn to when you begin working on a problem. You may not need PEDAC in every problem, but you’ll find a lot of advantages to employing some system when working on more sophisticated problems. The more complex the coding problem, the more you need a rigorous process like PEDAC. This approach is especially useful for beginners learning to code because many beginners have never been forced to employ a system of thinking before. It’s also a gateway concept for more formal algorithmic techniques to problem solving.

What is PEDAC? 🔗

PEDAC stands for “[Understand the] Problem, Examples / Test Cases, Data Structure, Algorithm, and Code.” PEDAC has two primary objectives: process the problem (PEDA) and **c**ode with intent (C).

PEDAC

Processing the problem consists of 4 steps that lead you from the initial problem statement to a solid understanding of what is required. The result is the Algorithm, which you will use to implement the solution.

Once you have understood the problem, chosen an appropriate data structure, and have an algorithm to approach the problem, all you need to do is convert the algorithm into the programming language of your choice. We call this final step coding with intent, and the final result is the implementation.

Why Use PEDAC? 🔗

For problems of a certain sophistication, PEDAC saves time. It may seem counter-intuitive that a laborious process like PEDAC can save time, but that’s exactly what it does when the problem is complicated. That’s not to say that going straight to coding is always slower; in fact, it is sometimes faster for simple problems. However, the more complex the problem, the more likely that going straight to coding will lead to what we call “hack and slash” coding, a derogatory term used to describe code written without intent or context. Hack and slash coding often fails to meet the requirements and handle the edge cases, and produces programs that are hard to understand, maintain, and scale. A disciplined approach, such as PEDAC, helps you identify and avoid the pitfalls you may encounter when you don’t code with intent.

Common pitfalls of hack and slash or coding without intent are:

  • Missed requirements
  • Unforeseen Edge Cases
  • Hard to understand code
  • Code that’s difficult to maintain

Example Problem 🔗

Let’s work through PEDAC using an example.

Suppose you have an arbitrary natural number (the target) and a set of one or more additional natural numbers (the factors). Write a program that computes the sum of all numbers from 1 up to the target number that are also multiples of one of the factors.
For instance, if the target is 20, and the factors are 3 and 5, that gives us the list of multiples 3, 5, 6, 9, 10, 12, 15, 18. The sum of these multiples is 78.
If no factors are given, use 3 and 5 as the default factors.

Understand the Problem 🔗

It’s tempting to jump into a REPL and type out some code real quick. Resist this temptation. The first critical step of PEDAC is to digest the problem to gain a holistic and well-rounded understanding of what the entire problem is asking. Don’t rush. Read the problem statement carefully; don’t miss any details. In most problem statements, there are no wasted words, so don’t read it like you would a magazine article. Every word and every detail matters. Your brain tends to fill in the gaps if you skip any details, but it may not do so correctly, as demonstrated by the following:

[Brain] Filling in the Gaps

Let’s first identify the inputs and outputs for our problem. Reading the problem statement, we can see that we have two inputs, and one output:

inputs:
target number
the set of factors
output:
sum of multiples

Unless you’re familiar with the problem domain, there’s also a subtle implicit concept that’s introduced in the problem: multiples.

Problem Domain

A problem domain is the area of expertise or application that needs to be examined to solve a problem. It limits the scope of the problem.

To make the requirements/terms explicit, we must understand the terms as they apply to the problem domain. These aren’t limited to words you’re unfamiliar with, but also words that have multiple meanings. For instance, “balance” can mean different things depending on whether we use it in an accounting or a supply chain context.

Going back to the term “multiples,” assume that this is a “mathematical term” that means “a number that can be divided by another number without a remainder.” (Normally, you would not want to assume something if you’re unfamiliar with the problem domain but would seek clarification first. Here, we’re the problem giver, so we’re going to confirm that this assumed definition is correct.) Using the example in the second paragraph, we can confirm this:

target number:
20
multiples of 3:
3, 6, 9, 12, 15, 18 (all have no remainder when divided by 3)
multiples of 5:
5, 10, 15 (all have no remainder when divided by 5)

Implicit Requirements 🔗

This problem statement also conveys a few rules that we must keep in mind:

  1. The multiples to be summed must be unique. The number 15 is present as a multiple of 3 and 5, but we add it just once when computing the sum(3 + 5 + 6 + 9 + 10 + 12 + 15 + 18 = 78). Note that we learn this implicitly from the example: the uniqueness requirement is not stated explicitly.
  2. The target value is the limit, but it is not considered a multiple. In the example, the target, 20, is not included in the sum even though it’s a multiple of 5. As with the first rule, this requirement is implicit.
  3. All numbers are natural numbers: they are the set of integers greater than or equal to 0 or 1 (see this definition from mathworld.wolfram.com). Since adding 0 to any number doesn’t change it, it doesn’t matter which definition we use. For simplicity, we’ll assume that the natural numbers start with 1.

Now that we’ve processed the problem, we should try to identify anything that needs clarification. For instance, here are some questions you might ask about this problem, and one set of possible answers.

Clarifying Questions 🔗

  1. What are the possible values for the target number? Are negative numbers allowed? Any natural number greater than 0. There will always be a target value.
  2. How will the factors be provided to the program? As an array.
  3. What happens if only one number is provided as a factor? Should the program assume that a second factor that is either 3 or 5 is needed? No. Default to 3 and 5 only if no factors are provided.

Do these answers agree with any assumptions you were about to make? Maybe; maybe not. That’s the point of asking questions. Ensure you’re solving the right problem by getting clarification, even if you think you understand the problem.

We now have a thorough understanding of the problem. To help round it off, though, let’s perform an optional processing step and come up with a mental model that describes it.

Wikipedia

We can think of the mental model as our summary view of the “entire problem.” In other words, it is our perspective on what the problem requires. Be sure to note that we’re not yet interested in how to solve the problem (the algorithm).

Here’s a simple mental model for this problem:

Determine a list of all multiples of a set of factors up to a target value, then filter the list of multiples to the unique values. Finally, compute and return the sum of the unique multiples.

Here’s another mental model:

Incrementally build a list of numbers that are multiples of a set of one or more factors. Add a multiple to the list only if it is not yet on the list. Finally, compute and return the sum of the numbers on the list.

Note that we came up with two mental models in this example. We did this to highlight that there are multiple perspectives to generate a sound mental model; as a general rule, you don’t need to come up with more than one model as long as it fully and accurately captures the requirements.

Examples / Test Cases 🔗

That was a lot, but it was only the first step — the “P” in PEDAC. We’re now ready to move on to the “E”, which stands for “Examples or Test Cases”. In this step, our objective is to come up with examples that validate our understanding of the problem and confirm that we are working in the right direction. The confirmation will typically come from a person or documentation of a process: we can ask the person to confirm the output given the input, or we can follow the process to check the output given the input.

Going back to the example problem, let’s come up with some examples. Our examples will be in the form of tests that show the expected outputs given certain inputs:

Validation 🔗

Example 1 🔗

Inputs:

  • Target number: 20
  • Factors: [3, 5]

Output

  • 78

Example 2 🔗

Inputs:

  • Target number: 20
  • Factors: [3]

Output

  • 63

Example 3 🔗

Inputs:

  • Target number: 20
  • Factors: [5]

Output

  • 30

Example 4 🔗

Inputs:

  • Target number: 20
  • Factors: []

Output

  • 78

Example 5 🔗

Inputs:

  • Target number: 1
  • Factors: []

Output

  • 0

Note that we derived our examples from our rules. Those are typically an excellent place to find test cases.

In addition to test cases based on our rules, we should also provide test cases that handle any edge cases we can find. Edge cases are inputs at the “edges” of the problem description that may be mishandled if we aren’t careful. For instance, problems that involve iterating over numbers have edge cases at one or both ends of the range. If you’re not careful, you may get incorrect answers at these edges. Typical edge cases can involve working with negative numbers, the number zero, or extremely high values (if performance is a requirement). When working with collections, it’s normally a good idea to find test cases that deal with zero, one or multiple elements in the collection.

This example problem has one significant edge case: what happens if the last number before the target value is a multiple of one or more of the factors?

In each of our test cases above, the last number added to the sum is either 18 or 15. That leaves 19 (the last value checked) out of the sum, which is the right thing to do. Suppose, though, that 19 should be included in the sum, which it should if 19 is one of the factors. Since 19 is the last possible number to check (given a target of 20), it’s at the “edge” of the range of values that get summed. To be certain we include 19 in the sum, we need to provide a test case that handles it.

Validation 🔗

Example 6 🔗

Inputs:

  • Target number: 20
  • Factors: [19]

Output

  • 19

Data Structure 🔗

We’re now ready to move on to the third step in the PEDAC approach, the “D”. With our test cases ready, the next thing to do is to determine what data structures we will work with to convert the input to the output. The chief considerations here are our intended programming language and our mental model.

Using either of our mental models, we need to collect the values that are multiples of our factors, and then add them up. An array seems like a good fit for this collection of multiples. The only difference between our models lies in how and when we filter those numbers, but we’ll worry about that later.

One thing to take note of is that the data structure will influence your algorithm. For this reason, we typically pair the “Data Structure” and “Algorithm” steps together.

Algorithm 🔗

Where to Start?

In this step, if you have a mental model, you can start there. Otherwise, start with the “Data Structure” step and think of how you’d build and manipulate it to get the output. For instance, if it’s an array, you’d probably focus on constructing or iterating over a collection.

Our chief objective here is to determine a series of instructions that will transform the input to the desired output. The challenge is to get the right level of detail; we want something that we can readily convert to code without actually writing code.

The reason you don’t want it written at the programming language level is that you will lose flexibility during implementation. Programming languages often provide several ways to achieve a given result, but each of those approaches can affect other parts of the program. If you make an implementation choice too soon by making it part of your algorithm, then later discover you should have chosen something else, you may need to go back and modify both the code and the algorithm. If you don’t address the changes at both levels, you may encounter the pitfalls we discussed earlier.

That said, it is not uncommon to change an algorithm once coding starts; don’t feel constrained to stick with what you’ve initially written. In fact, two individuals working on the same problem will often come up with different algorithms, especially if each individual has formulated different mental models. To demonstrate this, here are the algorithms using both mental models from our example:

First Mental Model 🔗

Determine a list of all multiples of a set of factors up to a target value, then filter the list of multiples to the unique values. Finally, compute and return the sum of the unique multiples.

  1. Create an empty array called multiples that will contain the multiples.
  2. Check whether the list of factors is empty. If there are no factors, set the list to [3, 5]
  3. For every factor in the factors list:
    1. Set the current_multiple to factor to keep track of the multiples of factor.
    2. While current_multiple < target
      1. Append the current_multiple to multiples.
      2. Add factor to current_multiple.
  4. Filter duplicate numbers from multiples.
  5. Compute and return the sum of the numbers in multiples.

Second Mental Model 🔗

Incrementally build a list of numbers that are multiples of a set of one or more factors. Add a multiple to the list only if it is not yet on the list. Finally, compute and return the sum of the numbers on the list.

  1. Create an empty array called multiples that will contain the list of multiples
  2. Check whether the list of factors is empty. If there are no factors, set the list to [3, 5]
  3. For every factor in the factors list:
    1. Set the current_multiple to factor to keep track of the multiples of factor.
    2. While current_multiple < target
      1. Is the current_multiple in multiples already?
        1. Yes - do nothing
        2. No - Append the current_multiple to multiples.
      2. Add factor to current_multiple.
  4. Compute and return the sum of the numbers in multiples.

Before implementing your algorithm, you should test it manually with your test cases. You don’t need to check all of the test cases, just enough to be confident that the algorithm works.

Let’s try it using the first example from our first mental model:

Example 1 🔗

Inputs:

  • Target number: 20
  • Number to get multiples: [3, 5]

Output

  • 78

Algorithm

  1. Create an empty array called multiples that will contain the list of multiples

    multiples = []

  2. Check whether the list of factors is empty. If there are no factors, set the list to [3, 5]

    [3, 5] obtained from supplied factors.

  3. For every factor in the factors list: [3, 5]

    1. Set the current_multiple to factor to keep track of the multiples of factor.

      current_multiple = 3
      current_multiple = 5
      
    2. While current_multiple < target

      1. Append the current_multiple to multiples.

        multiples = [3]
        multiples = [3, 6]
        multiples = [3, 6, 9]
        ...
        multiples = [3, 6, 9, 12, 15, 18, 5, 10, 15]
        
      2. Add factor to current_multiple.

        current_multiple = 6
        current_multiple = 9
         ...
        current_multiple = 18
        current_multiple = 21
        current_multiple = 5
        current_multiple = 10
        current_multiple = 15
        current_multiple = 20
        
  4. Filter duplicate numbers from multiples.

    multiples = [3, 6, 9, 12, 15, 18, 5, 10]

  5. Compute and return the sum of the numbers in multiples.

    78

After verifying that a few of our test cases gives the expected output, it’s time to put our algorithm to code.

Code 🔗

This is the last step in PEDAC — the “C”, which stands for “code with intent”. This stage is all about implementing the solution in your language of choice. The major benefit of investing time in the previous steps (the PEDA) is that it reduces the implementation to a simple translation of the algorithm into programming language syntax.

Don’t be alarmed if, after doing all the steps above, you still have to circle back to your algorithm. That can and will happen often. After all, you’re human, and you may have missed something. PEDAC, however, aims to minimize those mistakes, so you don’t miss major requirements and even if you are circling back to previous steps, it’s mostly for fine-tuning the approach.

Here’s a Ruby implementation of the algorithm we designed for the first mental model:

def sum_of_multiples(target, factors)
  multiples = []
  factors = [3, 5] if factors.length == 0

  factors.each do |factor|
    current_multiple = factor

    while current_multiple < target
      multiples << current_multiple
      current_multiple += factor
    end
  end

  multiples.uniq.inject(0, :+)
end

sum_of_multiples(20, [3, 5])  # returns 78
sum_of_multiples(20, [3])     # returns 63
sum_of_multiples(20, [5])     # returns 30
sum_of_multiples(20, [])      # returns 78
sum_of_multiples(1, [])       # returns 0
sum_of_multiples(20, [19])    # returns 19

Here’s the JavaScript implementation using the algorithm we designed for the second mental model:

function sumOfMultiples(targetNumber, factors) {
  var multiples = [];
  if (factors.length === 0) {
    factors = [3, 5];
  }

  factors.forEach(function(factor) {
    var currentMultiple;
    for (currentMultiple = factor; currentMultiple < targetNumber; currentMultiple += factor) {
      if (multiples.indexOf(currentMultiple) === -1) {
        multiples.push(currentMultiple);
      }
    }
  });

  return multiples.reduce(function(sum, value) {
    return sum + value;
  }, 0);
}

sumOfMultiples(20, [3, 5]);  // returns 78
sumOfMultiples(20, [3]);     // returns 63
sumOfMultiples(20, [5]);     // returns 30
sumOfMultiples(20, []);      // returns 78
sumOfMultiples(1, []);       // returns 0
sumOfMultiples(20, [19]);    // returns 19

Either Ruby or JavaScript would work for both mental models. However, the first model is slightly better suited for Ruby since Ruby has a method for returning unique values in an Array; JavaScript does not.

Summary 🔗

We hope that this demonstration of the PEDAC process has made a case for using it. Though it was on the verbose side of things to give each step ample coverage, this type of verbosity isn’t always needed for every problem. You also don’t need to follow the PEDA sequence strictly. The key ideas are to process the problem so that you come up with an algorithm and then code with intent. It may seem like a lot of work at first, but once you get used to employing it, you’ll be surprised at how fast and effective PEDAC is in assisting your problem solving ability.

FAQ 🔗

  1. What problems can I solve using PEDAC? PEDAC isn’t well suited for many abstract operations like “designing a UI.” It works well with procedural coding problems since it leads you to a series of steps/instructions that you can follow to produce a deterministic result.
  2. When should I use PEDAC? Should I use it even if the problem is small? If you don’t have prior experience using a formal problem-solving process then, yes, use it for trivial problems too. Get your mind used to solving problems by following PEDAC. Once you get used to it, it will become part of your “muscle memory,” and you’ll be able to deploy it more naturally on more difficult problems, which is where you will really need it. If your first exposure to PEDAC is on the most difficult problems, then it’ll be hard to get good at using this process.
  3. Can I use PEDAC for developing applications and not just working on solving a coding problem? Yes, but you’ll have some pre-work to do before using it. To effectively use PEDAC, break down the application into smaller requirements. You’ll likely have to compare against previous coding problems you’ve solved if it’s appropriately small for PEDAC. PEDAC can be applied to any problem that has specific inputs and unambiguous outputs, so you’ll have to break down your application until you have specific and unambiguous requirements.

This license allows reusers to distribute, remix, adapt, and build upon the material in any medium or format, so long as attribution is given to the creator. The license allows for commercial use. If you remix, adapt, or build upon the material, you must license the modified material under identical terms.