STL源码剖析中rotate算法的说明

        在《STL源码剖析》中,对于rotate算法没有很好地解释,__rotate_cycle是如何发挥作用的,其中很值得研究。

SGI STL的rotate函数是将一个序列[first,last)的前半部分[first,middle)和后半部分[middle,last)进行调换。整个算法分为3种不同实现。对于书中所说的最后一种算法,本质就是序列的循环移位。见下图:













将[first,middle)循环移动到序列末尾,[middle,last)移动到序列最前面,即可完成rotate功能。

      但STL的rotate实现中采用了__rotate_cycle()函数,此函数对序列中一组元素向前移动middle-first长度。采用while循环调用__rotate_cycle()共移动了gcd(m,n)组。见下图:






__rotate_cycle函数每次移动一种颜色的分组,例如第一次移动红色的元素,第二次移动绿色的元素。每个元素都被向前移动middle-first长度。

       __rotate_cycle函数第一次移动分组: m%n,2m%n,3m%b,......nm%n;(m<n)

第二次移动分组:m%n+1,2m%n+1,......nm%n+1;

第三次移动分组:m%n+2,2m%n+2,......nm%n+2;

依次类推,每个被移动的元素,都向前移动了m距离,移动后仍在当前分组中。

下面是算法用到的一个关键定理

       假设两个正整数m,n,m、n互质,则序列{m%n,2m%n,3m%n,......nm%n }一定覆盖{0,1,2,3......n-1}所有值。(证明见文章末尾)

定理推广形式

假设两个正整数m,n,m、n的最大公约数为d=gcd(m,n),则序列{m%n,2m%n,3m%n,......nm%n }一定覆盖{0,d,2d,3d,......n-d}所有值。

由于m,n可被d整除,因此长度为n的序列可以分成若干长度为d的小序列。如下图:






现在可以清楚地看到,__rotate_cycle函数移动的分组,对应序列中的位置有:

第一分组:{0, d, 2d,...  n-d}。

第二分组:{1, d+1, 2d+1...... n-d+1}。

第三分组:{2, d+2, 2d+2...... n-d+2}。

......

共需要移动多少分组,才能完整覆盖序列? 显然需要d组,这就是为什么需要调用gcd(m,n)次__rotate_cycle的原因。

凡是被__rotate_cycle访问的元素,都被向前移动了m距离。当序列中所有元素都被向前移动m距离后,即完成了一次rotate操作。

下面是定理的证明

引理:假设正整数m,n,m、n互质,对于任意正整数0<k1、k2<=n,k1 != k2 和 k1m%n != k2m%n互为充要条件。

需要证明 k1 != k2时,k1m%n != k2m%n

k1m%n != k2m%n时,k1 != k2。


采用反证法:假设k1 != k2时,有k1m%n == k2m%n。

由于0<k1、k2<=n,则-n<k1-k2<n; 根据取模性质:

k1m%n - k2m%n = (k1 - k2)m%n = 0;

即(k1-k2)m能够整除n。因此(k1-k2)m = np(p为整数)。由于m、n互为质数,其最小公倍数为mn,因此k1-k2>=n或k1-k2 == 0;

由于-n<k1-k2<n,且k1 != k2,因此产生矛盾。假设不成立。

反之证明 k1m%n != k2m%n时,k1 != k2。不再过多叙述。

利用引理证明定理,可知{m%n,2m%n,3m%b,......nm%n }中元素两两不相等,共有n个。每个元素小于n的整数,可能是{0,1,2,3......n-1},因此只能覆盖所有值

证明定理推广形式:

利用取模性质:m = d*m'、n=d*n', 有m%n = d(m'%n');

对于任何正整数,必有

m = d * m';

n = d *n' ;  其中m'和n'互为质数。

m%n = d(m'%n'),因此序列{m%n,2m%n,3m%b,......nm%n }可表示为:

{d(m'%n'), d(2m'%n'), ...... d(n'm'%n'),d((n'+1)m'%n'),......d(n'dm'%n')}。其中(n'+1)m'%n' = m'%n',相当于

{m%n,2m%n,3m%b,......n‘m%n }循环出现了d次。

覆盖范围为d*{0,1,2,3,......n'-1},即为{0,d,2d,......n'd-d} 为{0,d,2d,......n-d}。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值