HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Algorithm to rotate array elements in Ruby

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
elementsarrayalgorithmrubyrotate

Problem

I wrote this algorithm to rotate elements in an array. It's not very efficient, but it works. It has another disadvantage that it doesn't rotate right (it would be nice to pass it negative steps, for example).

def rotate_left!(array, steps = 1)
  for i in 0...steps
    first = array[0]
    for j in 1...array.size
      array[j - 1] = array[j]
    end
    array[-1] = first
  end
end


An aside, does this count as \$O(n^2)\$? For each step, we have to iterate over the entire array and shift positions by -1.

Let \$A\$ be the size of the array, \$S\$ be the steps, and \$C\$ be the constant operations of storing the first element then adding it to the end of the array.

$$
T(A, S) = S(A) + S(2C)
$$

This notation is ad-hoc, so excuse me if it doesn't make sense.

I am aware of array#rotate, and of array#shift and array#pop. For example:

def my_rotate!(array, steps = 1)
  for i in 1..steps
    array.push(array.shift)
  end
end
# or 
array.push(array.shift(steps)).flatten!


But I'm curious if there are other ways that rely less on Array instance methods.

Solution

Your algorithm is \$O(N*M)\$ where:

\$N\$ = size of array

\$M\$ = steps

It's possible to do an array rotation that's \$O(N)\$, regardless of the number of steps you specify though.

Start by thinking of the array as two pieces, separated at "steps" indices into the array. For example, if you're going to rotate by 4, then mentally separate it into array[1..5] and array[5..N].

To do the rotation, start by reversing each of those pieces. Then reverse the entire array, something on this order:

for j in 1...steps/2
    temp = array[j]
    array[j] = array[steps-j+1]
    array[steps-j+1] = temp
end

for k in 1...(array.size-steps)/2
    temp = array[steps+k]
    array[steps+k] = array[array.size-k+1]
    array[array.size-k+1]=temp
end

for i in 1...array.size
    temp = array[i]
    array[i] = array[array.size-i+1]
    array[array.size-i+1] = temp
end


Note: I'm more accustomed to C-style arrays, where indexes start at 0 instead of 1. I've tried to compensate appropriately, but I haven't tested this, so I wouldn't be surprised if there were still a few off-by-one errors.

This steps through the entire array twice, regardless of the size of rotation, so it's likely to be slower than yours for steps=1, about the same speed for steps = 2, and faster for steps > 2.

Code Snippets

for j in 1...steps/2
    temp = array[j]
    array[j] = array[steps-j+1]
    array[steps-j+1] = temp
end

for k in 1...(array.size-steps)/2
    temp = array[steps+k]
    array[steps+k] = array[array.size-k+1]
    array[array.size-k+1]=temp
end

for i in 1...array.size
    temp = array[i]
    array[i] = array[array.size-i+1]
    array[array.size-i+1] = temp
end

Context

StackExchange Code Review Q#125221, answer score: 4

Revisions (0)

No revisions yet.