这个算法在执行gcd(i,n)次后就停止了,为什么? 先来了解一点数论知识(以下内容摘自《初等数论》,潘承洞著):
1 同余:
设m不等于0, 若m|a-b,即 a-b=km, 则称m为模,a同余于b模m,以及b是a对模m的剩余。记作
。
2 同余类
对给定的模m,有且恰有m个不同的模m的同余类,他们是
0 mod m,1 mod m,…,(m-1)mod m。
3 完全剩余系
一组数y1,y2,…,ys称为是模m的完全剩余系,如果对任意的a有且仅有一个yj是a对模m的剩余,即a同余于yj模m。
由此可见0,1,2,…,m-1是一个完全剩余系。因此,如果m个数是两两不同余的,那么这m个数便是完全剩余系。
基于以上知识,我们可以证明这样一个事实,即如果i和n互质的话,那么序列:
0 i mod n 2i mod n 3i mod n …. (n-1)*i mod n
就包括了集合{0,1,2 … n-1}的所有元素。(下一个元素(n)*i mod n 又是0)
为什么?
证明:
由于0,1,2,…,n-1本身是一个完全剩余系,即它们两两互不同余。设此序列为Xi(0<=i<=n-1),可得下式:
Xi≠Xj (mod n),
由于i与n是互质的,所以
Xi*i ≠i*Xj (mod n),这里由于不能打出不同余字符因此用不等于替代,因此i*Xi是m个互不同余数,那么可断定它们是完全剩余系。有了上面的结论,那么如果i和n互质,下面的赋值过程便能完成所有值的赋值(设数组为X[0..n-1],长度为n):
t = X[0]
X[0] = X[i mod n]
X[i mod n] = X[2i mod n]
…….
X[(n-2)*i mod n] = X[(n-1)*i mod n]
X[ (n-1)*i mod n] = t。
因为以上操作已经把包括{0,1,…,n-1}所有元素放到了最终位置上,每次完成一个元素的放置。根据以上我们得到了一个中间结论,如果i和n互质,我们就可以一次完成。那么如果i和n不是互质的呢? 自然的想法是利用我们已经得到的结论,让i和n互质,即让i’ = i/(gcd(i,n)),n’ = n/(gcd(i,n))。这样便构造了一对互质的数, i’和n’。这意味着把整个数组的每g=gcd(i,n)个元素组成块,如下所示:
这样,根据已得结论,我们可以一次获得最终答案,因为i’和n’互质,由于我们的单位是块元素,所以,必须要g次来完成块的移动,每次相当于把g中的一个元素移到最终位置上。所以总共需要g次移动,算法终止。□ 整个证明过程最巧妙的地方在于对i和n进行处理的时候,以及处理之后转换成块元素的这个地方,感觉很巧妙,这样的证明绝对秒杀什么陪集数目的说法,回味无穷。
下面是我的实现:
#include
int gcd(int i, int n){
while(i != n){
if(i > n)
i -= n;
else
n -= i;
}
return i;
}
template
void turnleft(T a[],int i,int n)
{
int count = gcd(i,n);
for(int var =0;var
2:转置算法。
这种算法比较直观,容易理解。应用的话,代码正确率也高些。
#include
template
void Swap(T a[],int l,int r,int interval)
{
for(int i=0;i
void swap_block(T a[],int interval,int n)
{
if((interval==0)||(interval==n))
return ;
int i;
int p = i = interval;
int j= n-interval;
while(i!=j)
{
if(i>j)
{
Swap(a,p-i,p,j);
i-= j;
}
else
{
Swap(a,p-i,p+j-i,i);
j-=i;
}
// printf("%s\n",a);
}
Swap(a,p-i,p,i);
// printf("%s\n",a);
}
int main()
{
char a[] = "abcdefgh";
swap_block(a,3,8);
printf("%s\n",a);
return 0;
}