用C语言和汇编语言实现将1个整数分解成几个素数的乘积
2012-06-13 23:08
471 查看
任何1个大于2的整数都可以分解成几个素数的乘积。将1个整数分解成几个素数的乘积是个热门话题。
经常有人问到。本文试图用C语言和32位X86汇编语言给出一个比较好的实现。希望对c语言学习者和
汇编语言学习者带来帮助。
问题的提出:将一个整数分解素因数
根据算术基本定理,任何大于2的正整数都可以表示为素数的乘积,如果不考虑这些素数出现的次序,其
表示方式为唯一的。本题目的要求是输入一个正整数,将这个正整数显示为几个素数乘积的形式,如
如
1=1
2=2
3=3
4=2*2
5=5
6=2*3
7=7
...
60=2*2*3*5
算法:
对于合数n,它总可表示为两个数的乘积,如n=a*b,令a<=b,容易证明a <= sqrt(n),这里sqrt(n)表示n的
平方根,下同。这说明了,用i =2 到 sqrt(n)之间的数逐一试除,如果所有的i都不是n的因数,则n一定
是一个素数。如果在试除过程中,某个i能够整数n,则将i存入因子表,n/i => n,直到 i*i大于n为止。
在试除过程中,若p和q都是n的因子,p是素数,q大于p,q是p的倍数,如果p能够整除n,则n = n / p,
这样,最终n就不再是p的倍数,故n也不会是q的倍数,故在计算n = n / p这一过程,p一定是一个素数,
这种方法得到的所有因子都是素因子。
在所有的试除方法中,最少的试除步骤是用2到sqrt(n)之间的所有素数去试除。但这个算法需要预先算出
sqrt(n)之间的所有素数。这是最快的算法,但我们这里不打算搞得这么复杂,我们这里的算法是用2和所有2
以上的奇数去试出。因为如果一个数是4,6,8的倍数,那么他一定是2的倍数,当被2除后,就不需要试再试
除4,6,8了。
下面给出一个C语言的实现
汇编语言可能是最麻烦的语言,在实践中,除非必要,好少有人用汇编语言写程序。这主要是因为
1.同样的题目,用汇编语言编程需要的代码量较大,费时费力
2.汇编语言的可读性很差,日后维护的成本也大。
许多有经验的工程师对汇编语言写程序都很头疼,对许多初学这说,更是一件难以完成的任务。
初学者常犯的错误是,一上来就有汇编写程序,不管有没有理清头绪,这样造成的结果就是,程序改
了又改,但总是不对,好容易改对了,但程序的风格很差,代码冗长,逻辑混乱,可读性很差。其实,
用任何语言写成都需要设计,程序应该具有模块化,条理清晰,风格一致。汇编语言是最接近机器的
语言,编写汇编程序,非常有利用理解计算机的工作原理。
个人觉得,对于一个逻辑比较复杂的汇编程序的设计,不妨采用高级语言为原型,用高级语言编译运
行通过了,说明你也就对这个题目理解了,然后,看看你的程序,哪里可以简化,哪里可以重构使其
更优雅。最后可将你用高级语言写的程序翻译成汇编语言,这里的翻译不是简单的翻译,汇编语言有
汇编语言的特点,他具有更大的灵活性,开发者应该总是用最适当的方式来实现它。
下面是这个题目的汇编版本,使用masm32编写。关于输入输出部分,使用的几个Windows API函数,
这里说明如下:
程序使用ReadFile从控制台输入数据,使用WriteConsole输出运算结果到控制台。字符串到数的转化
子程序是自己写的。见myAtoi。而数到字符串的转化调用的则是Windows API函数wsprintf。此外,程
序还用到GetStdHandle和ExitProcess,感兴趣的同学,可以查MSDN来得到更多的信息。
在C语言版,外循环的判断语句"while (fac*fac<=n)"使用了乘法,而汇编语言班则使用了性能更好的lea
指令,见下
mov edx,s_fac
mov eax,fac
lea edx,[edx+eax*4+4] ; (fac+2)*(fac+2)=s_fac * 4*fac+4
mov s_fac,edx
add fac,2 ; fac +=2
其基本原理为,若s_fac是fac的平方,则在求(fac+2)^2时,使用了数学公式(fac+2)^2= fac^2 + 4*fac +4,
这里的^表示乘方,程序中,fac和s_fac分别表示当前因子和当前因子的平方,然后当前因子加2,同时需要
更新s_fac,在上面的代码中,先计算(fac+2)^2,(fac+2)=fac^2 + 4*fac +4=s_fac+ 4*fac +4,然后计算fac+2.
lea指令支持*4,*8这样的形式,故这里使用lea指令而没有时候性能很慢的乘法指令。
下面给出汇编语言版的代码,可借助c版的程序来理解汇编版,同时请体会汇编语言和c语言在处理逻辑方面的
的不同,特别是分支和循环语句。
liangbch@263.net, 转载请注明出处。
经常有人问到。本文试图用C语言和32位X86汇编语言给出一个比较好的实现。希望对c语言学习者和
汇编语言学习者带来帮助。
问题的提出:将一个整数分解素因数
根据算术基本定理,任何大于2的正整数都可以表示为素数的乘积,如果不考虑这些素数出现的次序,其
表示方式为唯一的。本题目的要求是输入一个正整数,将这个正整数显示为几个素数乘积的形式,如
如
1=1
2=2
3=3
4=2*2
5=5
6=2*3
7=7
...
60=2*2*3*5
算法:
对于合数n,它总可表示为两个数的乘积,如n=a*b,令a<=b,容易证明a <= sqrt(n),这里sqrt(n)表示n的
平方根,下同。这说明了,用i =2 到 sqrt(n)之间的数逐一试除,如果所有的i都不是n的因数,则n一定
是一个素数。如果在试除过程中,某个i能够整数n,则将i存入因子表,n/i => n,直到 i*i大于n为止。
在试除过程中,若p和q都是n的因子,p是素数,q大于p,q是p的倍数,如果p能够整除n,则n = n / p,
这样,最终n就不再是p的倍数,故n也不会是q的倍数,故在计算n = n / p这一过程,p一定是一个素数,
这种方法得到的所有因子都是素因子。
在所有的试除方法中,最少的试除步骤是用2到sqrt(n)之间的所有素数去试除。但这个算法需要预先算出
sqrt(n)之间的所有素数。这是最快的算法,但我们这里不打算搞得这么复杂,我们这里的算法是用2和所有2
以上的奇数去试出。因为如果一个数是4,6,8的倍数,那么他一定是2的倍数,当被2除后,就不需要试再试
除4,6,8了。
下面给出一个C语言的实现
#include <stdio.h> #include <stdlib.h> typedef unsigned long DWORD; //函数decomp_integer对n分解素因数,分解后的素数存入facArr数组,并返回因子的个数 int decomp_integer( DWORD n, DWORD facArr[]) { DWORD fac; //n的可能的一个因子 int count; if (n<4) //4以内的数,不需要分解 { facArr[0]=n; return 1; } count=0; //下面的while循环为2试出n,直到2不是n的因子为止 while ( (n & 1)==0) // 这里判断偶数用 (n &1)==0,这比(n % 2)==0更快 { facArr[count++]=2; n/=2; } fac=3; //用3到sqrt(n)之间的奇数试除 while (fac*fac<=n) // fac*fac <= n { while (n % fac==0) { facArr[count++]=fac; n /= fac; } fac+=2; } if (n==1) return count; facArr[count++]=n; return count; } int main(int argc, char* argv[]) { DWORD n,facArray[32]; int i,count; printf("Please input unsigned integer:"); scanf("%u",&n); count=decomp_integer(n,facArray); printf("%u=%u",n,facArray[0]); for (i=1;i<count;i++) printf("*%d",facArray[i]); printf("\n"); return 0; }
汇编语言可能是最麻烦的语言,在实践中,除非必要,好少有人用汇编语言写程序。这主要是因为
1.同样的题目,用汇编语言编程需要的代码量较大,费时费力
2.汇编语言的可读性很差,日后维护的成本也大。
许多有经验的工程师对汇编语言写程序都很头疼,对许多初学这说,更是一件难以完成的任务。
初学者常犯的错误是,一上来就有汇编写程序,不管有没有理清头绪,这样造成的结果就是,程序改
了又改,但总是不对,好容易改对了,但程序的风格很差,代码冗长,逻辑混乱,可读性很差。其实,
用任何语言写成都需要设计,程序应该具有模块化,条理清晰,风格一致。汇编语言是最接近机器的
语言,编写汇编程序,非常有利用理解计算机的工作原理。
个人觉得,对于一个逻辑比较复杂的汇编程序的设计,不妨采用高级语言为原型,用高级语言编译运
行通过了,说明你也就对这个题目理解了,然后,看看你的程序,哪里可以简化,哪里可以重构使其
更优雅。最后可将你用高级语言写的程序翻译成汇编语言,这里的翻译不是简单的翻译,汇编语言有
汇编语言的特点,他具有更大的灵活性,开发者应该总是用最适当的方式来实现它。
下面是这个题目的汇编版本,使用masm32编写。关于输入输出部分,使用的几个Windows API函数,
这里说明如下:
程序使用ReadFile从控制台输入数据,使用WriteConsole输出运算结果到控制台。字符串到数的转化
子程序是自己写的。见myAtoi。而数到字符串的转化调用的则是Windows API函数wsprintf。此外,程
序还用到GetStdHandle和ExitProcess,感兴趣的同学,可以查MSDN来得到更多的信息。
在C语言版,外循环的判断语句"while (fac*fac<=n)"使用了乘法,而汇编语言班则使用了性能更好的lea
指令,见下
mov edx,s_fac
mov eax,fac
lea edx,[edx+eax*4+4] ; (fac+2)*(fac+2)=s_fac * 4*fac+4
mov s_fac,edx
add fac,2 ; fac +=2
其基本原理为,若s_fac是fac的平方,则在求(fac+2)^2时,使用了数学公式(fac+2)^2= fac^2 + 4*fac +4,
这里的^表示乘方,程序中,fac和s_fac分别表示当前因子和当前因子的平方,然后当前因子加2,同时需要
更新s_fac,在上面的代码中,先计算(fac+2)^2,(fac+2)=fac^2 + 4*fac +4=s_fac+ 4*fac +4,然后计算fac+2.
lea指令支持*4,*8这样的形式,故这里使用lea指令而没有时候性能很慢的乘法指令。
下面给出汇编语言版的代码,可借助c版的程序来理解汇编版,同时请体会汇编语言和c语言在处理逻辑方面的
的不同,特别是分支和循环语句。
; 如何编译链接该程序 ; 本程序需使用masm32来编译,若你的masm32安装在c盘,请设置如下环境变量 ; set include=c:\masm32\include ; set lib=c:\masm32\lib ; set PATH=c:\masm32\bin;%PATH% ; 若本程序名为decomp.asm,则运行如下命令编译和链接和运行 ; ml /c /coff decomp.asm ; link /subsystem:console decomp.obj ; decomp.exe .386 .model flat,stdcall ; 32 bit memory model option casemap :none ; case sensitive include windows.inc include user32.inc includelib user32.lib include kernel32.inc includelib kernel32.lib .data facTab dd 32 dup(?) ;number的所有素因子 facCount dd ? ;number的所有素因子的个数 number dd ? ;被分解的数 fac dd ? ;number的当前因子 s_fac dd ? ;number的平方 tLen dd ? ;临时变量,仅在调用Windows API函数是用 hInput dd ? ;标准输入设备句柄 hOutput dd ? ;标准输出设备句柄 inBuffer db 32 dup(?) ;输入缓冲区 outBuffer db 128 dup(?) ;输出缓冲区 .const szFmtIn db '%u',0 szFmtOut1 db 'Please input an unsigned integer:' szFmtOut2 db '%u=%u',0 szFmtOut4 db '*%u',0 .code ;use global varible, don't pass any in parameter in stack dcomp proc far uses esi mov esi,number ;esi is copy of n cmp esi,4 jae case_02 case_01: mov facTab[0],eax mov ecx,1 jmp this_exit case_02: xor ecx,ecx ;ecx: copy of count jmp loop_02_cmp loop_02: mov facTab[ecx*4], 2 inc ecx ;count++ shr esi,1 ;esi is copy of n,n /=2 loop_02_cmp: mov edx,esi and edx, 1 jz loop_02 ; n % 2==0 mov fac,3 mov s_fac, 9 jmp loop03_outer_cmp loop03_outer: jmp loop03_inner_cmp loop03_inner: mov edx, fac mov facTab[ecx*4],edx ;fac[count++]=fac inc ecx ;count++ xor edx,edx mov eax,esi div fac mov esi,eax loop03_inner_cmp: xor edx,edx mov eax,esi div fac or edx,edx jz loop03_inner mov edx,s_fac mov eax,fac lea edx,[edx+eax*4+4] ; (fac+2)*(fac+2)=s_fac * 4*fac+4 mov s_fac,edx add fac,2 ; fac +=2 loop03_outer_cmp: mov eax,s_fac cmp eax,esi jbe loop03_outer cmp esi,1 ; jz this_exit ; n ==1 mov facTab[ecx*4],esi inc ecx mov eax,ecx this_exit: mov eax,ecx ;返回素因子数 ret dcomp endp ; convert from a string to a integer myAtoi proc far C uses ebx esi, var_String mov esi,var_String ;in parameter => esi mov ebx,10 xor eax,eax xor ecx,ecx loop_read_char: mov cl,byte ptr [esi] ; get a char from in_buff inc esi sub cl,'0' cmp cl,9 ja input_exit ;if the char <'0' or >'9', then jump out loop mul ebx add eax,ecx jmp loop_read_char input_exit: ret myAtoi endp ; use global varible, don't pass parameter in stack ; number: in parameter ; facTab: in paramter ; facCount: factor count ; hInstance: input device handle output_fac proc far C uses ebx edi lea edi, outBuffer mov eax,facTab[0] invoke wsprintf, edi, addr szFmtOut2,number,eax add edi, eax mov ebx,1 jmp next_cmp convert_loop: mov eax,facTab[ebx*4] invoke wsprintf,edi,addr szFmtOut4,eax add edi, eax ;edi the write pointer in next ; 下次输出时的地址送edi inc ebx next_cmp: cmp ebx,facCount jb convert_loop print: lea eax, outBuffer sub edi, eax ;计算输出的字符串的长度,存入edi invoke WriteConsole,hOutput,addr outBuffer,edi,addr tLen,0 ;在控制台输出 ret output_fac endp main proc far invoke GetStdHandle,STD_OUTPUT_HANDLE ; 获取控制台输出设备的句柄 mov hOutput,eax invoke WriteConsole,hOutput,addr szFmtOut1,sizeof szFmtOut1,addr tLen,0 invoke GetStdHandle,STD_INPUT_HANDLE ; 获取控制台输入设备的句柄 mov hInput, eax invoke ReadFile,hInput,addr inBuffer,sizeof inBuffer,ADDR tLen,NULL lea eax, inBuffer invoke myAtoi,eax ;转化字符串为一个数 mov number, eax ;将这个数存入number invoke dcomp ;调用因子分解子程序,,各个因子存入facTab mov facCount,eax ;分解后的因子个数存入facCount invoke output_fac ;调用output_fac打印number的分解式 xor eax, eax invoke ExitProcess,eax ;退出进程 ret main endp end main
liangbch@263.net, 转载请注明出处。
相关文章推荐
- 将一个整数分解为一个或者多个素数的乘积
- c语言:实现对于给定的正整数N,依次打印出小于等于N的所有素数。两种方法及其优化
- 每一个比1大的整数N只能有一种方式分解成素数的乘积。
- tbox的项目:vm86(汇编语言虚拟机),tbox(类似dlib),gbox(c语言实现的多平台图形库)
- 分解一个整数为素数的乘积形式
- C语言调用汇编语言 实现字符串拷贝
- C语言switch语句的汇编语言实现
- C程序实现整数的素数和分解问题
- 不用定时器和汇编语言,只用C语言实现精确无误的延时
- 使用中语言实现整数的因子分解算法
- 【C/C++】任意大于1的整数分解成素数因子乘积的形式
- C语言中嵌入汇编语言实现简单的加法
- 【源代码】将一个整数的每位数分解并按逆序放入一个数组中(用递归算法)(C语言实现)
- 【源码】将一个整数的每位数分解并按逆序放入一个数组中(用递归算法)(C语言实现)
- <C语言>如何一步一步根据简单的代码联想到更多的功能?(实现输入一个整数,输出比它小包括它本身的所有素数。)
- 将一个整数分解为一个或者多个素数的乘积
- [Codeforces546D]Soldier and Number Game[dp][实现][素数筛][分解整数][数学]
- C语言switch语句的汇编语言实现
- c语言:实现对于给定的正整数N,依次打印出小于等于N的所有素数。两种方法及其优化
- 对于1个正整数N,将其拆分成几个正整数的和,如何拆分可使得其乘积最大?