向量旋转算法 收藏


view plaincopy to clipboardprint?
1. /*
2. Name: 向量旋转算法集锦
3. Copyright: 始发于goal00001111的专栏;允许自由转载,但必须注明作者和出处
4. Author: goal00001111
5. Date: 28-12-08 23:28
6. Description:
7. 向量旋转算法:将具有n个元素的向量a向左旋转r个位置。
8. 例如 :将字符串"abcdefghij"旋转成"defghjabc",其中n=10,r=3。
9. 其实就是将 AB 转换成 BA 的过程,这里A ="abc", B="defghij"。
10. 本文总共采用了四种方法来解决向量旋转问题,依次是:
11. 方法一:最简单的直接移动向量旋转算法;
12. 方法二:简明的的逆置数组旋转算法;
13. 方法三:传说中的杂耍旋转算法;
14. 方法四:一个类似于欧几里得算法的旋转算法;
15. 其中方法一需要一个辅助数组,空间复杂度较高;方法二每个元素都要移动两次,效率相对较低;
16. 方法三和方法四都是极牛的算法,空间和时间复杂度都很低。
17. 这是牛书《编程珠玑》中的一个例题,在书的网站上有详细的源码,我把它改成了我所熟悉的样子。
18. 源码的网站是:http://www.cs.bell-labs.com/cm/cs/pearls/code.html
19. */
20. #include<iostream>
21. #include <time.h>
22.
23. using namespace std;
24.
25. template <class T> //最简单的直接移动向量旋转算法
26. void SlideRotate(T a[], int n, int r);
27.
28. template <class T> //逆置数组的原子操作
29. void Reverse(T a[], int left, int right);
30.
31. template <class T> //简明的的逆置数组旋转算法
32. void ReverseRotate(T a[], int n, int r);
33.
34. int Gcd(int m, int n); //欧几里德算法求最大公约数
35.
36. template <class T> //传说中的杂耍旋转算法
37. void JuggleRotate(T a[], int n, int r);
38.
39. template <class T> //交换两个长度均为len的向量块a[left_1..left_1+len) 和 a[left_2..left_2+len)
40. void Swap(T a[], int left_1, int left_2, int len);
41.
42. template <class T> //一个类似于欧几里得算法的旋转算法
43. void GcdRotate(T a[], int n, int r);
44.
45. template <class T> //创建一个向量
46. void InitVector(T a[], int n);
47.
48. template <class T> //输出一个向量
49. void PrintVector(const T a[], int n);
50.
51. template <class T> //判断向量旋转是否成功
52. bool CheckRotate(const T a[], int n, int r);
53.
54. int main()
55. {
56. const int N = 601; //测试次数
57. const int MAX = 500000; //向量长度
58. int a[MAX] = {0};
59. int rotateDistance = 100000;
60. time_t startTime, endTime;
61.
62. 最简单的直接移动向量旋转算法///
63. time(&startTime);
64. InitVector(a, MAX);
65. // PrintVector(a, MAX);
66. for (int i=0; i<N; i++)
67. SlideRotate(a, MAX, rotateDistance);
68. // PrintVector(a, MAX);
69. if (CheckRotate(a, MAX, rotateDistance))
70. cout << "True" << endl;
71. else
72. cout << "False" << endl;
73.
74. time(&endTime);
75. cout << "time = " << difftime(endTime, startTime) << endl << endl;
76. ///
77. //简明的的逆置数组旋转算法//
78. time(&startTime);
79. InitVector(a, MAX);
80. // PrintVector(a, MAX);
81. for (int i=0; i<N; i++)
82. ReverseRotate(a, MAX, rotateDistance);
83. // PrintVector(a, MAX);
84. if (CheckRotate(a, MAX, rotateDistance))
85. cout << "True" << endl;
86. else
87. cout << "False" << endl;
88.
89. time(&endTime);
90. cout << "time = " << difftime(endTime, startTime) << endl << endl;
91. ///
92. 传说中的杂耍旋转算法 //
93. time(&startTime);
94. InitVector(a, MAX);
95. // PrintVector(a, MAX);
96. for (int i=0; i<N; i++)
97. JuggleRotate(a, MAX, rotateDistance);
98. // PrintVector(a, MAX);
99. if (CheckRotate(a, MAX, rotateDistance))
100. cout << "True" << endl;
101. else
102. cout << "False" << endl;
103.
104. time(&endTime);
105. cout << "time = " << difftime(endTime, startTime) << endl << endl;
106. ///
107. /一个类似于欧几里得算法的旋转算法///
108. time(&startTime);
109. InitVector(a, MAX);
110. // PrintVector(a, MAX);
111. for (int i=0; i<N; i++)
112. GcdRotate(a, MAX, rotateDistance);
113. // PrintVector(a, MAX);
114. if (CheckRotate(a, MAX, rotateDistance))
115. cout << "True" << endl;
116. else
117. cout << "False" << endl;
118.
119. time(&endTime);
120. cout << "time = " << difftime(endTime, startTime) << endl << endl;
121. ///
122.
123. system("pause");
124. return 0;
125. }
126.
127. 方法一:创建一个长度为min{r, n-r)的辅助数组,以帮助完成旋转任务//
128. /*
129. 函数名称:SlideRotate
130. 函数功能:最简单的直接移动向量旋转算法:先利用一个辅助数组将较短的那一段元素存储起来,
131. 再移动原向量中未另外存储的那部分元素,最后将辅助数组中的元素再复制到正确位置
132. 输入参数:T a[]:需要被旋转的向量
133. int n:向量的长度
134. int r:向量左半段长度
135. 输出参数:T a[]:旋转后的向量
136. 返回值:无
137. */
138. template <class T>
139. void SlideRotate(T a[], int n, int r)
140. {
141. if (r < n - r) //如果左半段小于右半段,存储左半段的元素
142. {
143. T *buf = new T[r];
144. for (int i=0; i<r; i++)//存储左半段的元素
145. buf[i] = a[i];
146.
147. for (int i=r; i<n; i++)//移动右半段的元素
148. a[i-r] = a[i];
149.
150. for (int i=0; i<r; i++)//复制左半段的元素到右半段
151. a[n-r+i] = buf[i];
152.
153. delete []buf;
154. }
155. else //否则存储右半段的元素
156. {
157. T *buf = new T[n-r];
158. for (int i=r; i<n; i++)//存储右半段的元素
159. buf[i-r] = a[i];
160.
161. for (int i=r-1; i>=0; i--)//移动左半段的元素
162. a[i+n-r] = a[i];
163.
164. for (int i=0; i<n-r; i++)//复制右半段的元素到左半段
165. a[i] = buf[i];
166.
167. delete []buf;
168. }
169. }
170.
171. //方法二:使用一个逆置数组的原子操作
172. /*
173. 函数名称:Reverse
174. 函数功能:逆置数组的原子操作
175. 输入参数:T a[]:需要被逆置的向量
176. int left: 数组的左界
177. int right:数组的右界
178. 输出参数:T a[]:逆置后的数组
179. 返回值:无
180. */
181. template <class T>
182. void Reverse(T a[], int left, int right)
183. {
184. T t;
185. while (left < right)
186. {
187. t = a[left];
188. a[left] = a[right];
189. a[right] = t;
190. left++;
191. right--;
192. }
193. }
194.
195. /*
196. 函数名称:ReverseRotate
197. 函数功能:简明的的逆置数组旋转算法:构造一个逆置数组的原子操作子函数,然后设置不同的数组左右界,
198. 分三次调用子函数就行了。因为每个元素都需要移动两次,所以效率不是很高。
199. 输入参数:T a[]:需要被旋转的向量
200. int n:向量的长度
201. int r:向量左半段长度
202. 输出参数:T a[]:旋转后的向量
203. 返回值:无
204. */
205. template <class T>
206. void ReverseRotate(T a[], int n, int r)
207. {
208. Reverse(a, 0, r-1); //逆置左半段数组
209. Reverse(a, r, n-1); //逆置右半段数组
210. Reverse(a, 0, n-1); //逆置整段数组
211. }
212.
213. //方法三:传说中的杂耍旋转算法
214.
215. /*
216. 函数名称:Gcd
217. 函数功能:欧几里德算法求最大公约数
218. 输入参数:int m:正整数之一
219. int n:正整数之二
220. 输出参数:无
221. 返回值:正整数m和n的最大公约数
222. */
223. int Gcd(int m, int n)
224. {
225. int t;
226. while (m > 0)
227. {
228. t = n % m;
229. n = m;
230. m = t;
231. }
232. return n;
233. }
234.
235. /*
236. 函数名称:JuggleRotate
237. 函数功能:传说中的杂耍旋转算法:
238. 先将a[0]存储到临时变量t中,然后将a[r]移动到a[0],将a[2r] 移动到 a[r],
239. 依此类推(数组中所有的下标都要对数组的长度n取模),直到(k*r)%n == 0,
240. 此时我们不能在a[0]中取数,而是在临时变量t中取数,然后结束该过程。
241. 如果该过程不能移动所有的元素,那么我们从a[1]开始移动,一直依次下去,
242. 直到移动了所有的元素为止。
243. 那么总共要重复开始移动几次呢?数学证明是Gcd(r, n)次。
244. 此算法每个元素只需移动一次就可以到达正确位置,理论上效率是最高的,
245. 但由于要做求最大公约数和求余运算等准备工作,所以没有显示出优势。
246. 输入参数:T a[]:需要被旋转的向量
247. int n:向量的长度
248. int r:向量左半段长度
249. 输出参数:T a[]:旋转后的向量
250. 返回值:无
251. */
252. template <class T>
253. void JuggleRotate(T a[], int n, int r)
254. {
255. int i, j, k;
256. int cyc = Gcd(r, n); //用r和n的最大公约数作为循环周期
257.
258. for (i=0; i<cyc; i++) //总共需要重复开始移动cyc次,才能使得所有的元素都移动到正确位置
259. {
260. T t = a[i]; //临时变量t存储a[i]
261. j = i;
262. while (1)//依次移动元素,直到 (k*r)%n == 0
263. {
264. k = j + r;
265. if (k >= n) //用除法运算替代模运算
266. k -= n;
267.
268. if (k == i)
269. break;
270. a[j] = a[k];
271. j = k;
272. }
273. a[j] = t;
274. }
275. }
276.
277. //方法四:一个类似于欧几里得辗转相除算法的旋转算法/
278. /*
279. 函数名称:Swap
280. 函数功能:交换两个长度均为len的向量块a[left_1..left_1+len) 和 a[left_2..left_2+len)
281. 输入参数:T a[]:需要被处理的向量
282. int left_1:向量块a[left_1..left_1+len)的左界
283. int left_2:向量块a[left_2..left_2+len)的左界
284. int len: 两个向量块的长度
285. 输出参数:T a[]:交换了部分元素的向量
286. 返回值:无
287. */
288. template <class T>
289. void Swap(T a[], int left_1, int left_2, int len)
290. {
291. T t;
292. while (len > 0)
293. {
294. t = a[left_1];
295. a[left_1] = a[left_2];
296. a[left_2] = t;
297. left_1++;
298. left_2++;
299. len--;
300. }
301. }
302.
303. /*
304. 函数名称:JuggleRotate
305. 函数功能:一个类似于欧几里得辗转相除算法的旋转算法:
306. 就像一个做阻尼振动的弹簧振子一样,按照由两边到中间的顺序,整段的交换向量块,
307. 并且被交换的向量块长度不断缩减,直到lLen == rLen。
308. 由于重复移动的元素较少,所以效率比逆置数组旋转算法要高。
309. 输入参数:T a[]:需要被旋转的向量
310. int n:向量的长度
311. int r:向量左半段长度
312. 输出参数:T a[]:旋转后的向量
313. 返回值:无
314. */
315. template <class T>
316. void GcdRotate(T a[], int n, int r)
317. {
318. if (r == 0 || r == n) //特殊情况不用旋转
319. return;
320.
321. int lLen, rLen, pos;
322. lLen = pos = r;
323. rLen = n - r;
324. while (lLen != rLen)
325. {
326. if (lLen > rLen) //左半段大于右半段,移动右半段
327. {
328. Swap(a, pos-lLen, pos, rLen);
329. lLen -= rLen; //减少移动范围
330. }
331. else
332. {
333. Swap(a, pos-lLen, pos+rLen-lLen, lLen);
334. rLen -= lLen;
335. }
336. }
337. Swap(a, pos-lLen, pos, lLen); //最后交换中间段
338. }
339.
340. /*
341. 函数名称:InitVector
342. 函数功能:创建一个向量
343. 输入参数:T a[]:需要被赋值的向量
344. int n:向量的长度
345. 输出参数:T a[]:创建好的向量
346. 返回值:无
347. */
348. template <class T>
349. void InitVector(T a[], int n)
350. {
351. for (int i=0; i<n; i++) //创建一个向量
352. a[i] = i;
353. }
354.
355. /*
356. 函数名称:PrintVector
357. 函数功能:输出一个向量
358. 输入参数:const T a[]:需要被输出的向量
359. int n:向量的长度
360. 输出参数:无
361. 返回值:无
362. */
363. template <class T>
364. void PrintVector(const T a[], int n)
365. {
366. for (int i=0; i<n; i++)
367. cout << a[i] << ' ';
368. cout << endl;
369. }
370.
371. /*
372. 函数名称:CheckRotate
373. 函数功能:判断向量旋转是否成功
374. 输入参数:T a[]:需要被旋转的向量
375. int n:向量的长度
376. int r:向量左半段长度
377. 输出参数:无
378. 返回值:旋转成功返回true,否则返回false
379. */
380. template <class T>
381. bool CheckRotate(const T a[], int n, int r)
382. {
383. for (int i=0; i<n-r; i++) //判断左半段
384. {
385. if (a[i] != i+r)
386. return false;
387. }
388.
389. for (int i=0; i<r; i++)//判断右半段
390. {
391. if (a[n-r+i] != i)
392. return false;
393. }
394.
395. return true;
396. }

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/clearriver/archive/2009/07/22/4371746.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值