【问题标题】:Divide by 9 without using division or multiplication operator除以 9 而不使用除法或乘法运算符
【发布时间】:2015-05-31 18:52:40
【问题描述】:

这个问题我已经尝试解决了,但没有任何办法。任何指针将不胜感激。

正则减法做除法不是这里的意图,巧妙地使用移位运算符来完成它的意图。

【问题讨论】:

  • 最简单的除法方法是重复减法。
  • Martin James:不,不是作业,而是geeksforgeeks.org/makemytrip-interview-questions-set-6这个问题。
  • 我想知道为什么人们急于投反对票,以便为自己争取更多的赞成票。
  • @newbie_old 不是我,但投反对票会让投反对票的人付出代价。它不会给他们加分。
  • @newbie_old 我已经发布了一个迟来的答案。

标签: c algorithm math multiplication integer-division


【解决方案1】:

虽然答案已被接受,但我还是发布我的答案。

更新。这是通过乘以一个循环二进制分数来实现的。十进制 1/9 = 0.1111111 重复出现。在二进制中,即 1/1001 = 0.000111000111000111 重复出现。

请注意,二进制乘法器以 6 位为一组,十进制 7 重复出现。所以我在这里要做的是将被除数乘以 7,将其右移 6 位,然后将其添加到运行商中。但是为了保持重要性,我在加法之后进行移位,并在循环结束后将商q 移位以正确对齐。

对于 32 位 int(6 位 * 6 次移位 = 36 位),计算循环最多有 6 次迭代。

#include<stdio.h>

int main(void)
{
    unsigned x, y, q, d;
    int i, err = 0;

    for (x=1; x<100; x++) {             // candidates
        q = 0;                          // quotient
        y = (x << 3) - x;               // y = x * 7

        while(y) {                      // until nothing significant
            q += y;                     // add (effectively) binary 0.000111
            y >>= 6;                    // realign
        }
        q >>= 6;                        // align

        d  = x / 9;                     // the true answer
        if (d != q) {
            printf ("%d / 9 = %d (%d)\n", x, q, d);     // print any errors
            err++;
        }
    }

    printf ("Errors: %d\n", err);
    return 0;
}

不幸的是,由于与乘以十进制 27 * 0.111111 = 2.999999 而不是 3 相同的原因,这对于每个 9 的倍数的候选者来说都失败了,因为舍入错误。所以我现在通过保留 4 ls 来使答案复杂化。用于舍入的商的位。结果是它适用于所有int 值,由两个顶部半字节限制,一个用于* 7,一个用于* 16 意义。

#include<stdio.h>

int main(void)
{
    unsigned x, y, q, d;
    int i, err = 0;

    for (x=1; x<0x00FFFFFF; x++) {
        q = 8;                          // quotient with (effectively) 0.5 for rounding
        y = (x << 3) - x;               // y = x * 7
        y <<= 4;                        // y *= 16 for rounding

        while(y) {                      // until nothing significant
            q += y;                     // add (effectively) binary 0.000111
            y >>= 6;                    // realign
        }
        q >>= (4 + 6);                  // the 4 bits significance + recurrence

        d  = x / 9;                     // the true answer
        if (d != q) {
            printf ("%d / 9 = %d (%d)\n", x, q, d);     // print any errors
            err++;
        }
    }

    printf ("Errors: %d\n", err);
    return 0;
}

【讨论】:

  • 在二进制中,即 1/1001 = 0.000111000111000111 重复出现。没有得到这个。哦,明白了。基本上 1/9 是 1/1001。
  • . 之后的第一个二进制表示二分之一,下一个四分之一,等等。所以这是 1/16 + 1/32 + 1/64 + 等等。前 6 位 (点之后)等于 0.109375 十进制,所以你可以看到它正在收敛到 0.111111 循环小数。
  • 很好的逻辑。我认为 i>= 6; // 重新对齐 z += y; // 添加(有效)二进制 0.000111 } 我也知道为什么要添加 "z >>= 6; // align" 吗?
  • @newbie_old 谢谢。两点 1) 而不是for(i ...) { 循环,使用while(y) { 会更干净,因为当y 已转移到0 时,继续没有意义。 2)循环后z &gt;&gt;= 6;的原因是因为循环内的两条指令应该是相反的:在添加到z之前移位y。我这样做是为了保持重要性。
  • 你能解释一下“保持意义”是什么意思吗?
【解决方案2】:

这是一个深受Hacker's Delight 启发的解决方案,它真正只使用位移位:

def divu9(n):
    q = n - (n >> 3)
    q = q + (q >> 6)
    q = q + (q>>12) + (q>>24); q = q >> 3
    r = n - (((q << 2) << 1) + q)
    return q + ((r + 7) >> 4)
    #return q + (r > 8)

【讨论】:

  • 检查我提供的链接。该章节可免费下载,值得一读。
【解决方案3】:

看到这个答案:https://stackoverflow.com/a/11694778/4907651

除了除数是 3 之外,您正在寻找的正是您想要的。

编辑:解释

我将用简单的+ 替换add 函数,因为您正在寻找不使用*/ 的解决方案。

在这个解释中,我们假设我们除以 3

另外,我假设您知道如何将十进制转换为二进制,反之亦然。

int divideby3 (int num) {
    int sum = 0;
    while (num > 3) {
        sum += (num >> 2);
        num = (num >> 2) + (num & 3);
    }
    if (num == 3)
        sum += 1;
    return sum; 
}

这种方法使用位运算符:

  • 按位与:&amp;
  • 按位左移:&lt;&lt;。将二进制值左移。
  • 按位右移:&gt;&gt;。将二进制值右移。
  • 按位异或:^

第一个条件(num &gt; 3)是这样,因为除数是3。在你的情况下,除数是9,所以当你使用它时,条件必须是(num &gt; 9)

假设我们要除的数是 6。

在二进制中,6 表示为000110

现在,我们进入while (num &gt; 3) 循环。第一条语句将sum(初始化为0)添加到num &gt;&gt; 2

num &gt;&gt; 2 做了什么:

二进制初始数字:00000000 00000110

按位移位后:00000000 00000001 i.e. 1 in decimal

添加num &gt;&gt; 2后的sum1

由于我们知道 num &gt;&gt; 2 等于 1,我们将其添加到 num &amp; 3

二进制初始编号:00000000 00000110

3 二进制:00000000 00000011

对于表达式a &amp; b的结果中的每个位位置,如果两个操作数都包含1,则该位为1,否则为0

num &amp; 3的结果:00000000 00000010 i.e. 2 in decimal

num = (num &gt;&gt; 2) + (num &amp; 3) 之后的num 等于 1 + 2 = 3

现在,由于 num 等于 3,我们进入 if (num==3) 循环。

然后我们将 sum 加 1,然后返回值。 sum 的这个值就是商。

如预期的那样,返回值为 2。

希望这不是一个可怕的解释。

【讨论】:

  • 然后重复 --> 除以 9。
  • 我没看懂解释。我已经读了100遍了,如果你能解释一下,我可以把它作为正确答案。
  • @newbie_old 这里是一个解释。如果我解释的不是很清楚,请告诉我。我会详细说明。
【解决方案4】:

创建一个循环,每一步你都应该减去N-9 .. 然后(N-9)-9 .. 直到N&lt;9 OR N=0 并且每一个减法你都要计算步骤例如:36/9 36-9=27 cmpt (1) 27-9=18 cmpt(2) 18-9=9 cmpt(3) 9-9=0 cmpt (4)

所以36/9= 4

【讨论】:

    【解决方案5】:

    这个http://en.wikipedia.org/wiki/Ancient_Egyptian_multiplication 算法可以在log(n) 时间内只使用减法和二进制移位来完成。然而,据我所知,最先进的硬件已经在使用这个,甚至是更好的算法。因此,我认为您无能为力(假设性能是您的目标),除非您可以以某种方式完全避免除法或更改用例以便您可以除以 2 的幂,因为这些有一些技巧案例。

    【讨论】:

      【解决方案6】:

      如果不允许乘法/除法,则剩下的就是加法/减法。除以数字显示除数包含被除数的次数。您可以使用它作为回报:您可以从原始值中减去该数字多少次?

      divisor = 85;
      dividend = 9;
      remaining = divisor;
      result = 0;
      while (remaining >= dividend)
      {
          remaining -= dividend;
          result++;
      }
      std::cout << divisor << " / " << dividend << " = " << result;
      

      【讨论】:

        【解决方案7】:

        如果需要除正数,可以使用如下函数:

        unsigned int divideBy9(unsigned int num) 
        {
            unsigned int result = 0;
            while (num >= 9)
            {
                result += 1;
                num -= 9;
            }
            return result;
        }
        

        如果是负数,可以使用类似的方法。

        希望这会有所帮助!

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-01
          • 1970-01-01
          • 2012-12-02
          • 2019-05-31
          • 1970-01-01
          • 2016-07-08
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多