编程之美读书笔记_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
,内存访问开销小,判断次数对效率影响较大。
数组循环移位
对长度为
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 ; }
相关文章推荐
- 编程之美读书笔记2.17—数组循环移位
- 《编程之美》读书笔记09: 2.17 数组循环移位
- 编程之美 2.17 数组循环移位
- 2.17——数组循环移位
- [编程之美] PSet2.17 数组循环移位
- 读书笔记之编程之美 - 2.17 数组循环移位
- 编程之美 2.17 数组循环移位
- 2.17 数组循环移位
- 编程之美 2.17 数组循环移位
- 编程之美2.17 数组循环移位
- 编程之美2.17—数组循环移位(旋转数组)
- 2.17 数组循环移位
- [编程之美2.17]数组循环移位
- 编程之美2.17——数组循环移位
- 编程之美-2.17 数组移位循环
- 编程之美2.17数组循环移位Java版
- 2.17数组循环移位
- 编程之美2.17——数组循环移位——解法…
- 编程之美读书笔记-数组循环移位
- 编程之美2.17 数组循环移位