Move all odd positioned element to left half and even positioned to right half in-place
Asked Answered
V

6

11

Given an array with positive and negative integers, move all the odd indexed elements to the left and even indexed elements to the right.

The difficult part of the problem is to do it in-place while maintaining the order.

e.g.

7, 5, 6, 3, 8, 4, 2, 1

The output should be:

5, 3, 4, 1, 7, 6, 8, 2

If the order didn't matter, we could have been used partition() algorithm of quick sort.

How to do it in O( N )?

Viewy answered 9/9, 2012 at 11:24 Comment(6)
O(n) space+time is trivial using two lists. I assume you are looking for O(1) space?Pongid
@amit, yes. In-place means O(1) space.Viewy
This paper describes O(N) in-place algorithm: arxiv.org/abs/0805.1598Manila
Why did the 7 move to the right? Should odd numbers on the left remain on the left? What do you intend for the output order? This question is not at all clear.Hughs
@AdrianMcCarthy: the question is not about odd/even numbers, but about odd/even-positioned elements. 7 is at even position, so it goes to the right. The problem of rearranging odd/even numbers is discussed in the comments.Manila
@AdrianMcCarthy, the title clearly says: Move all odd positioned element to left half and even positioned to right half in-place. What is not clear in this title?Viewy
M
7
  1. Get largest sub-array having size 3k+1
  2. Apply cycle leader algorithm to the parts of this sub-array, starting from positions 1, 3, 9, ... 3k-1: move element to its proper position in sub-array (even-indexed elements to the left of sub-array, odd-indexed - to the right), the replaced element should be also moved to its proper position, etc. until this procedure comes back to the starting position. This paper gives number-theoretic explanation why such selection of starting positions shuffles sub-array to the correct order.
  3. Process the remaining parts of the array recursively, using steps 1 and 2.
  4. Now we only need to join the reordered parts together. Start with the smaller sub-arrays at the end of the whole array. To exchange sub-array halves, use reverse algorithm: reverse(reverse(a), reverse(b)); or, for sub-array halves of equal size, use pair-wise swaps.
  5. Now all even-positioned elements are on the left. To get them on the right, as required, exchange elements i and i+N/2 for all i = 0 .. N/2-1.

Algorithm is in-place, time complexity is O(N).

Example:

0 1 2 3 4  5 6 7 8 9   10 11 (the original array)
0 1 2 3 4  5 6 7 8 9 # 10 11 (split to sub-arrays)
0 2 4 3 8  1 6 5 7 9 # 10 11 (first cycle leader iteration, starting with 1)
0 2 4 6 8  1 3 5 7 9 # 10 11 (second cycle leader iteration, starting with 3)
0 2 4 6 8  9 7 5 3 1 # 10 11(2nd half of 1st part& 1st half of 2nd part reversed)
0 2 4 6 8 10 1 3 5 7    9 11 (both halves reversed together)

Variation of this algorithm, that does not need step 5:

  • On step 1, get largest sub-array having size 3k-1.
  • On step 2, move even-indexed elements to the right of sub-array, odd-indexed - to the left. Use starting positions 0, 2, 8, ... 3k-1-1 for cycle-leader algorithm.

Here is different O(N log N) in-place algorithm, which does not need number-theoretic proofs:

  1. Reinterpret your array as a sequence of single-element 2*2 matrices, transpose these matrices.
  2. Reinterpret the result as a sequence of two-element 2*2 matrices and transpose them.
  3. Continue while matrices size is less than the array size.
  4. Now we only need to join the reordered parts together (exactly as in previous algorithm).
  5. Exchange elements of left and right halves of the array (exactly as in previous algorithm).

Example:

0  1   2 3   4 5   6 7  (the original array)
[0 2] [1 3] [4 6] [5 7] (first transposition)
[0 2] [4 6] [1 3] [5 7] (second transposition)

This problem is just a special case of the In-place matrix transposition.

Manila answered 9/9, 2012 at 13:22 Comment(40)
Can you please explain how 0 1 2 3 4 5 6 7 transforms to 0 2 4 3 1 5 6 7 after the first cycle leader iteration?Wilkie
@Cupidvogel: 1 goes to its proper place and replaces 4. Then 4 replaces 2. Then 2 goes to the vacant place, previously occupied by 1. All other elements remain in their places.Manila
I just noted that the original array needs the transformations: 0->4,4->6,6->7,7->3,3->1,1->0 and '2->5, 5->2'. As per the paper you suggested, any new permutation is a disjoint set of such cyclic leader rotations. Where does your steps 2-4 fit in? And secondly, is there any guarantee that these rotations won't go for a fairly long time (here it is only 6 and 2 for the 2 sets) so that it no longer remains linear?Wilkie
@Cupidvogel: algorithm should move even-positioned elements to the left (which means we need additional block-exchange at the end to satisfy requirements of this question). Also algorithm works only for sizes 3^k - 1, that's why steps 3..4 are needed. Rotations won't go for a fairly long time because each element is moved exactly once and we stop as soon as we get back to starting position.Manila
2 questions. I just noted that even for arrays whose length is not of the form 3^k-1, only step 1 suffices. For example, in the length 10 array '4,7,2,9,8,5,1,3,6,5, to convert it into the array 7 9 5 3 5 4 2 8 1 6, only 1 cycle is required: 0->5,5->2,2->6,6->8,8->9,9->4,4->7,7->3,3->1 & 1->0, as summarized by i -> L/2 + i/2` when i%2 == 0, else i -> i/2. Steps 3-4 are not required here. And for the given array in question, two sets of disjoint cycles are required. So when does only 1 cycle suffice, and when more than 1 is needed? And if more than 1 is needed, how to know that?Wilkie
@Cupidvogel: For length not of the form 3^k-1 there is no way to know how many cycles we have and where are these cycles, no mathematical proof exists for this case (or at least I don't know one). If you know array length in-advance, you can pre-calculate number of cycles and all starting points, and use them in a fixed-length algorithm.Manila
Oh, so you are saying that the need for only one cycle is my 10-element array is a coincidence, it is in general unpredictable? But why 2 cycles are needed for the given array even though it's length is 8 (3^2-1)?Wilkie
@Cupidvogel: Yes, it's just coincidence. As for 3^k-1 lengths, there are always k cycles; see proof in the paper.Manila
Right. But how to find the K-cycles?Wilkie
But the 2 cycles start from 0 and 2 for the given question. One cycle runs as 0->4->6->7->3->1->0 and the other as 2->5->2.Wilkie
@Cupidvogel: algorithm from the paper is designed to move even-positioned elements to the first half of the array. I've already answered this 3 hours ago.Manila
So what modifications need to be done to the algo so that the reverse can also be done, like moving even positioned elements to the right half, as per the question here?Wilkie
@Cupidvogel: after all sub-arrays are transposed and joined (after steps 1..4 are completed), we just exchange elements i and i+N/2 for all i = 0 .. N/2-1Manila
Okay. Can you tell me whether this kind of algo applies to some other situations too, like there is an array where even and odd numbers are interspersed. We have to shuffle it so that even numbers go to even indexes and odd numbers to odd indexes. Initially the numbers are present in no definite order, even indexes may contain both even and odd numbers, same for odd indexes. Can we do it in-place in O(N) time with O(1) space?Wilkie
This algorithm can only rearrange elements from even positions to 1st half of the array or backwards. It completely ignores element values. If you need to rearrange odd/even numbers to odd/even indexes, this algorithm may be used as part of the solution. Actually similar question you asked yesterday, and every answer I gave to it may be used to solve this, in particular, the last one, with in-place radix sort.Manila
How can in-place radix sort put even numbers in even indexes and odd numbers in odd-places?Wilkie
@Cupidvogel: If key is a single least significant bit, it can put even numbers to the first half of the array, and (reversed) algorithm from this question can rearrange them to even indexes.Manila
How can radix sort put even numbers in the first half of the array?Wilkie
Didn't understand If key is a single least significant bit, it can put even numbers to the first half of the array. Can you please provide some pseudo-code? I have never imagined radix-sort doing anything other than sorting, so I can't really envision it.Wilkie
@Cupidvogel: Here's the example: 75638421; after extracting key bits: 11010001; sort them with stable radix sort or any other stable algorithm: 00001111; while sorting, "value" part of each element is moved together with key: 68427531.Manila
But again, this is possible only when the number of elements is of the form 3^k-1, right? And secondly, can you elaborate a bit more in reversing the left-out subarray, combining it with the 2nd half of the main array, etc?Wilkie
let us continue this discussion in chatManila
@ Evgeny Kluev, What if the array size is of the form 3^k - 1? Suppose the array is given by 0 1 2 3 4 5 6 7. After first cycle leader iteration, starting with 1, it becomes 0 2 4 3 1 5 6 7 . AFter second cycle leader iteration, starting with 3, it becomes 0 2 4 6 1 5 3 7. What to do next? There is no split sub-arrays to reverse. The answer should be 0 2 4 6 1 3 5 7Viewy
@Aashish: second cycle leader iteration moves 3 to the place of 5 (because its proper position is 5), 5 replaces 6, and 6 comes to unoccupied place of 3. As a result we have 02461357. We'll never have 0 2 4 6 1 5 3 7.Manila
@ Evgeny Kluev, thanks I got it. Can you please check your algorithm for the below input? It doesn't seem to be working. 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15Viewy
@Aashish: On the contrary, this is very simple input for this algorithm. Here you have 2 sub-arrays, both of size 8. How to transpose arrays of this size shows the example in my answer. The result is 0 2 4 6 1 3 5 7 8 10 12 14 9 11 13 15. Now you exchange 2 middle blocks of 4 elements each (this may be done with simple pairwise element exchange), and finally exchange 1st and 2nd halves of the array to get odd elements first.Manila
@ Evgeny Kluev, Can my input be solved by the first approach? As said, i am interested in O( N ) algorithm? Thanks.Viewy
@Aashish: Yes. My previous comment is about the first approach. Blocks of size 8 are exactly the largest sub-arrays having size 3^k-1.Manila
@ Evgeny Kluev,After first cycle leader iteration, starting with 1, 0 2 4 3 8 5 6 7 1 9 10 11 12 13 14 15 After second cycle leader iteration, starting with 3, 0 2 4 6 8 5 12 7 1 3 10 11 9 13 14 15 Now, after reversing second half of first part and first half of second part 0 2 4 6 7 12 5 8 11 10 3 1 9 13 14 15. Now, reversing both parts together: 0 2 4 6 1 3 10 11 8 5 12 7 9 13 14 15. I don't know where i have mistaken because the final result i got is wrong.Viewy
@Aashish: please re-read step 1 of the algorithm. You cannot apply cycle leader algorithm to array of any size. You have to split it to sub-arrays of size 3^k-1, transpose each of them, then merge.Manila
let us continue this discussion in chatViewy
@ Evgeny Kluev, I have observed that the problem arises with size 24, i.e. it is failing for all the cases where size of array is greater than 22. i.e. starting from array, 0 1 2....... 23, it fails for all the cases. So, the algorithm works only for arrays where size <= 22? How to make it work for array of large size also?Viewy
@Aashish: I've just found a bug in the algorithm. After fixing it, array of size 26 is transposed perfectly. Now I'm thinking how to fix the answer.Manila
@Aashish: bug was in formula for determining sub-array sizes, now fixed. Thanks for finding it.Manila
@Aashish: also pay attention to step 3 of the algorithm. You have to split sub-arrays of size 3^k+1 recursively. There may be more than two such sub-arrays. So your implementation is not correct.Manila
@EvgenyKluev Deducing of k is not O(1) but is O(log(N)), isn't it?Kurzawa
@Orient: It depends. If you have any means of computing natural or binary logarithm in O(1) time then deducing of k or computing base-3 logarithm may be also done in O(1) time. Otherwise you could do it in O(log log N) time and space using binary exponentiation. Or O(log log N) time and O(1) space using bitwise tricks.Manila
Not clear what to do with reverse (4.) if we have, say, at least 3 parts? E.g. array of length 16 = 10 + 4 + 2.Kurzawa
The subarrays legths is 10 + 4 + 2. On the second step the length of the right side is six (4 + 2)? Further should we reverse subarrays of five (10/2) and three (4/2 + 2/2) sizes and then both together?Kurzawa
That's right. And if I remember correctly, once again, you've implemented it exactly like this (only with rotate instead of reverse).Manila
K
1

I tried to implement as Evgeny Kluev said, and here is the result:

#pragma once

#include <iterator>
#include <algorithm>
#include <type_traits>
#include <limits>
#include <deque>
#include <utility>

#include <cassert>

template< typename Iterator >
struct perfect_shuffle_permutation
{

    static_assert(std::is_same< typename std::iterator_traits< Iterator >::iterator_category, std::random_access_iterator_tag >::value,
                  "!");

    using difference_type = typename std::iterator_traits< Iterator >::difference_type;
    using value_type = typename std::iterator_traits< Iterator >::value_type;

    perfect_shuffle_permutation()
    {
        for (difference_type power3_ = 1; power3_ < std::numeric_limits< difference_type >::max() / 3; power3_ *= 3) {
            powers3_.emplace_back(power3_ + 1);
        }
        powers3_.emplace_back(std::numeric_limits< difference_type >::max());
    }

    void
    forward(Iterator _begin, Iterator _end) const
    {
        return forward(_begin, std::distance(_begin, _end));
    }

    void
    backward(Iterator _begin, Iterator _end) const
    {
        return backward(_begin, std::distance(_begin, _end));
    }

    void
    forward(Iterator _begin, difference_type const _size) const
    {
        assert(0 < _size);
        assert(_size % 2 == 0);
        difference_type const left_size_ = *(std::upper_bound(powers3_.cbegin(), powers3_.cend(), _size) - 1);
        cycle_leader_forward(_begin, left_size_);
        difference_type const rest_ = _size - left_size_;
        if (rest_ != 0) {
            Iterator middle_ = _begin + left_size_;
            forward(middle_, rest_);
            std::rotate(_begin + left_size_ / 2, middle_, middle_ + rest_ / 2);
        }
    }

    void
    backward(Iterator _begin, difference_type const _size) const
    {
        assert(0 < _size);
        assert(_size % 2 == 0);
        difference_type const left_size_ = *(std::upper_bound(powers3_.cbegin(), powers3_.cend(), _size) - 1);
        std::rotate(_begin + left_size_ / 2, _begin + _size / 2, _begin + (_size + left_size_) / 2);
        cycle_leader_backward(_begin, left_size_);
        difference_type const rest_ = _size - left_size_;
        if (rest_ != 0) {
            Iterator middle_ = _begin + left_size_;
            backward(middle_, rest_);
        }
    }

private :

    void
    cycle_leader_forward(Iterator _begin, difference_type const _size) const
    {
        for (difference_type leader_ = 1; leader_ != _size - 1; leader_ *= 3) {
            permutation_forward permutation_(leader_, _size);
            Iterator current_ = _begin + leader_;
            value_type first_ = std::move(*current_);
            while (++permutation_) {
                assert(permutation_ < _size);
                Iterator next_ = _begin + permutation_;
                *current_ = std::move(*next_);
                current_ = next_;
            }
            *current_ = std::move(first_);
        }
    }

    void
    cycle_leader_backward(Iterator _begin, difference_type const _size) const
    {
        for (difference_type leader_ = 1; leader_ != _size - 1; leader_ *= 3) {
            permutation_backward permutation_(leader_, _size);
            Iterator current_ = _begin + leader_;
            value_type first_ = std::move(*current_);
            while (++permutation_) {
                assert(permutation_ < _size);
                Iterator next_ = _begin + permutation_;
                *current_ = std::move(*next_);
                current_ = next_;
            }
            *current_ = std::move(first_);
        }
    }

    struct permutation_forward
    {

        permutation_forward(difference_type const _leader, difference_type const _size)
            : leader_(_leader)
            , current_(_leader)
            , half_size_(_size / 2)
        { ; }

        bool
        operator ++ ()
        {
            if (current_ < half_size_) {
                current_ += current_;
            } else {
                current_ = 1 + (current_ - half_size_) * 2;
            }
            return (current_ != leader_);
        }

        operator difference_type () const
        {
            return current_;
        }

    private :

        difference_type const leader_;
        difference_type current_;
        difference_type const half_size_;

    };

    struct permutation_backward
    {

        permutation_backward(difference_type const _leader, difference_type const _size)
            : leader_(_leader)
            , current_(_leader)
            , half_size_(_size / 2)
        { ; }

        bool
        operator ++ ()
        {
            if ((current_ % 2) == 0) {
                current_ /= 2;
            } else {
                current_ = (current_ - 1) / 2 + half_size_;
            }
            return (current_ != leader_);
        }

        operator difference_type () const
        {
            return current_;
        }

    private :

        difference_type const leader_;
        difference_type current_;
        difference_type const half_size_;

    };

    std::deque< difference_type > powers3_;

};
Kurzawa answered 9/11, 2012 at 14:34 Comment(11)
I think this implementation has O(N log N) complexity because you exchange sub-arrays starting from larger one (so you move a growing sub-array on each iteration).Manila
Another way is to keep the length of all the subarrays and then do backward motion with reverse function. But this violates the requirement O(log log n) memoryKurzawa
Or, we can calculate the (growing) length of the next small subarray every step back - it will be O (log n log n) time additionaly.Kurzawa
Please, tell me how to perform step 4 with O(n) time complexity and O(log log n) memory?Kurzawa
You just shown two ways to do it without violating any requirements. Second one, lengths recalculation on each step, requires O((log N)^3) additional time. But is is less than O(N), so the whole algorithm still has O(N) time complexity. First one, "keep the length of all the sub-arrays" should be corrected: keep only the number of sub-arrays of each size (0 .. 3), which means 2 * log N bits. You could pack these bits into a pair of integers.Manila
I granted the O(n) time. This solution is pretty dirty, but quite a workable.Kurzawa
Your solution's iterators seem to go out-of-bounds, try running it with iterator checking enabled.Requisite
@Mehrdad Thank you. Yes, this is the issue, but I have no time. In any case, you have given a working version (I hope) of the solution.Kurzawa
@Dukales: Yes -- I actually found an easier solution I wrote below this answer. :)Requisite
I tried to check my solution with the -D_GLIBCXX_DEBUG=1 and found that the problem is only in the backward algorithm. So forward algorithm is still workable.Kurzawa
@Mehrdad I rewrote the solution. Now it designed only for even numbers.Kurzawa
R
0

I modified the code here to get this algorithm:

void PartitionIndexParity(T arr[], size_t n)
{
    using std::swap;
    for (size_t shift = 0, k; shift != n; shift += k)
    {
        k = (size_t)pow(3, ceil(log(n - shift) / log(3)) - 1) + 1;
        for (size_t i = 1; i < k; i *= 3)  // cycle-leader algorithm
        {
            size_t j = i;
            do { swap(arr[(j = j / 2 + (j % 2) * (k / 2)) + shift], arr[i + shift]); } while (j != i);
        }

        for (size_t b = shift / 2, m = shift, e = shift + (k - k / 2), i = m; shift != 0 && k != 0; )  // or just use std::rotate(arr, b, m, e)
        {
            swap(arr[b++], arr[i++]);
            if (b == m && i == e) { break; }
            if (b == m) { m = i; }
            else if (i == e) { i = m; }
        }
    }
}
Requisite answered 4/11, 2013 at 5:9 Comment(0)
D
0

Here's a Java implementation of Peiyush Jain's algorithm:

import java.util.Arrays;

public class InShuffle {
    public static void main(String[] args) {
        Integer[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };

        inShuffle(nums);

        System.out.println(Arrays.toString(nums));
    }

    public static <T> void inShuffle(T[] array) {
        if (array == null) {
            return;
        }

        inShuffle(array, 0, array.length - 1);
    }

    private static <T> void inShuffle(T[] array, int startIndex, int endIndex) {
        while (endIndex - startIndex + 1 > 1) {
            int size = endIndex - startIndex + 1;
            int n = size / 2;
            int k = (int)Math.floor(Math.log(size + 1) / Math.log(3));
            int m = (int)(Math.pow(3, k) - 1) / 2;

            rotateRight(array, startIndex + m, startIndex + n + m - 1, m);

            for (int i = 0, cycleStartIndex = 0; i < k; ++i, cycleStartIndex = cycleStartIndex * 3 + 2) {
                permuteCycle(array, startIndex, cycleStartIndex, 2 * m + 1);
            }

            endIndex = startIndex + 2 * n - 1;
            startIndex = startIndex + 2 * m;
        }
    }

    private static <T> void rotateRight(T[] array, int startIndex, int endIndex, int amount) {
        reverse(array, startIndex, endIndex - amount);
        reverse(array, endIndex - amount + 1, endIndex);
        reverse(array, startIndex, endIndex);
    }

    private static <T> void reverse(T[] array, int startIndex, int endIndex) {
       for (int leftIndex = startIndex, rightIndex = endIndex; leftIndex < rightIndex; ++leftIndex, --rightIndex) {
           swap(array, leftIndex, rightIndex);
       }
    }

    private static <T> void swap(T[] array, int index1, int index2) {
        T temp = array[index1];
        array[index1] = array[index2];
        array[index2] = temp;
    }

    private static <T> void permuteCycle(T[] array, int offset, int startIndex, int mod) {
        for (int i = ((2 * startIndex + 2) % mod) - 1; i != startIndex; i = ((2 * i + 2) % mod) - 1) {
            swap(array, offset + i, offset + startIndex);
        }
    }
}

And doing an out-shuffle is as simple as:

public static <T> void outShuffle(T[] array) {
    if (array == null) {
       return;
    }

    inShuffle(array, 1, array.length - 1);
}
Dagley answered 3/1, 2016 at 6:31 Comment(0)
R
0
public class OddToLeftEvenToRight {

    private static void doIt(String input){

        char[] inp = input.toCharArray();
        int len = inp.length;

        for(int j=1; j< len; j++)
        {
            for(int i=j; i<len-j; i+=2)
            {

                swap(inp, i, i+1);

            }
        }

        System.out.print(inp);

    }

    private static void swap(char[] inp, int i, int j) {

        char tmp = inp[i];
        inp[i]= inp[j];
        inp[j]=tmp;

    }

    public static void main(String[] args)
    {

        doIt("a1b");
    }

}

This program does it in O(n^2).

Rahman answered 27/3, 2016 at 1:0 Comment(0)
O
0

There is a simple in-place algorithm that does exactly N swaps described here: https://mcmap.net/q/581137/-de-interleave-an-array-in-place

Define a source index function to find the value that belongs at a given location in the desired result.

def index(loc, N): 
  mid = N-N//2 
  return (loc*2)+1 if loc < mid else (loc-mid)*2

then walk from beginning to the end, swapping every item with the one at the calculated index, but if that index is earlier than the current location, recalculate to see where it was swapped to.

def unshuffle(A):
    N = len(A)
    for i in range(N):
      source = index(i, N)
      while source < i:
        source = index(source,N)
      A[i],A[source] = A[source],A[i]
    return A

The number of calls to index is logarithmic, but for large arrays on real hardware, the time cost is minimal compared to the cost of the swaps.

This gives

>>> unshuffle([7, 5, 6, 3, 8, 4, 2, 1])
[5, 3, 4, 1, 7, 6, 8, 2]
Owl answered 24/4, 2022 at 2:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.