《编程珠玑》中向量旋转“杂技法”

设向量x长度为L,要将前N位循环左移。

设待移动的初始位为i,则杂技法是x[i] <->x[i+N] <-> x[i+2N] ….. <-> x[i+kN] ,当(i+kN)%L ==i时,不再移位,并将x[i+kN] = x[i] 此时完成一次移位。设LN的最大公约数为d,则共需要完成d次移位后,才能完成了前N位的循环左移。


想要证明这种方法的可行性,也就是证明这种方法是否完成了所有位的循环左移N位。

1、设当前位为i,则第(i+N)%L将循环左移到第i位,(在此,可将向量作为一个首尾相连的圆环来理解,参考图1)

2、如何证明一共移了L位呢?

证明:在进行d次移位后,可完成前N位的循环左移,考虑每次移位移动了多少位。设L=αd,N=βd,则由于每次移位在(i+kN)%L ==i时结束,设i+kN = mL +i,则 kβd = mαd,由于α和β互素,则k ==α时,是使得等式成立的最小值,这就证明了每一次移位移动了α位后结束。只有在进行d次移位后,才能让所有L =αd位循环左移。

3、如何证明每一次移位不会对之前所移的位产生影响呢?

证明:这分为两种情况进行证明

(1)在一次移位中,不存在一个位被两次操作的情况,也就是证明(i+kN)%L == i可在一次移位中发生两次。在2中已证明,想要使得等式成立,至少需要移位α次,由此可证在一次移位中,不可能出现二次操作已被操作的位的情况。

(2)多次移位中,不存在已操作的位被再次操作的情况。证明如下:

设从0开始,进行第一次移位,即i=0时,则 x[0]ß x[0+N]x[0+N] ß x[(0+2N)%L]

…… x[(0+(α-1)N)%L] ß x[0] 在第一次移位中,不会影响1234……d-1 及其倍数位,证明如下:设kN%L = x … r kN = xL +r 即kβd�C xαd = r => (kβ �C xα)d = r

由此证明,r只能是d的倍数,不可能是1234……d-1 及其倍数。

     当第一次结束时,i=i+1 =1 ,进行下一次循环,同理也可证,移位操作时,不会影响023

           4……d-1 及其倍数位,i=d-1执行完时,移位结束,这时共移了αd位。


到此为止 证明结束


图1


实现代码1:


int* vector_loop1(int* array,int len,int loop_num)
  {  
      int i=0,j=0,temp;
      int d = gcd(len,loop_num);
      while(d)
      {
          temp = array[i];
          while((i+loop_num)%len != j)
          {
              array[i] = array[(i+loop_num)%len];
              i = (i+loop_num)%len;
          }  
          array[i] = temp;
          j++;
          i=j;
          d--;
      }  
      return array;
  }



     实现代码2


int* vector_loop2(int* array,int len,int loop_num)
  {
      int i=0,j=0,temp;
      temp = array[i];
      for(int k=0;k<len;k++)
     {
          if((i+loop_num)%len == j)
          {
              array[i] = temp;
              j++;
             i=j;
          }
          else
          {
             array[i] = array[(i+loop_num)%len];
              i = (i+loop_num)%len;
        }
      }
     return array;
 }



你可能感兴趣的:(编程珠玑,向量移位,杂技法)