这文章主要想讲一讲 rotate 算法的数学原理以及代码的一点解析.
网上有人讲过这个函数 , 但是直接让我去看某书第几节 , 这….. 当然我去看了一下 , 感觉那本书讲的不是很容易理解 , 这里我尽量以大白话去讲 , 逻辑当然还是严格的.
BidirectionalIterator版
索引从 0 开始 , 设前半段某个元素索引为 x (后半段同理) . 设整个长度为 l . middle 索引为 m .
当我们完成操作时 , x 应该变为 l - m + x
第一个操作 reverse( first , middle ) 后 x 变为 m - x - 1
第二个操作不影响前半段
第三个操作 reverse( first , last ) 后 m - x - 1 变为 l - m + x
RandomAccessIterator版
引理
若有两个正整数 m , n , 且 gcd( m , n ) = d , 那么序列 { m % n , 2m % n , 3m % n , …. , nm % n } 一定是 { 0 , d , 2d , …. , n-d } 的某个排列并重复出现 d 次.
引理证明
设 m = m’ d , n = n’ d , m’ 与 n’ 的最大公因数一定是 1.否则的话 d 可以取更大.
很明显 , 一个数 mod n’ 结果只有 n’ 种可能 ( 0 , 1 , 2 , …. , n’-1 )
而 n’ * m’ 是一定整除 n’ 的
考虑 ( m’ % n’ , 2m’ % n’ , 3m’ % n’ , …. , n’ * m’ % n’ ) 一共 n 个余数 , 而这些余数一定位于那些可能当中 . 接下来我们如果能证明任意两个余数不相等 , 那么我们就可以知道这必定是余数可能的某种排列.
假设 k m’ 与 j m’ 的余数相等( 0 ≤ j ≤ k ≤ n ) . 即 ( k - j ) * m’ % n’ = 0 ,加上 m’ 与 n’ 最大公因数为 1 ,所以必须 ( k - j ) % n’ = 0 ( 可以理解为需要找到数把 n’ 给消掉 , 而 m 不可能贡献任何因数 ) , 而 k - j < n’ , 所以这是不可能的 . 在把 n’ , m’ , 余数所有可能 , 全部乘以 d 以后即可得到引理.
__rotate
1 | //代码来自书上 |
__rotate_cycle
1 | //代码来自书上 |
代码原理
这里不想讲了 , 一轮 len/gcd 个元素 , 一共 gcd 轮.
你可以自己用纸笔画一下就知道了