您的位置:首页 > 其它

俄罗斯农夫算法

2015-08-20 14:54 441 查看

引入问题

  众所周知,位运算比加减乘除法省时间。

  那么问题来了,给你一个非高精度数a和b,要求你不用任何乘除号完成a*b的运算,如何实现。

写在第二

  当当当当,俄罗斯农夫算法闪亮出场。

  当我真的开始着手研究这个算法时,惊讶于这方面的资料之少。想来如果可以重载一下运算符的话,如果能把每一边乘法的时间都进行优化的话,说不定能使整个算法的效率大大提高。然而当我花了半个小时才写出一个正确代码后,不仅感慨用这么多代码去换一丝丝丝的时间是否真的值得。虽然如此,即以决定,就把这个详细介绍俄罗斯农夫算法的博客写完吧。

理解算法 

规则:什么是俄罗斯农民乘法?我要怎么使用它?
原理:俄罗斯农民乘法的工作原理是什么?
联系:俄罗斯农民乘法是如何与二进制相关联的呢?

什么是俄罗斯农民乘法?我要怎么使用它?

资料来源:http://article.yeeyan.org/view/66573/28201

我们绝大多数人学的都是这样做大数字乘法的:

86
x 57
------
602
+ 4300
------
4902

如果你懂得乘法算式,那么这种“长式相乘”的方法是快速和相对简单的。不过,还有许多其它的计算方法。其中之一通常被称之为俄罗斯农民算法。使用它时不需要你懂得乘法算式,你只需要将数字加倍,减半再进行合计。具体规则如下:

* 把每一个数字分别写在列头。
* 将头一列的数字加倍,将第二列的数字减半。
如果在第二列的数字是奇数,将它除以二并把余数去掉。
* 如果第二列的数字是偶数,将其所在行删除。
* 继续加倍、减半和删除直到第二列的数字为1。
* 将第一列中剩余的数字相加。于是就得出了根据原始数字计算出的结果。

让我们以计算57乘以86为例。
把每一个数字分别写在列头。
57 86
将头一列的数字加倍,将第二列的数字减半。
57 86
114 43
如果第二列的数字是偶数,将其所在行删除。
57 86
114 43
继续加倍、减半和删除直到第二列的数字为1。
57 86
114 43
228 21
456 10
912 5
1824 2
3648 1
将第一列中剩余的数字相加。于是就得出了根据原始数字计算出的结果。
57 86
114 43
228 21
456 10
912 5
1824 2
+ 3648 1
4902

真实的俄罗斯农民们可能会用好几碗的鹅卵石来记录他们加倍的数字,用来代替写在列里面的数字。(当然,他们或许不会对我们的例子里那么大的数字感兴趣,要知道四千多个鹅卵石可是很难操作的哟!)俄罗斯的农民们并不是唯一使用这种算法的人,在数千年之前古埃及人就已经发明了类似的方法,而同时在今天的计算机中仍然在使用与之相关的程序。

俄罗斯农民乘法的工作原理是什么?

让我们以计算9×8为例:

9 8
18 4
36 2
72 1

72是唯一一个留在左列里的数字,所以我们的答案就是72。请注意我们在其中一边乘以2,在另一边除以2,2 × 1/2 = 1,所以对最终结果并没有影响:

9 * 8

#include<stdio.h>
void main()
{
int m,n,s,flag;
while(scanf("%d%d",&m,&n)==2)
{
flag=0;
if(m<0)
{
flag=1-flag;
m=0-m;
}
if(n<0)
{
flag=1-flag;
n=0-n;
}
s=0;
while(m>=1)
{
if(m&1==1)
{
s+=n;
m=(m-1)>>1;
n=n<<1;
}
else
{
m=m>>1;
n=n<<1;
}
}
if(flag==0)
printf("%d\n",s);
else
printf("-%d\n",s);
}
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: