views:

401

answers:

8

Hello,

I have a working rotating function going for my "items" int array. The code below gets it done, except that im transferring values out unnecessarily. Im trying to acheive the "inplace" rotation. What I mean by that is where the ptrs would increment or decrement instead of grabbing values out of the array..By which I need to "up" the efficiency level in that way for this method..Any Suggestions?

void quack::rotate(int nRotations)
{
 if ( count <= 1 ) return;
 else  // make sure our ptrs are where we want them.
 {
  intFrontPtr = &items[0].myInt;
  intBackPtr  = &items[count-1].myInt;
 }
 for (int temp = 0; nRotations != 0;)
 {
  if ( nRotations > 0 )
  {
     temp = *intFrontPtr;
    *intFrontPtr = *intBackPtr;
    *intBackPtr  = temp; // Connect temps for the rotation
   --intBackPtr; // Move left [...<-] into the array
  }
  else if ( nRotations < 0 ) 
  {
   temp = *intBackPtr;
   *intBackPtr  = *intFrontPtr;
   *intFrontPtr = temp; // Connect temps for the rotation
   ++intFrontPtr; // Move right [->...] into the array
  }
  if ( intBackPtr  == &items[0].myInt  || 
    intFrontPtr == &items[count-1].myInt ) 
  {
   intFrontPtr = &items[0].myInt; 
   intBackPtr  = &items[count-1].myInt; // need to re-set
   if ( nRotations > 0 ) nRotations--;  // Which ways did we rotate?
   else nRotations++;
  }
 }
 }

Oh yes, Im trying to practice c++ and know their are many functions floating around that are programmed to do this already...Im trying to "build my own". I think i've got it down syntactically, but the efficiency is always where i struggle. As, a novice, I would greatly appreciate critisim towards this aspect..

+1  A: 

hi...doing the rotations one by one is really not the way to go. If you are doing anything more than 2 or 3 rotations it gets really slow really quick.

edit: as a final thought... putting the elements in a (double) linked 'looped' list (so the final element points to the first), would require for a rotate to only move the head pointer a few elements. (The head pointer being a pointer to indicate which element in the looped list is the beginning).

this is by far the quickest (and easiest) way to do a rotate on a list of elements

Toad
+4  A: 

There is an old trick for rotating elements in an array (I first saw it in Programming Pearls)

Say you want to rotate an array to the left by three elements.

First reverse the first three elements, next reverse the remaining elements, and then reverse the entire array.

Starting Array:
1 2 3 4 5 6 7

After reversing the first three elements
3 2 1 4 5 6 7

After reversing the remaining elements
3 2 1 7 6 5 4

Finally reverse the entire array to get the final rotated array
4 5 6 7 1 2 3

Reversing portions of the array can be done in place so you don't need any extra memory.

sdtom
Isn't that rotating the array to the left?
Nick Meyer
Yep. Typo fixed.
sdtom
great trick. Though you are always moving an element twice while it can be done in one go.
Toad
@reinier - How would you rotate the array an arbitrary number of positions while only touching each element once?
sdtom
@sdtom: You mentioned "Programming Pearls", right? The "Juggling Algorithm" in the very same "Programming Pearls" touches each element only once (i.e. one read, one write per element)
AndreyT
@AndreyT Yeah I forgot about the juggling algorithm. IIRC I think I mentally blocked it for being so ugly compared to the block swap. Beauty, of course, is in the eye of the beholder :-)
sdtom
@reinier: While the "juggling algorithm" does it in one go, it is the least efficient among the reasonable ones. The most efficient algorithm for reasonably large input is the "block swap" from "Programming Pearls". And it also moves elements more that once.
AndreyT
andreyt: good point
Toad
Very nice, I hadn't seen this algorithm before. Haven't read Programming Pearls, so I assume the "Juggling Algorithm" is where you chase the predecessor of each element -- right? It's O(n), but that algorithm has much worse locality of reference than the algorithm you give here.
j_random_hacker
+3  A: 

You can leave the data in place, and have a "base index" member to indicate where the array should start. You then need to use this to adjust the index when accessing the array. The array itself should be private, and only accessed through accessor functions that do the adjustment. Something like this:

class quack
{
public:
    explicit quack(int size) : items(new Item[size]), size(size), base(0) {}
    ~quack() {delete [] items;}

    void rotate(int n)      {base = (base + n) % size;}
    Item &operator[](int i) {return items[(base + i) % size];}

private:
    Item *items;
    int   size;
    int   base;
};

although I'd call it something like RotatableArray, rather than quack.

Mike Seymour
A: 

Really the way to do it is to use indexes instead of pointers.

int to = 0;
int from = (to + nRotations) % count;
if (to == from)
    return;

for (int i=0; i < count; i++) {
   swap(from, to);
   from = advance(from);
   to = advance(to);
}

// ...
static inline int advance(int n, int count) { return (n + 1) % count; }
plinth
This doesn't work.
sdtom
+1  A: 

As usual, if you really have to physically rotate the elements, the correct answer for C++ would be to use std::rotate, which does exactly what you want to do.

If you have to implement it manually (as a practice assignment), take a look at these slides for algorithms from John Bentley's "Programming Pearls".

AndreyT
A: 

lampshade, i have a question regarding hash tables do you think you can help me?

Corey
A: 

I may have an alternate solution to rotating the array inline. Rather than the old trick of reversing sets of elements, as proposed earlier, this approach works as follows:

Initialization:

(Note q = amount to shift left, n = length of array)

  1. Determine the first source element, which is located at x1=q%n
  2. The destination element is at x2=0
  3. char, ch1, is the ar[x1] element
  4. char, ch2, is the ar[x2] element

Loop on i=0 to n-1, where n = length of array

  1. Overwrite the destination element, ar[x2] with ch1
  2. Set ch1 = ch2
  3. Set x1 = x2
  4. Set x2 = x2 - q
  5. If x2 is negative due to the above subtraction, add n to it
  6. ch2 = ar[x2]

The following may help explain how this works.

Example, rotate to the left by 2 characters:

a b c d e f g

c d e f g a b

x1    ch1    x2    ch2
2     c      0     a
0     a      5     f
5     f      3     d
3     d      1     b
1     b      6     g
6     g      4     e
4     e      2     c

As you can see, this requires no more than n iterations, so it is linear time algorithm that also rotates inline (requires no additional storage other than the few temporary variables).

Here is a function that implements the above algorithm so you can try it out:

void rotate(char *ar, int q)
{
    if (strlen(ar) < 2)
    {
     return;
    }

    if (q <= 0)
    {
     return;
    }

    char ch1;
    char ch2;
    int x1;
    int x2;
    int i;
    int n;

    n = strlen(ar);

    q %= n;

    if (q == 0)
    {
     return;
    }

    x1 = q;
    ch1 = ar[x1];
    x2 = 0;
    ch2 = ar[x2];

    for (i=0;i<n;i++)
    {
     ar[x2] = ch1;
     ch1 = ch2;

     x1 = x2;

     x2 -= q;

     if (x2 < 0)
     {
      x2 += n;
     }

     ch2 = ar[x2];
    }
}
Daniel Parrott
A: 

This link discusses 6 different methods to rotate an Array.

http://www.rawkam.com/?p=1008

sunil