Guest User

Untitled

a guest
Dec 14th, 2017
75
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.89 KB | None | 0 0
  1. /*********************************************
  2. * 多倍長乗算 ( by Karatsuba 法 )
  3. * - 多倍長 * 多倍長
  4. * - 最下位の桁を配列の先頭とする考え方
  5. *********************************************/
  6. #include <cstdlib> // for rand()
  7. #include <iostream> // for cout
  8. #include <math.h> // for pow()
  9. #include <stdio.h> // for printf()
  10.  
  11. //#define TEST // テスト ( 乗算回数・処理時間 ) するならコメント解除
  12. #define D_MAX 1024 // 計算可能な最大桁数 ( 2のべき乗 )
  13. #define D 1024 // 実際に計算する桁数 ( D_MAX 以下の 4 の倍数 )
  14.  
  15. using namespace std;
  16.  
  17. /*
  18. * 計算クラス
  19. */
  20. class Calc
  21. {
  22. int A[D]; // 被乗数配列
  23. int B[D]; // 乗数配列
  24. #ifdef TEST
  25. int cnt_mul; // 乗算回数
  26. clock_t t1, t2; // 計算開始CPU時刻、計算終了CPU時刻
  27. double tt; // 計算時間
  28. #endif
  29.  
  30. public:
  31. Calc(); // コンストラクタ
  32. void calcKaratsuba(); // 計算 ( Karatsuba 法 )
  33.  
  34. private:
  35. void multiplyNormal(int *, int *, int, int *); // 乗算 ( 標準(筆算)法 )
  36. void multiplyKaratsuba(int *, int *, int ,int *); // 乗算 ( Karatsuba 法 )
  37. void doCarry(int *, int); // 繰り上がり・借り処理
  38. void display(int *, int *, int *); // 結果出力
  39. };
  40.  
  41. /*
  42. * コンストラクタ
  43. */
  44. Calc::Calc()
  45. {
  46. /* ====================================== *
  47. * テストなので、被乗数・乗数は乱数を使用 *
  48. * ====================================== */
  49.  
  50. int i; // LOOP インデックス
  51.  
  52. // 被乗数・乗数桁数設定
  53. for (i = 0; i < D; i++) {
  54. A[i] = rand() % 10;
  55. B[i] = rand() % 10;
  56. }
  57. }
  58.  
  59. /*
  60. * 計算 ( Karatsuba 法 )
  61. */
  62. void Calc::calcKaratsuba()
  63. {
  64. int a[D_MAX]; // 被乗数配列
  65. int b[D_MAX]; // 乗数配列
  66. int z[D_MAX * 6]; // 計算結果用配列
  67. int i; // LOOPインデックス
  68.  
  69. #ifdef TEST
  70. t1 = clock(); // 計算開始時刻
  71. for (int l = 0; l < 1000; l++) {
  72. cnt_mul = 0; // 乗算回数リセット
  73. #endif
  74. // 配列初期設定 ( コンストラクタで設定した配列を設定 )
  75. for (i = 0; i < D; i++) {
  76. a[i] = A[i];
  77. b[i] = B[i];
  78. }
  79.  
  80. // 最大桁に満たない部分は 0 を設定
  81. for (i = D; i < D_MAX; i++) {
  82. a[i] = 0;
  83. b[i] = 0;
  84. }
  85.  
  86. // 乗算 ( Karatsuba 法 )
  87. multiplyKaratsuba(a, b, D_MAX, z);
  88.  
  89. // 繰り上がり・借り処理
  90. doCarry(z, D_MAX * 2);
  91. #ifdef TEST
  92. }
  93. t2 = clock(); // 計算終了時刻
  94. tt = (double)(t2 - t1) / CLOCKS_PER_SEC; // ==== 計算時間
  95. #endif
  96.  
  97. // 結果出力
  98. display(a, b, z);
  99. }
  100.  
  101. /*
  102. * 乗算 ( 標準(筆算)法 )
  103. */
  104. void Calc::multiplyNormal(int *a, int *b, int tLen, int *z)
  105. {
  106. int i, j; // ループインデックス
  107.  
  108. // 計算結果初期化
  109. for(i = 0; i < tLen * 2; i++) z[i] = 0;
  110.  
  111. // 各配列を各桁とみなして乗算
  112. for (j = 0; j < tLen; j++) {
  113. for (i = 0; i < tLen; i++) {
  114. z[j + i] += a[i] * b[j];
  115. #ifdef TEST
  116. cnt_mul++; // 乗算カウント
  117. #endif
  118. }
  119. }
  120. }
  121.  
  122. /*
  123. * 乗算 ( Karatsuba 法 )
  124. */
  125. void Calc::multiplyKaratsuba(int *a, int *b, int tLen, int *z)
  126. {
  127. // ==== 変数宣言
  128. int *a0 = &a[0]; // 被乗数/右側配列ポインタ
  129. int *a1 = &a[tLen / 2]; // 被乗数/左側配列ポインタ
  130. int *b0 = &b[0]; // 乗数 /右側配列ポインタ
  131. int *b1 = &b[tLen / 2]; // 乗数 /左側配列ポインタ
  132. int *v = &z[tLen * 5]; // v (= a1 + a0) 用配列ポインタ
  133. int *w = &z[tLen * 5 + tLen / 2]; // w (= b1 + b0) 用配列ポインタ
  134. int *x1 = &z[tLen * 0]; // x1 (= a0 * b0) 用配列ポインタ
  135. int *x2 = &z[tLen * 1]; // x2 (= a1 * b1) 用配列ポインタ
  136. int *x3 = &z[tLen * 2]; // x3 (= v * w) 用配列ポインタ
  137. int i; // LOOPインデックス
  138.  
  139. // ==== 配列が4個になった場合は標準乗算
  140. if (tLen <= 4) {
  141. multiplyNormal(a, b, tLen, z);
  142. return;
  143. }
  144.  
  145. // ==== v = a1 + a0, w = b1 + b0
  146. for(i = 0; i < tLen / 2; i++) {
  147. v[i] = a1[i] + a0[i];
  148. w[i] = b1[i] + b0[i];
  149. }
  150.  
  151. // ==== x1 = a0 * b0
  152. multiplyKaratsuba(a0, b0, tLen / 2, x1);
  153.  
  154. // ==== x2 = a1 * b1
  155. multiplyKaratsuba(a1, b1, tLen / 2, x2);
  156.  
  157. // ==== x3 = (a1 + a0) * (b1 + b0)
  158. multiplyKaratsuba(v, w, tLen / 2, x3);
  159.  
  160. // ==== x3 = x3 - x1 - x2
  161. for(i = 0; i < tLen; i++) x3[i] -= x1[i] + x2[i];
  162.  
  163. // ==== z = x2 * R^2 + (x3 - x2 - x1) * R + x1
  164. // ( x1, x2 は既に所定の位置にセットされているので、x3 のみ加算 )
  165. for(i = 0; i < tLen; i++) z[i + tLen / 2] += x3[i];
  166. }
  167.  
  168. /*
  169. * 繰り上がり・借り処理
  170. */
  171. void Calc::doCarry(int *a, int tLen) {
  172. int cr; // 繰り上がり
  173. int i; // ループインデックス
  174.  
  175. cr = 0;
  176. for(i = 0; i < tLen; i++) {
  177. a[i] += cr;
  178. if(a[i] < 0) {
  179. cr = -(-(a[i] + 1) / 10 + 1);
  180. } else {
  181. cr = a[i] / 10;
  182. }
  183. a[i] -= cr * 10;
  184. }
  185.  
  186. // オーバーフロー時
  187. if (cr != 0) printf("[ OVERFLOW!! ] %d\n", cr);
  188. }
  189.  
  190. /*
  191. * 結果出力
  192. */
  193. void Calc::display(int *a, int *b, int *z)
  194. {
  195. int i; // LOOPインデックス
  196.  
  197. // 上位桁の不要な 0 を削除するために、配列サイズを取得
  198. int aLen = D_MAX, bLen = D_MAX, zLen = D_MAX * 2;
  199. while (a[aLen - 1] == 0) if (a[aLen - 1] == 0) aLen--;
  200. while (b[bLen - 1] == 0) if (b[bLen - 1] == 0) bLen--;
  201. while (z[zLen - 1] == 0) if (z[zLen - 1] == 0) zLen--;
  202.  
  203. // a 値
  204. printf("a =\n");
  205. for (i = aLen - 1; i >= 0; i--) {
  206. printf("%d", a[i]);
  207. if ((aLen - i) % 10 == 0) printf(" ");
  208. if ((aLen - i) % 50 == 0) printf("\n");
  209. }
  210. printf("\n");
  211.  
  212. // b 値
  213. printf("b =\n");
  214. for (i = bLen - 1; i >= 0; i--) {
  215. printf("%d", b[i]);
  216. if ((bLen - i) % 10 == 0) printf(" ");
  217. if ((bLen - i) % 50 == 0) printf("\n");
  218. }
  219. printf("\n");
  220.  
  221. // z 値
  222. printf("z =\n");
  223. for (i = zLen - 1; i >= 0; i--) {
  224. printf("%d", z[i]);
  225. if ((zLen - i) % 10 == 0) printf(" ");
  226. if ((zLen - i) % 50 == 0) printf("\n");
  227. }
  228. printf("\n\n");
  229.  
  230. #ifdef TEST
  231. printf("Counts of multiply / 1 loop = %d\n", cnt_mul); // 乗算回数
  232. printf("Total time of all loops = %f seconds\n", tt); // 処理時間
  233. #endif
  234. }
  235.  
  236. /*
  237. * メイン処理
  238. */
  239. int main()
  240. {
  241. try
  242. {
  243. // 計算クラスインスタンス化
  244. Calc objCalc;
  245.  
  246. // 乗算 ( Karatsuba 法 )
  247. objCalc.calcKaratsuba();
  248. }
  249. catch (...) {
  250. cout << "例外発生!" << endl;
  251. return -1;
  252. }
  253.  
  254. // 正常終了
  255. return 0;
  256. }
Add Comment
Please, Sign In to add comment