Try Tuts+ Premium, Get Cash Back!
Understanding the Principles of Algorithm Design

Understanding the Principles of Algorithm Design

Tutorial Details
  • Difficulty: Intermediate
  • Completion Time: 30 Minutes

This article will dive into the principles of algorithm design. If you haven’t a clue what I’m referring to, read on!

When you hear the word “algorithm,” you probably respond in one of three ways:

  1. You immediately know and understand what we’re talking about because you studied computer science.
  2. You know that algorithms are the workhorses of companies like Google and Facebook, but you aren’t really sure what the word means.
  3. You run and hide in fear because everything you know about algorithms reminds you of high-school Calculus nightmares.

If you are one of the second two, this article is for you.


What is an Algorithm, Exactly?

Algorithms are not a special type of operation, necessarily. They are conceptual, a set of steps that you take in code to reach a specific goal.

Algorithms have been commonly defined in simple terms as “instructions for completing a task”. They’ve also been called “recipes”. In The Social Network, an algorithm is what Zuckerberg needed to make Facemash work. If you saw the movie, you probably remember seeing what looked like a scribbly equation on a window in Mark’s dorm room. But what does that scribbly algebra have to do with Mark’s simple “hot or not” site?

Algorithms are indeed instructions. Perhaps a more accurate description would be that algorithms are patterns for completing a task in an efficient way. Zuckerberg’s Facemash was a voting site to determine someone’s attractiveness relative to a whole group of people, but the user would only be given options between two people. Mark Zuckerberg needed an algorithm that decided which people to match up to one another, and how to value a vote relative to that person’s previous history and previous contenders. This required more intuition than simply counting votes for each person.

For instance, let’s say you wanted to create an algorithm for adding 1 to any negative number, and subtracting 1 from any positive number, and doing nothing to 0. You might do something like this (in JavaScript-esque pseudo code):

function addOrSubtractOne(number){
    if (number < 0) {
        return number + 1
    } else if (number < 0) {
        return number - 1
    } else if (number == 0) {
        return 0;
    }
}

You may be saying to yourself, “That’s a function.” And you’re right. Algorithms are not a special type of operation, necessarily. They are conceptual – a set of steps that you take in code to reach a specific goal.

So why are they a big deal? Clearly, adding or subtracting 1 to a number is a fairly simple thing to do.

But let’s talk for a second about searching. To search for a number in an array of numbers, how would you think to do it? A naive approach would be to iterate the number, checking each number against the one you’re searching for. But this isn’t an efficient solution, and has a very wide range of possible completion times, making it an erratic and unreliable search method when scaled to large search sets.

function naiveSearch(needle, haystack){
    for (var i = 0; i < haystack.length; i++){
        if (haystack[i] == needle) { return needle; }
    }
    return false;
}

Fortunately, we can do better than this for search.

Why is it Inefficient?

There is no better way to become a better algorithm designer than to have a deep understanding and appreciation for algorithms.

Let’s say your array has 50,000 entries, and you brute-force search (that is, search by iterating the full array). The entry you are searching for, in the best case scenario, will be the first entry in the 50,000-entry array. In the worst case scenario, however, the algorithm will take 50,000 times longer to complete than in the best case scenario.

So what’s Better?

Instead, you would search using binary search. This involves sorting the array (which I will let you learn about on your own) and subsequently dividing the array in half, and checking to see if the search number is greater or less than the halfway mark in the array. If it is greater than the halfway mark of a sorted array, then we know that the first half can be discarded, as the searched number isn’t a part of the array. We can also cut out a lot of work by defining the outer bounds of the array and checking to see if the searched number exists outside of those bounds, and if so, we have taken what would have been a multiple-iteration operation and turned it into a single iteration operation (which in the brute-force algorithm would have taken 50,000 operations).

sortedHaystack = recursiveSort(haystack);
function bSearch(needle, sortedHaystack, firstIteration){
    if (firstIteration){
        if (needle > sortedHaystack.last || needle < sortedHaystack.first){
            return false;
        }
    }
    if (haystack.length == 2){
        if (needle == haystack[0]) {
            return haystack[0];
            } else {
            return haystack[1];
            }
    }
    if (needle < haystack[haystack.length/2]){
        bSearch(needle, haystack[0..haystack.length/2 -1], false);
    } else {
        bSearch(needle, haystack[haystack.length/2..haystack.length], false);
    }
}

Sounds Fairly Complicated

Take the seemingly complicated nature of a single binary search algorithm, and apply it to billions of possible links (as searching through Google). Beyond that, let’s apply some sort of ranking system to those linked searches to give an order of response pages. Better yet, apply some kind of seemingly randomized “suggestion” system based on artificial intelligence social models designed to identify who you might want to add as a friend.

This gives us a much clearer understanding of why algorithms are more than just a fancy name for functions. At their finest, they are clever, efficient ways of doing something that requires a higher level of intuition than the most apparent solution. They can take what might would require a supercomputer years to do and turn it into a task that finishes in seconds on a mobile phone.

How do Algorithms Apply to Me?

For most of us as developers, we aren’t designing high-level abstracted algorithms on a daily basis.

Luckily, we stand on the shoulders of the developers who came before us, who wrote native sort functions and allow us to search strings for substrings with indexOf in an efficient manner.

But we DO, however, deal with algorithms of our own. We create for loops and write functions every day; so how can good algorithm design principles inform the writing of these functions?

Know Your Input

One of the main principles of algorithmic design is to, if possible, build your algorithm in such a way that the input itself does some of the work for you. For instance, if you know that your input is always going to be numbers, you do not need to have exceptions/checks for strings, or coerce your values into numbers. If you know that your DOM element is the same every time in a for loop in JavaScript, you shouldn’t be querying for that element in every iteration. On the same token, in your for loops, you shouldn’t use convenience functions with overhead if you can accomplish the same thing using (closer to) simple operations.

// don't do this:
for (var i = 1000; i > 0; i--){
    $("#foo").append("<span>bar</span>");
}

// do this instead
var foo = $("#foo");
var s = "";
for(var i = 1000; i > 0; i--){
    s += "<span>bar</span>";
}
foo.append(s);

If you are a JavaScript developer (and you use jQuery) and you don’t know what the above functions are doing and how they are significantly different, the next point is for you.

Understand Your Tools

At their finest, [algorithms] are clever, efficient ways of doing something that requires a higher level of intuition than the most apparent solution.

It is easy to think that this goes without saying. However, there is a difference between “knowing how to write jQuery” and “understanding jQuery”. Understanding your tools means that you understand what each line of code does, both immediately (the return value of a function or the effect of a method) and implicitly (how much overhead is associated with running a library function, or which is the most efficient method for concatenating a string). To write great algorithms, it is important to know the performance of lower-level functions or utilities, not just the name and implementation of them.

Understand the Environment

Designing efficient algorithms is a full-engagement undertaking. Beyond understanding your tools as a standalone piece, you must also understand the way that they interact with the larger system at hand. For instance, to understand JavaScript in a specific application entirely, it is important to understand the DOM and performance of JavaScript in cross browser scenarios, how available memory affects rendering speeds, the structure of servers (and their responses) you may be interacting with, as well as a myriad of other considerations that are intangible, such as usage scenarios.


Reducing the Workload

In general, the goal of algorithm design is to complete a job in fewer steps. (There are some exceptions, such as Bcrypt hashing.) When you write your code, take into consideration all of the simple operations the computer is taking to reach the goal. Here is a simple checklist to get started on a path to more efficient algorithm design:

  • Use language features to reduce operations (variable caching, chaining, etc).
  • Reduce iterative loop nesting as much as possible.
  • Define variables outside of loops when possible.
  • Use automatic loop indexing (if available) instead of manual indexing.
  • Use clever reduction techniques, such as recursive divide and conquer and query optimization, to minimize the size of recursive processes.

Study Advanced Techniques

There is no better way to become a better algorithm designer than to have a deep understanding and appreciation for algorithms.


Conclusion

If you didn’t know what an algorithm was at the start of this article, hopefully, now, you have a more concrete understanding of the somewhat elusive term. As professional developers, it is important that we understand that the code we write can be analyzed and optimized, and it is important that we take the time to do this analysis of the performance of our code.

Any fun algorithm practice problems you’ve found? Perhaps a dynamic programming “knapsack problem”, or “drunken walk”? Or maybe you know of some best practices of recursion in Ruby that differ from the same functions implemented in Python. Share them in the comments!

Note: Want to add some source code? Type <pre><code> before it and </code></pre> after it. Find out more
  • Brian

    Looks like there are some special character issues in the article. Makes some of the code a little difficult to decipher…

    • http://tierrepeterson.com/blog TrP

      I just thought he was trying to test our entity knowledge. It does break up your reading.

  • http://www.johnbelisle.com John

    This is not easy to follow because the special characters are not showing up as… well… special characters.

  • https://twitter.com/to_VishalS Vishal

    Jonathan – Nice article, gives good insight, looks like some decoding\escape characters used had not been encoding back by compiler properly “<, >, &lt;”.

  • David

    Really enjoyed this article! Definitely would like to see more on algorithms, especially with examples in PHP or Javascript

  • A_O_

    Very bad example. While searching after the sort is much faster, the sort itself would take at least twice as much than a simple loop…

    • XiaoP

      If you search for 1 item in the 50000, yes, you are faster with a linear search.
      But search for a couple more items and it starts getting interesting.

      Complexity of a binary search is O(log2 n), about 16 in this case.
      Complexity of sort is O(n.log2 n) comparisons, i.e. about 800000 comparisons.
      So after 16 searches on average you are already faster.

    • marcbraulio

      Yes, that may be true if you are conducting just a single operation on the array (which is what the author is doing in this case). However, if you intend to conduct multiple operations, instance checking for multiple numbers, it is far faster to sort the array first and run the find algorithm as needed than to iterate through the entire array every time.

      Depending on the amount of entries I am expecting to deal with, I would take it a step further and write a similar algorithm to insert new entries in the right order at the point of entry and that way I wouldn’t need to ever sort the array.

    • Anonymous Coward

      You’re right, but you usually sort only once and then search tons of times. Whenever you perform an access by key on a map or by index on a sparse array you perform a search, in fact.

      You also didn’t do your math. Efficient sorts are of O(n log(n)) complexity, an iteration is always O(n). Therefore, if your array isn’t typically of just a handfull of elements, sorting is always less than double the computational effort.

      • Andrew

        You seem to misunderstand time complexity analysis — O(n log n) always grows faster than O(n) by a factor of log n. At sufficiently large n (say, the 50,000 entries in the article’s example), log n very likely dominates two. At any rate, log n dominates any constant factor; O(1) O(n) < O(n log n).

      • Andrew

        You seem to misunderstand time complexity analysis — O(n log n) always grows faster than O(n) by a factor of log n. At sufficiently large n (say, the 50,000 entries in the article’s example), log n very likely dominates two. At any rate, log n dominates any constant factor; O(1) < O(log n) => O(n) < O(n log n).

      • Eric B.

        > Efficient sorts are of O(n log(n)) complexity, an iteration is always O(n). Therefore, if your array isn’t typically of just a handfull of elements, sorting is always less than double the computational effort.

        Eh, not exactly. If we assume the logarithm is in base 2 (though for big O it doesn’t really matter) then log(n) > 2 for all n > 4, meaning it will take more than double the time, asymptotically.

        Asymptotically speaking, iterating k times is going to be O(kn), so the better algorithm depends on how many times you’re going to search. If k is constant with respect to n, then doing linear scans is only going to be O(n). On the other hand, sorting and binary searching will be O(n*log(n)) + k*O(log(n)) = O(n*log(n)), if the number of searches is constant then the linear scans are better, asymptotically (This may vary in different implementations)

        If the growth of k is more than constant though, say if k ~= log(n) then linear scans would be O(n*log(n)) while sorting then binary search would be O(n*log(n)) + O(log(n)log(n)) = O(n*log(n)), so they’re about the same (again, this will vary depending on your implementation). As the number of searches increases to say n (so linear = O(n^2) and binary = O(n*log(n))) to even faster-growing functions, sorting once and binary searching has the theoretical advantage.

  • Vinay

    I was afraid of the term ‘Algorithm’. You really help me get out of this.

    Thanks Jonathan. You make my day :)

  • http://whiteboard.is Jonathan Cutrell

    Hey folks – sorry about the encoding issues. We’ll get those fixed!

    • http://www.wdonline.com/ Jeremy McPeak

      They be fixed!

  • ian

    Thanks Jonathan

    I enjoy these type of articles. They help to expand my checklist of things to consider when coding. In my case I understand algorithms but another persons thoughts are always helpful to increase the understanding.

    I don’t hear the term algorithm much since college when it was often used in discussing analysis and design, and pseudo code. These are two things I tend to want to run from but they can make all the difference in the efficiency of an algorithm.

  • Fernando

    I really appreciate this article. For those that want to to learn more about algorithms, take a look at
    https://www.coursera.org/course/algs4partI

    Please fix the < symbols. I know nettuts readers will get it but it makes the examples unnecessary more difficult to understand.

  • Stephan

    great article, helped me alot to get a basic understanding !

  • Jose Gomez

    Nice article, please fix the symbols, and, i got working the binary search by doing this…

    var haystack = [1,2,3,5,6,7];

    function bSearch(needle, haystack, firstIteration){
    if (firstIteration){
    if (needle > sortedHaystack.last || needle < sortedHaystack.first){
    return false;
    }
    }
    if (haystack.length == 2){
    if (needle == haystack[0] || needle == haystack[1]) {
    return true;
    } else{
    return false;
    }
    }
    if (needle < haystack[haystack.length/2]){
    return bSearch(needle, haystack.slice(0, (haystack.length/2) -1), false);
    } else {
    return bSearch(needle, haystack.slice(haystack.length/2, haystack.length), false);
    }
    }

  • http://petersr.com Peter Rasmussen

    Great read!

    Another good source of algorithm problems is Project Euler: http://projecteuler.net/

  • http://www.daquanwright.com Daquan Wright

    I’m a comp sci major myself as well as a ui designer/app developer. Algorithms are basically the building blocks of any modern program, it’s similar to a recipe for a dish.

    You have ingredients to cook the food (components/tokens/keywords/identifiers/variables, etc.) and then you have the steps in which to apply them (and how). A cook or chef (programmer) pieces it together in a similar fashion.

    You can use log from mathematics to greatly decrease search times.

    A really simple example of an algorithm is this:

    Sit down in chair
    Turn on computer
    check e-mail
    check recent blog entries
    Turn off computer
    Get up from chair
    Go do something else

    That’s assuming all you wanted to do was check e-mail and blog entries. ^_^ This is such a fun topic and I need to explore it more.

  • http://codeconquest.com Charles from CodeConquest.com

    Great explanation. Is the algorithm that search engines use to order results, or CSS’s specificity algorithm, are they the same kind of algorithm?

  • http://www.paulozemek.com Paulo Zemek

    In your first example, you wrote:
    if (number < 0) and if (number < 0)
    The second time it should be if (number > 0)

  • Dilip Ramierz

    You have a typo in your first javascript-esque code:

    function addOrSubtractOne(number){
    if (number < 0) {
    return number + 1
    } else if (number < 0) {

    should be

    function addOrSubtractOne(number){
    if (number 0) {

  • http://www.yannbane.com/ Yannbane

    There is a bug in your first code snippet, I’m afraid.

    if (number < 0)
    {
    return number + 1
    }
    else if (number < 0)

    You are checking if the number is smaller then zero, and if it is not, you are then checking it again. Also, I'll just add that I don't like your style of writing parentheses so coupled together with other statements.

  • http://www.opwernby.com Dan Sutton

    So, you mean, there are actually programmers out there who don’t know what “algorithm” means? I find that astounding — the algorithm is everything: how can one be a programmer without understanding that? No wonder I can’t find anyone decent to hire these days. I’m genuinely amazed…

    …so how do programmers write stuff these days, then, if they don’t know this? It’d like trying to learn a foreign language without learning any of the grammatical rules or idioms or whatever…

    • Anonymous

      Your response is unduly condescending and insulting to a lot of good programmers that haven’t had the exact same set of experiences you’ve had because—in fact—they aren’t you. It isn’t astounding that people can write good code without fully understanding the definition of the term “algorithm”, but it *is* astounding that you’re just learning that now. No wonder you can’t find anyone decent who wants to work with you.

      Your analogy of learning to program being like learning a foreign language is flawed in the assumption that one *must* know the terms labeling the structures in order to use them well. Rosetta Stone successfully teaches people foreign languages without such labeling.

      • http://www.opwernby.com Dan Sutton

        Rosetta Stone teaches people to mimic phrases in a language such that one can recall given phrases at will when in a foreign country. This is a far cry from being able to use a language properly and understand the subtleties of its grammar and nuance.

        The same is true of programmers who fail to understand algorithms: their code looks like a collection of preset ideas, forced to conform to whatever problem they’re trying to solve.

        My response is not condescending at all: it’s true. If you lack the frame of reference to see this, then rather than attacking me for my response, instead examine your own efficacy and knowledge.

      • http://www.opwernby.com Dan Sutton

        Actually, rather than argue, I’ll prove it to you: write me an algorithm to produce a 20-element array with the numbers 1 to 20 in it, in random order. Then I’ll tell you why it’s inefficient.

      • Anonymous

        <?php
        $a = range(1, 20);
        shuffle($a);
        ?>

    • http://www.opwernby.com Dan Sutton

      No. I mean really write it. Don’t use “shuffle” and things.

      • Anonymous

        If the problem you presented were a real-world problem, then I’ve solved it accurately and in a manner that was efficient in several ways.

        1. It reused the tools provided with the language, following the DRY principle.
        2. It took less than a minute to write, allowing me to move on and solve other problems.
        3. It’s easy to read and understand what’s going on, improving communication between team members and even myself at a later time.

        Now, if this practical solution to its supposedly real-world problem proved to be a noticeable performance bottleneck, then it might warrant the extra time and complexity that your custom algorithm would take. However, to go to such an extent before its performance its shown to be a real problem… well, that would be premature optimization. And, as you undoubtedly know from studying Donald Knuth, premature optimization is the root of all evil.

        I strongly recommend that any programmer learn more about algorithms. And this article has done a good job starting them on their way. However, thinking that it’s an requirement for one to know what an algorithm is in order to be a “programmer”, or to be even more than a “decent” programmer, is patently false.

        (Note: The words above are quoted because those are the ones you used in your original comment. Had you used “computer scientist”, “software engineer”, “exceptional”, or “outstanding”, then the expectations for such an individual would’ve been reasonably different… but your comment would still have been inappropriate given the target audience of the article.)

      • http://www.opwernby.com Dan Sutton

        As you know very well, you completely sidestepped the intent of the thing.

        It’s all very well to use existing classes and/or functions to do these things, but what about when you come up against a problem for which there are no existing classes or functions?

        I’m well aware that languages have functions for this: the idea was not to use them: I picked this example because it’s something simple enough to be done within the context of a page like this, which doesn’t require hundreds of lines of code to be written.

        So in reality, what you’ve done is (a) fail to engage me on the point I was trying to make, (b) make that my fault, and (c) reiterate what you’ve already said, which is wrong.

  • Hercules van Tonder

    Rooted as I am in C/C++ and other strongly-typed languages, I expect ‘JavaScript-esque pseudo code’
    to pay at least minimal attention to consistent typing. In the snippet function

    function naiveSearch(needle, haystack){
    for (var i = 0; i < haystack.length; i++){
    if (haystack[i] == needle) { return needle; }
    }
    return false;
    }

    false is highlighted as a reserved lexeme, presumably indicating a boolean type constant value. A
    well-behaved function should not return a boolean and some other data type – unless needle
    has the same type as false and haystack is an array of that type; very odd indeed.

    However, there is a more serious problem: if your 'JavaScript-esque pseudo code' compiler or
    interpreter is happy with this sloppiness, then the code certainly contains an even worse error,
    this time logical. The algorithm returns parameter needle without affecting it in any way.
    Whether the parameter is a reference or value type, the caller will receive no new information
    from this return – certainly not what the algortihm intends. Returning haystack[i] and/or
    i would be useful.

    Pedantically yours …

  • Paul

    Can we all please stop with the indignation about the fact there may be programmers that do not know what algorithms are. Not everybody who works in the industry or programs for personal enjoyment has been through a formal education in Computer Science so presumably one way of being introduced to them is through blog posts like this. One final point on that just because person has not been introduced to algorithms does not stop a person from being good at what they do it may prevent them from doing certain programming jobs.

    Anyway if you do not know what algorithms are I would highly recommend starting of with something like “Algorithms in a Nutshell” rather than going straight into “The Art of Computer Programming” MIX is a little bit scary to start with especially if you have not been through a formal education. Personally I am also finding “A Beginner’s Guide to Discrete Mathematics” (978-8181282156) helpful and not scary, a major criteria of mine. I would also recommend looking at the lectures on “Theory of Computation”, which is a helpful foundation for really understanding algorithms, form this http://www.aduni.org, considering the subject the I thought the subject matter comes across well.

  • Anonymous Coward

    IMO thee article misses something. IMO it makes little sense to talk about algorithms in isolation from data structures.

  • http://www.opwernby.com Dan Sutton

    In terms of what to read, I’d also include A Discipline Of Programming by Edsger W. Dijkstra, whose name should always be included when talking about algorithmic programming.

  • Andrew

    The binary search algorithm can be significantly simplified if you generalize the recursion so that you don’t need a firstIteration flag or the special case for a two-item haystack. From Wikipedia:

    function binary_search(needle, haystack, imin, imax) {
    if (typeof(imin) === ‘undefined’) imin = 0;
    if (typeof(imax) === ‘undefined’) imax = haystack.length – 1;

    // check if array is empty
    if (imax < imin) return false;

    imid = imin + Math.floor((imax – imin) / 2);

    // three-way comparison
    if (haystack[imid] > needle)
    return binary_search(needle, haystack, imin, imid – 1);
    else if (haystack[imid] < needle)
    return binary_search(needle, haystack, imid + 1, imax);
    else
    return imid;
    }​

  • http://shit.circulates.on.internet Anonymous Coward 2

    Why do people have to mention their recruitment frustrations as comments to some good article.

    read and move on you id$ot.

    Really good article btw.

  • Captain Obvious

    “There is no better way to become a better algorithm designer than to have a deep understanding and appreciation for algorithms.”

    How very insightful. Let’s replace ‘algorithm’ with ‘carpentry’.
    “There is no better way to become a better carpenter than to have a deep understanding and appreciation for carpentry.”

    Or let’s try ‘philosophy’.
    “There is no better way to become a better philosopher than to have a deep understanding and appreciation for philosophy.”

    Ahhh Hot Damn I love a good tautology in the morning!

  • http://techonamission.com Timothy Kaemmerer

    MIT has a good class on algorithms on Open Courseware: http://ocw.mit.edu/courses/electrical-engineering-and-computer-science/6-046j-introduction-to-algorithms-sma-5503-fall-2005/

    The first lesson describes sorting methods in algorithms like you were talking about. It’s also much more approachable than “The Art of Computer Programming” (though I love the book, I’ve got a lot of concept learning to do before I can pretend to really understand it).

  • Babu

    Dear Friends ,
    I want learn more about Dynamic programming I don’t know where to get more resources please suggest me if it available in any where in Internet .

    Best Regards
    Babu

  • http://alexconcepts.com alex

    I think the following phrase is bad advice for newbies (article’s target audience).

    ‘For instance, if you know that your input is always going to be numbers, you do not need to have exceptions/checks for strings, or coerce your values into numbers.’

    Even if you’re absolutely 101% sure about the input at the time of writing the code, your code might end up being used in a context you haven’t thought of in the first place. This is why people should be advised towards defensive programming: http://en.wikipedia.org/wiki/Defensive_programming

  • http://twitter.com/firatcali darth

    There is no universally accepted definition of algorithm and certainly can not be explained in one page tutorial.