2014-01-29

Repeating strings efficiently

Recently, Michael Ficarra pointed me to an efficient algorithm for repeating a string several times. In this blog post, I explain how it works.

The algorithm makes use of the fact that natural numbers (non-negative integers) can be represented as sums of powers of two.

Natural numbers as sums of powers of two

You can express any natural number as a sum of powers of two. In fact, that’s what a number in binary notation is. The value of a sequence of n binary digits di
dn-1dn-2 ... d0
is the sum
0 ≤ i < n di × 2i = d0 × 1 + d1 × 2 + d2 × 4 + ...
Each digit di is either one or zero. Therefore, each addend is either a power of two or it is zero.

The algorithm

Roughly, the algorithm iterates over the digits starting with d0. After each iteration, it doubles str, the string to repeat. If the current digit is one then the current value of str is added to the result. In other words, if the digit di is one, 2i times str is added to the result. The algorithm, implemented in JavaScript and slightly edited, looks as follows.
    function stringRepeat(str, num) {
        num = Number(num);

        var result = '';
        while (true) {
            if (num & 1) { // (1)
                result += str;
            }
            num >>>= 1; // (2)
            if (num <= 0) break;
            str += str;
        }

        return result;
    }
In line 1, the bitwise And operator (&) [1] is used to extract the value of the digit at the current position. In line 2, the bitshift operator (>>>) [2] is used to shift the next digit to the least significant (rightmost) position.

Where a naive algorithm takes num steps to produce a result, this algorithm takes log2(num) steps. As an aside, concatenating strings via the plus operator (+) has become fast on modern JavaScript engines [3].

ECMAScript 6’s String.prototype.repeat()

ECMAScript 6 will have a string method String.prototype.repeat() that produces the same results as the function stringRepeat. Mathias Bynens has written a spec-compliant polyfill that uses an algorithm similar to stringRepeat’s.

References

  1. Binary bitwise operators in JavaScript
  2. Integers and shift operators in JavaScript
  3. String concatenation in JavaScript

No comments: