这个分治策略可行么,高手指点一二,谢先!

tongdings 2003-08-10 09:59:48
加精
我想用分治策略实现计算两个大数的乘积,可行么?

//
// —————— ——————
// x = |___|___| y = |___|___|
// n/2 n/2 n/2 n/2
//
// 图1 大整数X和Y的分段 
//
// 我们将n位的二进制整数X和Y各分为2段,每段的长为n/2位(为简单起见,假设n是2的幂),
// 如图1所示。
// 由此,X=A2n/2+B ,Y=C2n/2+D。这样,X和Y的乘积为:
//
// XY=(A2n/2+B)(C2n/2+D)=AC2n+(AD+CB)2n/2+BD (1)
//
// 如果按式(1)计算XY,则我们必须进行4次n/2位整数的乘法(AC,AD,BC和BD),
// 以及3次不超过n位的整数加法(分别对应于式(1)中的加号),此外还要做2次移位
// (分别对应于式(1)中乘2n和乘2n/2)。所有这些加法和移位共用O(n)步运算。
// 设T(n)是2个n位整数相乘所需的运算总数,则由式(1),我们有:
//
// _
// | T(1) = 1
// | (2)
// |_T(n) = 4T(n/2) + O(n)
//
// 由此可得T(n)=O(n2)。因此,用(1)式来计算X和Y的乘积并不比小学生的方法更有效。
// 要想改进算法的计算复杂性,必须减少乘法次数。为此我们把XY写成另一种形式:
//
// XY=AC2n+[(A-B)(D-C)+AC+BD]2n/2+BD (3)
//
// 虽然,式(3)看起来比式(1)复杂些,但它仅需做3次n/2位整数的乘法(AC,BD和(A-B)(D-C)),
// 6次加、减法和2次移位。由此可得:
//
//
// _
// | T(1) = 1
// | (4)
// |_T(n) = 3T(n/2) + cn
//
// 用解递归方程的套用公式法可解得T(n)=O(nlog3)=O(n1.59)。利用式(3),设计
// 算法如下:

//
// Multiply(X,Y,n) △ X和Y为2个小于2n的整数,返回结果为X和Y的乘积XY
// 1. S ← sign(X) * sign(Y) △ S为X和Y的符号乘积}
// 2. X ← abs(X)
// 3. Y ← abs(Y) △ X和Y分别取绝对值}
// 4. if n = 1
// 5. then if (X = 1) and (Y = 1)
// 6. then return S
// 7. else return(0)
// 8. else A ← X的左边n/2位
// 9. B ← X的右边n/2位
// 10. C ← Y的左边n/2位
// 11. D ← Y的右边n/2位
// 12. m1 ← Multiply(A, C, n / 2)
// 13. m2 ← Multiply(A - B, D - C, n / 2)
// 14. m3 ← Multiply(B, D, n / 2)
// 15. S ← S * (m1 * 2n + (m1 + m2 + m3) * 2n/2 + m3)
// 16. return S
//

/* calculate the multiply of x and y,
n is their Bit count(32,16,8,4,2,1).
*/
__int64 Multiply(__int32 x, __int32 y, unsigned int n)
{
__int64 s; // s is the sign of x*y

__int64 m1, // m1 = ac
m2, // m2 = (a-b)*(d-c)
m3; // m3 = bd

__int32 a, // x的左边n/2位
b, // x的右边n/2位
c, // y的左边n/2位
d; // y的右边n/2位

unsigned long temp1,temp2;

// initialize s
if ( (x > 0 && y < 0) || (x < 0 && y > 0))
{
s = -1;
// abs x and y
x = abs(x);
y = abs(y);
}
else
if ( x == 0 || y == 0 )
{
s = 0;
return 0;
}
else
s = 1;

if (n == 1) // 一位数
{
if (x == 1 && y ==1)
return (s);
else
return 0;
}
else
{
temp1 = (1 << (n / 2)) - 1;
if (n == 32)
temp2 = 0xFFFFFFFF;
else
temp2 = (1 << n) - 1;

a = (x & (temp2 - temp1)) >> (n / 2); // x的左边n/2位
b = x & temp1; // x的右边n/2位
c = (y & (temp2 - temp1)) >> (n / 2); // y的左边n/2位
d = y & temp1; // y的右边n/2位

n /= 2;

m1 = Multiply (a, c, (n)); // && 0xFFFFFFFF;
m2 = Multiply ((a-b), (d-c), (n)); // && 0xFFFFFFFF;
m3 = Multiply (b, d, (n)); // && 0xFFFFFFFF;

s = s * (m1 * (temp2 + 1) + (m1 + m2 + m3) * (temp1 + 1) + m3);

return (s);

}
}

以上算法在调试中发现:计算4字节的两数相乘时有错,不晓得是否范围越界。计算2字节或者以下都能通过的。很怪。希望能得到根本性的解答。谢过……
...全文
79 49 打赏 收藏 转发到动态 举报
写回复
用AI写文章
49 条回复
切换为时间正序
请发表友善的回复…
发表回复
duguyai 2003-08-12
  • 打赏
  • 举报
回复
看看fft吧
tongdings 2003-08-11
  • 打赏
  • 举报
回复
Data Type Ranges
C/C++ recognizes the types shown in the table below.

Type Name Bytes Other Names Range of Values
______________________________________________________________________________
int * signed,signed int System dependent
unsigned int * unsigned System dependent
__int8 1 char,signed char –128 to 127
__int16 2 short,short int,signed short int –32,768 to 32,767
__int32 4 signed,signed int –2,147,483,648 to 2,147,483,647
__int64 8 none –9,223,372,036,854,775,808 to 9,223,372,036,854,775,807
char 1 signed char –128 to 127
unsigned char 1 none 0 to 255
short 2 short int,signed short int –32,768 to 32,767
unsigned short 2 unsigned short int 0 to 65,535
long 4 long int,signed long int –2,147,483,648 to 2,147,483,647
unsigned long 4 unsigned long int 0 to 4,294,967,295
enum * none Same as int
float 4 none 3.4E +/- 38 (7 digits)
double 8 none 1.7E +/- 308 (15 digits)
long double 10 none 1.2E +/- 4932 (19 digits)
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
想我当年,本来是想学VC的,买了2盘VC盘和2盘MSDN,结果发现VC很麻烦而且MSDN装了竟然不能用。所以一火改用DEV C++ :)
至于TC是我用习惯了,而且我查帮助都是TC下的。Dev C++的help和垃圾一样等于没有。
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
刚才竟然停电了...你注意一下这个问题:
在TC下, 1 << 16为0, 1 << 15为-32768
在Dev c++下, 1 << 32为0,或者更大的也为0.

在位运算哪里很有可能有毛病,具体其他的我明早上再认真看看.刚才没发现什么大问题,但是结果老是错!
tongdings 2003-08-11
  • 打赏
  • 举报
回复
你那边结果对么?
打印出来看看。
tongdings 2003-08-11
  • 打赏
  • 举报
回复
是这样的。我起先是写在下面的。后来我提到:s = -1;处了。总之在我这儿结果都不对啊。
不知道怎么搞的。

1->"*":
ix(16711679) * iy(16773119) = 2.80306980556801000000e+014
TimeSpared: 1214

2->"Multiply()":
ix(16711679) * iy(16773119) = 2.34950657000000000000e+008
TimeSpared: 542

Press any key to continue
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
主要原因是if (x < 0 && y < 0)仍然要都取绝对值,否则它们的符号位将通过移位运算连带传到下个数使值增大.
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
在DOS下int是16位的.程序改好了,真是"差之分厘,谬以千里",真得只差那么一点点但是折腾了老半天.
__int64 Multiply(long x, long y, unsigned int n)
{
long s; // s is the sign of x*y

long a, // x的左边n/2位
b, // x的右边n/2位
c, // y的左边n/2位
d; // y的右边n/2位

long m1, // m1 = ac
m2, // m2 = (a-b)*(d-c)
m3; // m3 = bd

unsigned long temp1,temp2;

// initialize s
if ( (x > 0 && y < 0) || (x < 0 && y > 0))
s = -1;
else
s = 1;
// abs x and y
x = abs(x);
y = abs(y);

if (n == 1) // 一位(Bit)数
{
if (x == 1 && y ==1)
return (s);
else
return 0;
}
else
{
temp1 = (1 << (n / 2)) - 1;
if (n == 32)
temp2 = 0xFFFFFFFF;
else
temp2 = (1 << n) - 1;

a = (x & (temp2 - temp1)) >> (n / 2); // x的左边n/2位
b = x & temp1; // x的右边n/2位
c = (y & (temp2 - temp1)) >> (n / 2); // y的左边n/2位
d = y & temp1; // y的右边n/2位

n /= 2;

m1 = Multiply (a, c, (n));
m2 = Multiply ((a-b), (d-c), (n));
m3 = Multiply (b, d, (n));

s = s * (m1 * (temp2 + 1) + (m1 + m2 + m3) * (temp1 + 1) + m3);

return (s);
}
}
tongdings 2003-08-11
  • 打赏
  • 举报
回复
在TC下, 1 << 16为0, 1 << 15为-32768
在Dev c++下, 1 << 32为0,或者更大的也为0.

从你这儿的结果分析,好像int是2Byte的。我这儿是4Byte的。
你在DOS下编译的嘛?
tongdings 2003-08-11
  • 打赏
  • 举报
回复
printf("%ld\n",1<<32);
printf("%d,%d,%d,%ld\n",1<<4,1<<8,1<<16,(1<<32)-1);

我这儿结果:

0
16,256,65536,-1
Press any key to continue

结果是意料中的吧,在32平台下,33Bit的当然无法表示了。1<<32是为0吧。
tongdings 2003-08-11
  • 打赏
  • 举报
回复
感觉MSDN确实不错,比Dephi下的on line help,毕竟平台是Microsoft的吧。我是从Delphi转到VC平台的。以前在TC和BC用过,但不会很熟,现在差不多没什么印象了。
tongdings 2003-08-11
  • 打赏
  • 举报
回复
断点用惯了,反而疏远了printf。
以前初学C时,C teacher不是经常强调用printf调试程序一说的么?
呵呵..
晕了
tongdings 2003-08-11
  • 打赏
  • 举报
回复
呵呵。这个printf用处还挺大么?
我跟到1 bit,跟了好几次,怎么就没想到用它呢。
高见
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
确实是比较难理解点,比如:
if (x == 1 && y ==1){
printf("%s %d\n", "-----------------------", s);
return s;
}else
return 0;
你看看结果就知道了.因为:
m1 = Multiply (a, c, (n));
m2 = Multiply ((a-b), (d-c), (n));
m3 = Multiply (b, d, (n));
传递值参数可能存在负数,n只是一个递归终止的条件,并不代表传入的x,y的只有n bit.
tongdings 2003-08-11
  • 打赏
  • 举报
回复
那些我基本了解。
int型别是随操作系统变化的。
看来,整个程序卡在temp1 和 temp2的型别上,比较难以捉摸了。
其实我有对temp2进行过特殊处理的,现在看来还是主观上的问题吧。


tongdings 2003-08-11
  • 打赏
  • 举报
回复
ok.
收到.3x
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
关于数据类型使用的可以看:
http://expert.csdn.net/Expert/topic/2130/2130535.xml?temp=.8538019
你原先的程序主要是错在选择错误的数据类型,改改就对了.另外分治算法是很麻烦的,错一个小地方可能累积起一个大错误很难调试.而且分析算法复杂度更是复杂.
分治是个好算法思想,但是难掌握.
tongdings 2003-08-11
  • 打赏
  • 举报
回复
恩。
对于1 bit而言,要想结果s为-1,则两者中一个为1,另一为-1,
long型的-1,表示为0xFF FF FF FF(0x7F FF FF FF为最大正数值),
好象经过移位,好象不会有得到-1这个值。
其实,严密来讲的话,还是要返回s保险啊。

if (n == 1) // 一位(Bit)数
{
// if (x == 1 && y ==1)
return (s);
// else //前已经返回0
// return 0;
}
再次感谢ZhangYv(恋爱中,请勿打扰...) 兄。
揭贴。
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
上面的这部分可以优化成:
temp1 = (1 << (n / 2)) - 1;
temp2 = ((temp2 = 1) << n) - 1;
a = x >> (n / 2); // x的左边n/2位
b = x & temp1; // x的右边n/2位
c = y >> (n / 2); // y的左边n/2位
d = y & temp1; // y的右边n/2位
ZhangYv 2003-08-11
  • 打赏
  • 举报
回复
那个return (1)是错误的,因为(s = -1) OR (s = 1),这是我的程序,你测测吧.
#include "stdio.h"
#include "stdlib.h"
#include "time.h"
#include "windows.h"
#include "math.h"
using namespace std;
/* calculate the multiply of x and y,
n is their Bit count(32,16,8,4,2,1).
*/
__int64 Multiply(long x, long y, unsigned int n)
{
__int64 s; // s is the sign of x*y

long a, // x的左边n/2位
b, // x的右边n/2位
c, // y的左边n/2位
d; // y的右边n/2位

__int64
m1, // m1 = ac
m2, // m2 = (a-b)*(d-c)
m3; // m3 = bd

long temp1;
__int64 temp2;

// initialize s
if ( (x > 0 && y < 0) || (x < 0 && y > 0))
s = -1;
else
s = 1;
x = abs(x);
y = abs(y);

if (n == 1) // 一位(Bit)数
{
if (x == 1 && y ==1)
return s;
else
return 0;
} else
{
temp1 = (1 << (n / 2)) - 1;
temp2 = ((temp2 = 1) << n) - 1;
a = (x & (temp2 - temp1)) >> (n / 2); // x的左边n/2位
b = x & temp1; // x的右边n/2位
c = (y & (temp2 - temp1)) >> (n / 2); // y的左边n/2位
d = y & temp1; // y的右边n/2位

n /= 2;

m1 = Multiply (a, c, (n));
m2 = Multiply ((a-b), (d-c), (n));
m3 = Multiply (b, d, (n));

s = s * (m1 * (temp2 + 1) + (m1 + m2 + m3) * (temp1 + 1) + m3);

return s;
}
}


void main()
{
long ix,iy;
__int64 result;

LARGE_INTEGER t1,t2; //对比算法的执行效率

// srand((unsigned)time(NULL)); //initialize
//srand((unsigned)0xFFFFFFFF);

// ix = rand(); //rand(),也可指定其他较小数测试
// iy = rand();
// ix = 16719; //对大数的测试
// iy = 16779;
ix = 0x4FFFFFFF;
iy = 0x4FFFFFF8;
//算术乘效率
QueryPerformanceCounter(&t1);
printf("1->\"*\":\nix(%8ld) * iy(%8ld) = %.20e \n",ix,iy,(double)ix*iy);
QueryPerformanceCounter(&t2);
printf("TimeSpared: %10ld \n\n",(t2.QuadPart-t1.QuadPart));

//Multiply的执行效率
QueryPerformanceCounter(&t1);

result = Multiply(ix,iy,32); //这里32也可取16..1 (2^n, n in [5..0]).
printf("2->\"Multiply()\":\nix(%8ld) * iy(%8ld) = %.20e \n",ix,iy,(double)result);
QueryPerformanceCounter(&t2);
printf("TimeSpared: %10ld \n\n",(t2.QuadPart-t1.QuadPart));
system("pause");
}
加载更多回复(29)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧