Juggling算法

最后更新于:2022-04-01 21:40:06

### 问题描述 关于本节问题描述,我们在前几节已经出现,即:***[旋转交换](http://blog.csdn.net/utimes/article/details/8762497)***的引用中问题。提出另一种方案,Juggling算法解决这个问题。(来源于**《编程珠玑》第2版**的第2章第11页问题B) 请将一个具有n个元素的一维向量向左旋转i个位置。例如,假设n=8,i=3,那么向量abcdefgh旋转之后得到向量defghabc。简单编码使用一个具有n个元素的中间向量分n步即可完成此作业。你可以仅使用几十字节的微小内存,花费与n成比例的时间来旋转该向量吗? ### 解析: 直观的想法:由于要对数组整体向左循环移动k位,那么我们就可以对数组中的每一个元素向左移动k位(超过数组长度的可以取模回到数组中),此时总是能获得结果的。如原串 01234567结果:34567012 步骤:(k表示循环移动的位数)  1)先将x[0]移到临时变量t中  2)将x[k]移动到x[0]中,x[2k]移动到x[k]中,依次类推  3)将x中的所有下标都对n取模,直到我们又回到从x[0]中提取元素。不过这时我们从t中提取元素,结束。 循环的终止条件:当我们要从循环的起始位置点中提取元素时,此次循环结束。由于k,2k...之间的偏移量是相同的,所以整个操作实际上就是讲序列向左移动k个位置。 **注意:**从下标0开始,按照上述步骤移动位置时,一次循环并不一定能够把所有数移到目标位置。这还与n和k是否互质有关。如果,n与k互质,从0开始,每一个元素向前移动k个位置,一次循环就可以处理完所有元素,最后一个元素会从0位置取元素。如果,n与k不互质,仅仅从0开始,每次向前移动k个位置。终止时是不能把所有元素放到目的地的。这是要需要进行gcd(n,k)次循环。即第一次是从0开始,每次向前移动k个位置,直至循环结束。第二次是从1开始,每次向前移动k个位置,直至循环结束。第三次...直到第gcd(n,k)-1次。而且每次循环的最后一个元素都会回到该循环的起点。我们这里把包含gcd(n,k)的元素称为一段,可以看出程序需要进行gcd(n,k)循环才能够把所有数移到目标位置。 ### 代码 ~~~ #include //使用辗转相除法求最大公约数 int gcd(int a, int b){ if (a % b == 0) return b; else return gcd(b, a%b); } //杂耍算法 void rotate(char* a,int n,int i){ int step = gcd(n,i); for(int j = 0; j < step; j++){ int temp = a[j]; int current = j; while(1){ int next= (current + i) % n; if(next== j) break; a[current] = a[next]; current= next; } a[current] = temp; } } int main( ){ char a[9] = "abcdefgh"; rotate(a,8,3); printf("%s\n",a); return 0; } ~~~ ### 补充知识: **辗转相除法求最大公约数** (关于辗转相除法详解:[Euclidean algorithm](http://en.wikipedia.org/wiki/Euclidean_algorithm).) 两个整数的最大公约数是能够同时整除它们的最大的正整数。辗转相除法基于如下原理:两个整数的最大公约数等于其中较小的数和两数的。 原理的证明: 设两数为a、b(b a,b的最大公约数为dc,而不是c。 5)既然m - kn与n互质,所以c = gcd(r,b)。 结论,gcd(a,b)=gcd(b,r)。 **转载请注明出处**:[http://blog.csdn.net/utimes/article/details/8759966](http://blog.csdn.net/utimes/article/details/8759966)
';