- Published on
- Sun Jan 26, 2014
EPI 6.13 - Rotating an array
Problem: ¶
Given an Array of n elements, design an algorithm for rotating an Array right by i positions. Only O(1) additional storage is allowed.
Solution: ¶
The natural solution is to start from position the value at index k to k + i, repeatedly n times. This will work well if GCD(n,i) is != 1. However a general solution is to perform m jumps of size i, l times, but each time starting from the next index.
The first helper function is to perform m jumps of “size” each starting from a given index, wrapping around if necessary. For example if A = [1,2,3,4,5,6,7,8,9,10],
1m_rotations A 0 3 3
would cause the following jumps: 1 -> 4 -> 7
, with A resulting in:
[1, 2, 3, 1, 5, 6, 4, 8, 9, 10]
1m_rotations xs index size m = elems (m_rotations' (arrayFromList xs 0) index (xs!!index) size m)
2 where
3 len = length xs
4 m_rotations' arr curr_index curr_value size numleft
5 | curr_index < 0 || size <= 0 || numleft <= 0 = arr
6 | otherwise = m_rotations' (arr // [(next_index, curr_value)]) next_index next_value size (numleft - 1)
7 where
8 next_index = mod (curr_index + size) len
9 next_value = arr!next_index
Now we create the actual rotator method that calls m_rotations k times, where k = gcd(|A|, j)
. This is:
1rotate_array xs j = rotate_array' xs 0
2 where
3 lxs = length xs
4 j' = mod j lxs
5 gcd_lxs_j = greatest_common_divisor lxs j'
6 numtimes = div lxs gcd_lxs_j
7 rotate_array' xs start_index
8 | start_index >= gcd_lxs_j = xs
9 | otherwise = m_rotations ys (j' - (start_index + 1)) j' numtimes
10 where
11 ys = rotate_array' xs (start_index + 1)
A simpler algorithm to perform this is very similar to reversing words in a sentence:
1rotate_array_simple xs j = reverse (take j rxs) ++ reverse (drop j rxs)
2 where rxs = reverse x