math, vectors

Today I stumbled across an interesting problem. The concern is that we should rotate a one-dimensional vector of nn element left by dd (Δ\Delta) positions.

Problem statement

For instance, say we have an array with n=8n=8 elements should rotate it by d=3d=3 shifts. For example, vector [1,2,3,4,5,6,7,8] is rotated to [4,5,6,7,8,1,2,3].

Vector Rotation

A naive algorithm will use an n-element intermediate vector to do the job in nn steps or copy the underlying array with language dependant slicing features (where space is O(n)O(n)) or compute in-place shift by holding a temporary variable with dd iterations (which will take a running time of O(Δn)O(\Delta * n)).

Alternative Approaches

Programming languages like python have insanely fast and well-optimized slicing operations. So, instead of iterating, a naive slicing algorithm could be faster for smaller input sizes (Eli Bendersky, 2008).

But we are better than this! can we rotate the vector in time proportional to O(n)O(n) without additional memory allocations O(1)O(1)?

Aha!

Yes, we can! Thanks to Jon Bentley, in his Programming Pearls (2nd edition) book, he mentioned three vector rotation algorithms called juggling, swapping, and reversing.

Jon derived a smart insight in which he calls the aha! moment! because, in the reverse rotation technique, it is basically two arrays reversed twice. Truly marvelous!

The reversing algorithm is pretty straightforward, and the other two are very clever. But for this code fragment, I wil only go through the reversing algorithm because that’s the most elegant code. Jon makes use of a bidirectional iterator to perform the task in o(n)o(n) time.

Bidirectional Iterator is an iterator that can be both incremented and decremented. The requirement that a bidirectional iterator can be decremented is the only thing that distinguishes bidirectional iterators from forward iterators1. In this case, we are using two iterators called ii and jj.

Now let’s dive into the algorithm! woohoo! 🥳🕺

Reverse rotate

Recall the example in the problem description. we have to left rotate the vector [1,2,3,4,5,6,7,8] by 33. If we view it as two sub-vectors, we get 33 and 55 items long sub-vector: [1,2,3]-[4,5,6,7,8].

Sub-vectors

In jon’s algorithm, to rotate the vector, we reverse the first sub-vector to obtain [3,2,1,4,5,6,7,8]. Essentially, the first reverse of the sub-vector a is swapping the positions of each element until delta.

Sub-vector A Swap

Then, reverse the second sub-vector b to obtain [3,2,1,8,7,6,5,4]. like previously, we make use of the two-pointers to swap them in-place without additional space overhead.

Sub-vector B Swap

Finally, reverse the whole thing to obtain [4,5,6,7,8,1,2,3].

Final Reverse

That’s it! how cool is that, huh?

Implementation

Here’s a golang implementation of this algorithm. The reverse function takes a pointer to the array xx and bidirectional iterator positions ii and jj for swapping the indexes. Then, we loop ij\sum_{i}^{j} until i<ji \lt j to swap xix_i and xjx_j, and consecutively we increment ii and decrement jj by one.

Then comes the real magic. Inside the reverseRotate function we invoke the reverse routine 33 times. The first call is to get AA' (we start from 00 for ii, and jj is always <Δ\lt\Delta), and then the second call is to derive BB' (we start from Δ\Delta for ii and swap until n1n-1). At this point, we only need to reverse the entire array once from 00 to n1n - 1 to obtain the rotated vector.

func reverse(x *[]int, i, j int) {
	for i < j {
		(*x)[i], (*x)[j] = (*x)[j], (*x)[i]
		i++
		j--
	}
}

func reverseRotate(x *[]int, delta int) {
	reverse(x, 0, delta-1)
	reverse(x, delta, len(*x)-1)
	reverse(x, 0, len(*x)-1)
}
This algorithm runs in guranteed O(n) time using constant space.

This is an elegant way to solve the problem, especially considering one vector as a sum of two (Prakhar Srivastav, 2014). In Aha! Algorithms Research Paper Jon mentions that this reversal code is guaranteed time and space-efficient and is so short and so damn simple that it’s pretty hard to get wrong.

It is precisely the code that Brian Kernighan & Bill Plauger use in their Software Tools book’s editor and also what Ken Thompson used in his text editor ed to manipulate lines.

Stay curious

These are some interesting resources I referred while writing this post. Be sure to check these posts from great engineers out there!

Thanks a million for reading! until next time!

Footnotes

  1. A forward iterator is an iterator that corresponds to the usual intuitive notion of a linear sequence of values (boost.org, 1999)

Well, now what?

You can navigate to more writings from here. Connect with me on LinkedIn for a chat.

  1. 2026

    1. We Might All Be AI Engineers Now
      March 05

      ai, engineering, tools, agents

    2. The Hardest Bug I Ever Fixed Wasn't in Code
      February 07

      engineering, career

    3. Why I Switched to Podman (and Why You Might Too)
      February 02

      docker, tools, linux

  2. 2024

    1. The World is Stochastic
      October 18

      career, philosophy

    2. Debugging a running Java app in Docker
      May 29

      java, docker, debugging

    3. Why is it UTC and not CUT?
      February 21

      time, history

  3. 2023

    1. Deep prop drilling in ReactJS
      December 26

      react, javascript, frontend

    2. Eigenvectors
      October 24

      math, linear-algebra

    3. Java's fork/join framework
      October 21

      java, concurrency

    4. TypeScript's omit and pick
      August 10

      typescript, frontend

    5. JavaScript's new immutable array methods
      June 28

      javascript, frontend

    6. Integrating JUnit 5 in Maven projects
      May 25

      java, testing

    7. My take on ChatGPT and prompt engineering
      March 11

      ai, prompts

    8. Declarative events in ReactJS
      March 09

      react, javascript, frontend

    9. Positive Lookaheads
      March 07

      regex, tools

    10. Functors
      March 06

      functional-programming, math

    11. Fast forward videos with ffmpeg
      January 18

      ffmpeg, tools

    12. Rotate y-axis of a 2D vector
      January 05

      math, vectors

  4. 2022

    1. Synchronizing time
      December 31

      distributed-systems, time

    2. Vector rotation
      November 20

      math, vectors

    3. Sed find and replace
      November 14

      sed, tools, linux

    4. Asgardeo try it application
      September 06

      identity, iam, asgardeo

    5. Flatten error constraints
      August 11

      java, algorithms

    6. Good Git commit messages
      July 24

      git, engineering

    7. Asgardeo JIT user provisioning
      March 09

      identity, iam, asgardeo

    8. Monotonic Arrays
      February 25

      algorithms, javascript

    9. How GOROOT and GOPATH works
      February 01

      go, tooling

  5. 2021

    1. Two summation
      November 21

      algorithms