Fastest algorithm for circle shift N sized array for M position

Viewed 35127

What is the fastest algorithm for circle shifting array for M positions?
For example, [3 4 5 2 3 1 4] shift M = 2 positions should be [1 4 3 4 5 2 3].

Thanks a lot.

26 Answers

If you want O(n) time and no extra memory usage (since array was specified), use the algorithm from Jon Bentley's book, "Programming Pearls 2nd Edition". It swaps all the elements twice. Not as fast as using linked lists but uses less memory and is conceptually simple.

shiftArray( theArray, M ):
    size = len( theArray )
    assert( size > M )
    reverseArray( theArray, 0, size - 1 )
    reverseArray( theArray, 0, M - 1 )
    reverseArray( theArray, M, size - 1 )

reverseArray( anArray, startIndex, endIndex ) reverses the order of elements from startIndex to endIndex, inclusive.

It's just a matter of representation. Keep the current index as an integer variable and when traversing the array use modulo operator to know when to wrap around. Shifting is then only changing the value of the current index, wrapping it around the size of the array. This is of course O(1).

For example:

int index = 0;
Array a = new Array[SIZE];

get_next_element() {
    index = (index + 1) % SIZE; 
    return a[index];
}

shift(int how_many) {
    index = (index+how_many) % SIZE;
}

Set it up with pointers, and it takes almost no time. Each element points to the next, and the "last" (there is no last; after all, you said it was circular) points to the first. One pointer to the "start" (first element), and maybe a length, and you have your array. Now, to do your shift, you just walk your start pointer along the circle.

Ask for a good algorithm, and you get sensible ideas. Ask for fastest, and you get weird ideas!

A very simple solution. This is a very fast way, here I use a temp array with the same size or original and attach to the original variable at the end. This method use O(n) temporal complexity and O(n) space complexity and it is very simple to implement.

int[] a  = {1,2,3,4,5,6};
    int k = 2;
    int[] queries = {2,3};

    int[] temp = new int[a.length];
    for (int i = 0; i<a.length; i++)
        temp[(i+k)%a.length] = a[i];

    a = temp;

Depending on the data structure you use, you can do it in O(1). I think the fastest way is to hold the array in the form of a linked list, and have a hash table that can translate between "index" in the array to "pointer" to the entry. This way you can find the relevant heads and tails in O(1), and do the reconnection in O(1) (and update the hash table after the switch in O(1)). This of course would be a very "messy" solution, but if all you're interested in is the speed of the shift, that will do (on the expense of longer insertion and lookup in the array, but it will still remain O(1))

If you have the data in a pure array, I don't think you can avoid O(n).

Coding-wise, it depends on what language you are using.

In Python for example, you could "slice" it (assume n is the shift size):

result = original[-n:]+original[:-n]

(I know that hash lookup is in theory not O(1) but we're practical here and not theoretical, at least I hope so...)

A friend of mine while joking asked me how to shift an array, I came up with this solutions (see ideone link), now I've seen yours, someone seems a bit esoteric.

Take a look here.

#include <iostream>

#include <assert.h>

#include <cstring>

using namespace std;

struct VeryElaboratedDataType
{
    int a;
    int b;
};

namespace amsoft
{
    namespace inutils
    {
        enum EShiftDirection
        {
            Left,
            Right
        };
template 
<typename T,size_t len>
void infernalShift(T infernalArray[],int positions,EShiftDirection direction = EShiftDirection::Right)
{
    //assert the dudes
    assert(len > 0 && "what dude?");
    assert(positions >= 0 && "what dude?");

    if(positions > 0)
    {
    ++positions;
    //let's make it fit the range
    positions %= len;

    //if y want to live as a forcio, i'l get y change direction by force
    if(!direction)
    {
        positions = len - positions;
    }

    // here I prepare a fine block of raw memory... allocate once per thread
    static unsigned char WORK_BUFFER[len * sizeof(T)];
    // std::memset (WORK_BUFFER,0,len * sizeof(T));
    // clean or not clean?, well
    // Hamlet is a prince, a prince does not clean

    //copy the first chunk of data to the 0 position
    std::memcpy(WORK_BUFFER,reinterpret_cast<unsigned char *>(infernalArray) + (positions)*sizeof(T),(len - positions)*sizeof(T));
    //copy the second chunk of data to the len - positions position
    std::memcpy(WORK_BUFFER+(len - positions)*sizeof(T),reinterpret_cast<unsigned char *>(infernalArray),positions * sizeof(T));

    //now bulk copy back to original one
    std::memcpy(reinterpret_cast<unsigned char *>(infernalArray),WORK_BUFFER,len * sizeof(T));

    }

}
template 
<typename T>
void printArray(T infernalArrayPrintable[],int len)
{
        for(int i=0;i<len;i++)
    {
        std::cout << infernalArrayPrintable[i] << " ";
    }
    std::cout << std::endl;

}
template 
<>
void printArray(VeryElaboratedDataType infernalArrayPrintable[],int len)
{
        for(int i=0;i<len;i++)
    {
        std::cout << infernalArrayPrintable[i].a << "," << infernalArrayPrintable[i].b << " ";
    }
    std::cout << std::endl;

}
}
}




int main() {
    // your code goes here
    int myInfernalArray[] = {1,2,3,4,5,6,7,8,9};

    VeryElaboratedDataType myInfernalArrayV[] = {{1,1},{2,2},{3,3},{4,4},{5,5},{6,6},{7,7},{8,8},{9,9}};
    amsoft::inutils::printArray(myInfernalArray,sizeof(myInfernalArray)/sizeof(int));
    amsoft::inutils::infernalShift<int,sizeof(myInfernalArray)/sizeof(int)>(myInfernalArray,4);
    amsoft::inutils::printArray(myInfernalArray,sizeof(myInfernalArray)/sizeof(int));
    amsoft::inutils::infernalShift<int,sizeof(myInfernalArray)/sizeof(int)>(myInfernalArray,4,amsoft::inutils::EShiftDirection::Left);
    amsoft::inutils::printArray(myInfernalArray,sizeof(myInfernalArray)/sizeof(int));
    amsoft::inutils::infernalShift<int,sizeof(myInfernalArray)/sizeof(int)>(myInfernalArray,10);
    amsoft::inutils::printArray(myInfernalArray,sizeof(myInfernalArray)/sizeof(int));


    amsoft::inutils::printArray(myInfernalArrayV,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType));
    amsoft::inutils::infernalShift<VeryElaboratedDataType,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType)>(myInfernalArrayV,4);
    amsoft::inutils::printArray(myInfernalArrayV,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType));
    amsoft::inutils::infernalShift<VeryElaboratedDataType,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType)>(myInfernalArrayV,4,amsoft::inutils::EShiftDirection::Left);
    amsoft::inutils::printArray(myInfernalArrayV,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType));
    amsoft::inutils::infernalShift<VeryElaboratedDataType,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType)>(myInfernalArrayV,10);
    amsoft::inutils::printArray(myInfernalArrayV,sizeof(myInfernalArrayV)/sizeof(VeryElaboratedDataType));

    return 0;
}

Similar to @IsaacTurner and not that elegant due to unnecessary copying, but implementation is quite short.

The idea - swap element A on index 0 with the element B which sits on destination of A. Now B is first. Swap it with the element C which sits on destination of B. Continue until the destination is not at 0.

If the greatest common divisor is not 1 then you're not finished yet - you need to continue swapping, but now using index 1 at your starting and end point.

Continue until your starting position is not the gcd.

int gcd(int a, int b) => b == 0 ? a : gcd(b, a % b);

public int[] solution(int[] A, int K)
{
    for (var i = 0; i < gcd(A.Length, K); i++)
    {
        for (var j = i; j < A.Length - 1; j++)
        {
            var destIndex = ((j-i) * K + K + i) % A.Length;
            if (destIndex == i) break;
            var destValue = A[destIndex];
            A[destIndex] = A[i];
            A[i] = destValue;
        }
    }

    return A;
}

Here is my solution in Java which got me 100% Task Score and 100% Correctness at Codility:

class Solution {
    public int[] solution(int[] A, int K) {
        // write your code in Java SE 8
        if (A.length > 0)
        {
            int[] arr = new int[A.length];
            if (K > A.length)
                K = K % A.length;

            for (int i=0; i<A.length-K; i++)
                arr[i+K] = A[i];

            for (int j=A.length-K; j<A.length; j++)
                arr[j-(A.length-K)] = A[j];

            return arr;
        }
        else
            return new int[0];
    }
}

Note that despite seeing two for loops, the iteration on the entire array is only done once.

Swift 4 version for shifting array left.

func rotLeft(a: [Int], d: Int) -> [Int] {

   var result = a
   func reverse(start: Int, end: Int) {
      var start = start
      var end = end
      while start < end {
         result.swapAt(start, end)
         start += 1
         end -= 1
      }
   }

   let lenght = a.count
   reverse(start: 0, end: lenght - 1)
   reverse(start: lenght - d, end: lenght - 1)
   reverse(start: 0, end: lenght - d - 1)
   return result
}

For example, if input array is a = [1, 2, 3, 4, 5], and left shift offset is d = 4, then result will be [5, 1, 2, 3, 4]

@IsaacTurner 's answer (C) https://stackoverflow.com/a/32698823/4386969

and @SomeStrangeUser 's answer (Java): https://stackoverflow.com/a/18154984/4386969

provide a simple O(N) time, O(1) space algorithm that answers the question and requires exactly N element assignments. I believe though (and someone correct me if I'm wrong) that computing the gcd between N and M is not necessary; simply counting the number of elements that we have put in their correct place should suffice. This is because once we have put an element in its correct place, we are guaranteed that we won't have to access it again neither in the current cycle nor in subsequent ones.

Here is a Python 3 implementation with this additional simplification:

# circle shift an array to the left by M 
def arrayCircleLeftShift(a, M):
    N = len(a)
    numAccessed = 0
    cycleIdx = 0
    while numAccessed != N:
        idx = cycleIdx
        swapIdx = (idx + M) % N
        tmp = a[idx]
        while swapIdx != cycleIdx:
            a[idx] = a[swapIdx]
            numAccessed += 1
            idx = swapIdx
            swapIdx = (idx + M) % N
        a[idx] = tmp
        numAccessed += 1
        cycleIdx += 1

I know it is an old post, however here is an optimal solution in O(n): each element is moved exactly once and no extra space is needed. It is very similar to the solution proposed by Isaac Turner but no requires gcd computation.

public static void shiftArray(int[] A, int k) {
    if (A.length == 0) {
        return;
    }
    k = k % A.length;
    k = (k + A.length) % A.length; // ensure k is positive
    if (k == 0) {
        return;
    }
    int i = 0, i0 = 0;
    int x = A[0];
    for (int u = 0; u < A.length; u++) { // count number of shifted elements
        int j = (i - k + A.length) % A.length; // ensure modulo is positive
        if (j == i0) { // end of a (sub-)cycle, advance to next one
            A[i] = x;
            x = A[i = ++i0];
        } else {
            A[i] = A[j];
            i = j;
        }
    }
}

Here is the c++ implementation. Time Complexity O(n), Space Complexity O(1)

M = M % theArray.size();
reverse(theArray.begin(), theArray.end());
reverse(nums.begin(), theArray.begin()+ M);
reverse(theArray.begin()+ M, theArray.end());

Here is another implementation using Cyclic Replacements technique. Time Complexity O(n), Space Complexity O(1).

void rotate(vector<int>& nums, int k) {
    int n = nums.size();
    int start = 0;
    int count = 0;
    while(count < n) {
        int temp = nums[start];
        int i = (start + k) % n;
        while(i != start) {
            int temp2 = nums[i];
            nums[i] = temp;
            temp = temp2;
            i = (i + k) % n;
            count++;
        }
        nums[i] = temp;
        count++;
        start++;
    }
    
}

Keep two indexes to the array, one index starts from beginning of the array to the end of array. Another index starts from the Mth position from last and loops through the last M elements any number of times. Takes O(n) at all times. No extra space required.

circleArray(Elements,M){
 int size=size-of(Elements);

 //first index
 int i1=0;

 assert(size>M)

 //second index starting from mth position from the last
 int i2=size-M;

 //until first index reaches the end
 while(i1<size-1){

  //swap the elements of the array pointed by both indexes
  swap(i1,i2,Elements);

  //increment first pointer by 1
  i1++;

  //increment second pointer. if it goes out of array, come back to
  //mth position from the last
  if(++i2==size) i2=size-M;

 }
}
Related