高效循环移位算法.doc

上传人:公**** 文档编号:549010822 上传时间:2023-03-11 格式:DOC 页数:5 大小:54.51KB
返回 下载 相关 举报
高效循环移位算法.doc_第1页
第1页 / 共5页
高效循环移位算法.doc_第2页
第2页 / 共5页
高效循环移位算法.doc_第3页
第3页 / 共5页
高效循环移位算法.doc_第4页
第4页 / 共5页
高效循环移位算法.doc_第5页
第5页 / 共5页
亲,该文档总共5页,全部预览完了,如果喜欢就下载吧!
资源描述

《高效循环移位算法.doc》由会员分享,可在线阅读,更多相关《高效循环移位算法.doc(5页珍藏版)》请在金锄头文库上搜索。

1、要求:设计一个算法,把一个含有N个元素的数组循环右移K位,要求时间复杂度为O(N),且只允许使用两个附加变量。不合题意的解法如下:我们先试验简单的办法,可以每次将数组中的元素右移一位,循环K次。abcd12344abcd12334abcd12234abcd11234abcd。版本1voidRightShift(char*arr,intN,intk)while(k-)chart=arrN-1;for(inti=N-1;i0;i-)arri=arri-1;arr0=t;虽然这个算法可以实现数组的循环右移,但是算法复杂度为O(K*N),不符合题目的要求,需要继续往下探索。分析与解法假如数组为abcd

2、1234,循环右移4位的话,我们希望到达的状态是1234abcd。不妨设K是一个非负的整数,当K为负整数的时候,右移K位,相当于左移(-K)位。左移和右移在本质上是一样的。【解法一】大家开始可能会有这样的潜在假设,KN,右移K-N之后的数组序列跟右移K位的结果是一样的。进而可得出一条通用的规律:右移K位之后的情形,跟右移K=K%N位之后的情形一样。版本2voidRightShift(char*arr,intN,intk)k%=N;while(k-)chart=arrN-1;for(inti=N-1;i0;i-)arri=arri-1;arr0=t;可见,增加考虑循环右移的特点之后,算法复杂度降

3、为O(N),这跟K无关,与题目的要求又接近了一步。但时间复杂度还不够低,接下来让我们继续挖掘循环右移前后,数组之间的关联。【解法二】假设原数组序列为abcd1234,要求变换成的数组序列为1234abcd,即循环右移了4位。比较之后,不难看出,其中有两段的顺序是不变的:1234和abcd,可把这两段看成两个整体。右移K位的过程就是把数组的两部分交换一下。变换的过程通过以下步骤完成:1. 逆序排列abcd:abcd1234 dcba1234;2. 逆序排列1234:dcba1234 dcba4321;3. 全部逆序:dcba4321 1234abcd。版本3Reverse(char*arr,in

4、tb,inte)for(;be;b+,e-)chartemp=arre;arre=arrb;arrb=temp;RightShift(char*arr,intN,intk)k%=N;Reverse(arr,0,N-k-1);Reverse(arr,N-k,N-1);Reverse(arr,0,N-1);编程之美中的题目要求只使用两个附加变量。王晓东编著的算法设计与实验题解中要求只用到O(1)的辅助空间。其它地方两本书的要求相同,都是O(n)的时间复杂度。两本书中的解法总结起来就是三种方法:(1)循环换位算法(2)三次反转算法(3)排列循环算法。这三种算法在王晓东的著作中都有实现代码。第一种算法

5、是最原始的算法。第二种算法比较巧妙,即使用VU=reverse(reverse(U)reserve(V),写成数学形式就是:。于是使用三次反转也可实现。第三种方法与数学有较大关系,以下着重解释第三种方法,借此复习一下数学。对于第三种方法,王晓东老师在著作中介绍了一条循环置换分解定理:对于给定数组A0.N-1向后循环换位N-K位运算,可分解为恰好gcd(K,N-K)个循环置换,且0,.,gcd(K,N-K)-1中的每个数恰属于一个循环置换。其中gcd(x,y)表示x和y的最大公因数。我们从头开始分析这个问题,对于数组A0.n-1,要将其向后循环移动k位元素。因为每个元素右移n位后又回到了原来的位

6、置上,所以右移k位等于右移k mod n位。考虑每个元素右移k位后的最终位置,比如对于A0,右移k位后在k mod n位置上,原来在k mod n位置上的元素右移k位后到了2*k mod n的位置上,把如此因为A0的移动而受到连环影响必须移动的位置列出来,就是下面这样一个位置序列:0,k,2*k,.,(t-1)k。其中每一项都是在模n的意义下的位置。t*k mod n 的结果是0。t是使得t*k mod n的结果为0的最小正整数。这个位置序列实质上是模n加法群中由元素k生成的一个循环子群。由群论中的结论(该结论的证明见最后)知,循环子群(k)的周期为n / gcd(k,n),元数为n / gc

7、d(k,n),其陪集个数为gcd(k,n)。换句话说,A0.n-1循环右移k位的结果是循环子群(k)以及它的所有陪集循环右移一位。例如,将A0.5 = 1,2,3,4,5,6循环右移4位,这里n = 6, k = 4, gcd(k, n) = 2。A0的最终位置是4,A4的最终位置是2,A2的最终位置是0,这样,位置0,4,2便是由k=4生成的循环群,周期为6 / gcd(4,6) = 6 / 2 = 3,这样的循环子群共有gcd(4,6) = 2个。第三种方法的完整代码如下:/ 数组的循环移位#include int gcd(int m, int n)int r;while(r = m %

8、n)m = n; n = r;return n;/ 因为左移的代码比右移的代码好实现的多,而右移k位/ 等价于左移-k位,-k = n - k。以下代码是通过左移-k位来实现右移k位void shiftArray(int A, int n, int k)k = n - (k % n);for(int i = 0, cnt = gcd(n, k); i cnt; i+)int t = Ai, p = i, j = (k+i) % n;while(j != i)Ap = Aj; p = j; j = (k + p) % n;Ap = t;void printArray(int A, int n)f

9、or(int i = 0; i n; i+)printf(%-3d, Ai);if(i+1)%10 = 0)printf(n);int main()int A = 1,2,3,4,5,6, 7;shiftArray(A, 7, 4);printArray(A, 7);return 0;上述所用到的那个群论结论的证明:结论:设G是一个循环群,其中一个生成元素为a,若r和n的最大公约数为d,则ar的周期为n / d。在模n加法群中,1是一个生成元素,任意元素k=1*k,所以任意元素k生成的循环子群(k)的周期为n / gcd(k,n)。因为gcd(k,n)=gcd(k,n-k),所以也可以写成n / gcd(k, n-k)。

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 生活休闲 > 社会民生

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号