8.5Recursive Multiply

Problem

Write a recursive function to multiply two positive integers without using the * operator (or / operator). You can use addition, subtraction, and bit shifting, but you should
minimize the number of those operations.

Solution

首先考虑一下做乘法意味着什么。思考一件事情到底意味着什么通常是很有用的,即使是在非常明显的情况下。

来看一下进行8 * 7,可以通过8个7相加或者7个8相加得到结果。我们也可以它认为是一个8 * 7的网格中方块的个数。

Solution1

  我们如何计算一个网格中方块的个数?最简单的方法就是一个个去数。但是,这真的很慢。
  我们还可以数一半的方块的个数,然后将它加倍(通过将它和自己相加)。为了计算一半方块的个数,我们重复同样的过程。
  当然,只有当方块的个数是偶数时,上述算法才能工作。当方块个数是奇数时,我们需要从头来做。

int minProductHelper(int smaller,int bigger)
{
    if(smaller == 0)//0*bigger = 0
        return 0;
    else if(smaller == 1)//1*bigger = bigger
        return bigger;
    int halfSmaller = smaller >> 1;
    int side1 = minProductHelper(halfSmaller,bigger);
    int side2 = side1;
    if(smaller %2 == 1)
        side2 = minProductHelper(smaller - halfSmaller,bigger);

    return side1 + side2;
}

int minProduct(int a,int b)
{
    int bigger = a < b?b:a;
    int smaller = a < b?a:b;

    return minProductHelper(smaller,bigger);

}

Solution2

  如果我们观察递归如何进行,就会发现我们做了很多重复工作。考虑下面的例子:
  这里写图片描述
对minProduct(4,23)的第二次调用不知道之前的调用,所以做了重复的工作做。我们可以缓存这些结果。

int minProductHelper(int smaller,int bigger,vector<int>& memo)
{
    if(smaller == 0)//0*bigger = 0
        return 0;
    else if(smaller == 1)//1*bigger = bigger
        return bigger;
    else if(memo[smaller] > 0)
        return memo[smaller];
    int halfSmaller = smaller >> 1;
    int side1 = minProductHelper(halfSmaller,bigger,memo);
    int side2 = side1;
    if(smaller %2 == 1)
        side2 = minProductHelper(smaller - halfSmaller,bigger,memo);
    memo[smaller] = side1 + side2;
    return memo[smaller];
}

int minProduct(int a,int b)
{
    int bigger = a < b?b:a;
    int smaller = a < b?a:b;
    vector<int> memo(smaller+1,-1);
    return minProductHelper(smaller,bigger,memo);

}

Solution3

  当我们看前面的代码时我们会发现使用偶数来调用minProduct的速度要比使用奇数来调用要快得多。例如,如果我们调用minProduct(30,35),那么我们只需要计算minProduct(15,35)然后将它翻倍就行了。但是,如果我们要计算minProduct(31,35),那么我们就需要调用minProduct(15,35)和minProduct(16,35)。
  这不是必须的。相反,我们可以这样做:
  minProduct(31,35) = 2*minProduct(15,35) + 35
因为31 = 2*15 + 1,所以31 *35 = 2 *15 *35 + 35。
  最后这个解法的逻辑就是:偶数时,我们将smaller除以2,将递归调用的结果翻倍;奇数时,我们与偶数时的操作相同,但是要加上bigger之后作为最后的结果。
  这样做,还带来了另一个好处。我们的minProduct一直向下递归,每次都是用更小的数调用。因为不会出现重复调用的情况,所以没有缓存信息的需要。
  

int minProductHelper(int smaller,int bigger)
{
    if(smaller == 0)//0*bigger = 0
        return 0;
    else if(smaller == 1)//1*bigger = bigger
        return bigger;
    int halfSmaller = smaller >> 1;
    int side1 = minProductHelper(halfSmaller,bigger);
    int side2 = side1;
    if(smaller %2 == 1)
        side2+=bigger;

    return side1 + side2;
}

int minProduct(int a,int b)
{
    int bigger = a < b?b:a;
    int smaller = a < b?a:b;

    return minProductHelper(smaller,bigger);

}

这个算法的时间复杂度时O(log s),其中s是两个数中比较小的那一个。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值