您的位置:首页 > 编程语言

编程之美读书笔记_2.17 数组循环移位

2010-05-18 23:49 183 查看
2.17
数组循环移位

对长度为
n
的数组
(ab)
左移
k
位,最直接的方法,就是用
stl

rotate
函数(
stl
针对三种不同的迭代器,提供了三个版本的
rotate
)。但在某些情况下,用
stl

rotate
效率极差。对数组循环,可以采用:



动态分配一个同样长度的数组,将数据复制到该数组并改变次序,再复制回原数组。



利用
ba=(br
)r
(ar
)r
=(ar
br
)r

,通过三次反转字符串。


分组交换(尽可能使数组的前面连续几个数为所要结果):


a
长度大于
b
,将
ab
分成
a0
a1
b
,交换
a0


b
,得
ba1
a0

,只需再交换
a1


a0




a
长度小于
b
,将
ab
分成
ab0
b1

,交换
a

b0

,得
b0
ab1

,只需再交换
a


b0



通过不断将数组划分,和交换,直到不能再划分为止。分组过程与求最大公约数很相似。


所有序号为
(i+t*k) % n (i
为指定整数,
t
为任意整数
)
,会构成一个循环链(共有
gcd(n,k)
个,
gcd

n

k
的最大公约数),每个循环链上的元素只要移动一个位置即可,总共交换了
n
次。

stl

rotate
的三种迭代器,分别使用了后三种方法。

多数情况下,前三种方法效率都比较高,第一种方法过分要求内存,第四种方法,元素间平均交换次数最少,理论上效率应该是最高的,但在
n

k
都很大时,其效率相当差(由于每次交换访问的内存不连续,在
n

k
比较大时,内存访问开销很大(
cache line
命中率很低,又不断跨页访问内存)。)。方法三的平均交换次数要少于方法二,但判断次数相对要多,效率相差不是太大,在大数组时方法三效率比较高,但同一个小数组左移几十万次,应该是方法二效率略高,毕竟整个数组都可以被
cache
,内存访问开销小,判断次数对效率影响较大。



// 2.17_rotate.cpp   by  flyinghearts#qq.com
 
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
using namespace std ;
 
/*
left_shift_0 通过复制到临时数组
left_shift_1 三次反转
left_shift_2 分组交换
left_shift_3 循环交换
left_shift_4 循环交换(先求最大公约数,确定次数)
left_shift_5 stl 的rotate
 
test 函数:
数组ff 存放要测试的函数
count 测试次数
arr_len 动态生成的测试数组的长度
show 是否显示每次测试用时
*/
 
typedef void MT ( int * , unsigned , int );
MT left_shift_0 , left_shift_1 , left_shift_2 , left_shift_3 ;
MT left_shift_4 , left_shift_5 ;
void test ( MT * ff [], unsigned sz , unsigned count = 1 , unsigned arr_len = 1e6 , bool show = 0 );
 
int main ()
{
   MT * ff []={ left_shift_0 , left_shift_1 , left_shift_2 , left_shift_3 , left_shift_4 , left_shift_5 };
   unsigned sz = sizeof ( ff )/ sizeof ( ff [ 0 ]);
   test ( ff , sz , 4e4 , 1e3 );
   test ( ff , sz , 4 , 2e7 , 1 );
   test ( ff , sz , 4e6 , 100 );
}
 
void left_shift_0 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  vector < int > a ( n );
  int * const p =& a [ 0 ];
  copy ( arr + k , arr + n , p );
  copy ( arr , arr + k , p + n - k );
  copy ( p , p + n , arr );
}
 
 
void my_reserve ( int * arr , int n )
{
  int * p = arr , * q = arr + n - 1 ;
  int tmp ;
  while ( p < q ){
    tmp = * p ;
    * p ++ = * q ;
    * q -- = tmp ;
  }
}
 
 
void left_shift_1 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  my_reserve ( arr , k );
  my_reserve ( arr + k , n - k );
  my_reserve ( arr , n );
}
 
 
void left_shift_2 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  int tmp ;
  int * low = arr , * last = arr + n , * mid = arr + k , * high = mid ;
  while ( mid != last ){
    tmp = * low ;
    * low ++ = * high ;
    * high ++ = tmp ;
    if ( low == mid ) mid = high ;
    else if ( high == last ) high = mid ;
  }
}
 
 
void left_shift_3 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  int tmp , count = n ;
  int * first = arr , * last = arr + n , * cur = first , * next = first ;
  tmp = * first ;
  while ( count --){
    cur = next ;
    next += k ;
    if ( next >= last )   next -= n ;
    if ( next != first ) * cur =* next ;
    else {
      * cur = tmp ;
      tmp = * ++ first ;
       next = first ;
    }
  }
}
 
 
unsigned my_gcd ( unsigned a , unsigned b )
{
  unsigned min = a , max = b , tmp ;
  if ( a > b ) { min = b ; max = a ;}
  while ( min ){
    tmp = min ;
    min =( max - min )% min ;
    max = tmp ;
  }
  return max ;
}
 
 
void left_shift_4 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  int tmp ;
  unsigned i , j ;
  int * first = arr , * last = arr + n , * cur = first , * next = first ;
  unsigned count1 = my_gcd ( k , n );
  unsigned count2 = n / count1 ;
  for ( i = 0 ; i < count1 ; ++ i , ++ first ){
    tmp =* first ;
    next = first ;
    for ( j = 0 ; j < count2 ; ++ j ){
      cur = next ;
      next += k ;
      if ( next >= last ) next -= n ;
      * cur =* next ;
    }
    * cur = tmp ;
  }
}
 
void left_shift_5 ( int * arr , unsigned n , int k )
{
  if ( n == 0 ) return ;
  k %= n ;
  if ( k == 0 ) return ;
  if ( k < 0 ) k += n ;
  rotate ( arr , arr + k , arr + n );
}
 
 
void test ( MT * ff [], unsigned sz , unsigned count , unsigned arr_len , bool show )
{
  clock_t tb , te ;
  unsigned i , j ;
  int k ;
  vector < int > arr ( arr_len );
  vector < clock_t > total ( sz );
  srand ( time ( 0 ));
  for ( i = 0 ; i < count ; ++ i ){
    k = rand ();
    k -= rand ();
    if ( show )   cout << "K: " << k << "/n" ;
    for ( j = 0 ; j < sz ; ++ j ){
      tb = clock ();
      ff [ j ](& arr [ 0 ], arr_len , k );
      te = clock ();
      total [ j ] += te - tb ;
      if ( show ) cout << j << " :  " << te - tb << "  ms/n" ;
    }
    if ( show ) cout << endl ;
  }
    cout << "/nTotal: /n" ;
    for ( j = 0 ; j < sz ; ++ j ){
      cout << j << "  " << total [ j ]<< "  ms/n" ;
    }
    cout << endl ;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: