检查一个数字是否可以被3整除

时间:2009-05-10 07:22:00

标签: puzzle division modulo

编写代码以确定一个数字是否可以被3整除。该函数的输入是单个位,0或1,如果到目前为止收到的数字是输出,则输出应为1可被3整除的数字的二进制表示,否则为零。

示例:

input  "0":       (0)  output 1
inputs "1,0,0":   (4)  output 0
inputs "1,1,0,0": (6)  output 1

这是基于面试问题。我要求绘制逻辑门,但由于这是stackoverflow,我会接受任何编码语言。硬件实现的奖励点(verilog等)。

Part a(easy):第一个输入是MSB。

B部分(稍微难一点):第一个输入是LSB。

C部分(困难):哪一个更快更小,(a)或(b)? (理论上不是Big-O意义上的,但实际上更快/更小。)现在采用更慢/更大的一个,并使其快/小与更快/更小的一样。

10 个答案:

答案 0 :(得分:27)

通过交替地加上和减去其十进制数字,确定一个数是否是11的倍数有一个相当着名的技巧。如果你在最后得到的数字是11的倍数,那么你开始时的数字也是11的倍数:

47278    4 - 7 + 2 - 7 + 8 = 0, multiple of 11     (47278 = 11 * 4298)
52214    5 - 2 + 2 - 1 + 4 = 8, not multiple of 11 (52214 = 11 * 4746 + 8)

我们可以对二进制数应用相同的技巧。当且仅当其位的交替总和也是3的倍数时,二进制数是3的倍数:

4   = 100       1 - 0 + 0 = 1, not multiple of 3
6   = 110       1 - 1 + 0 = 0, multiple of 3
78  = 1001110   1 - 0 + 0 - 1 + 1 - 1 + 0 = 0, multiple of 3
109 = 1101101   1 - 1 + 0 - 1 + 1 - 0 + 1 = 1, not multiple of 3

无论是从MSB还是LSB开始都没有区别,因此以下Python函数在两种情况下都能正常工作。它需要一个迭代器一次返回一个位。 multiplier在1和2之间交替而不是1和-1,以避免采用负数的模数。

def divisibleBy3(iterator):

    multiplier = 1
    accumulator = 0

    for bit in iterator:
        accumulator = (accumulator + bit * multiplier) % 3
        multiplier = 3 - multiplier

    return accumulator == 0

答案 1 :(得分:20)

这里......新的东西...如何检查任何长度(甚至数千个数字)的二进制数是否可以被3整除。

divisible-by-3 state machine

-->((0))<---1--->()<---0--->(1)        ASCII representation of graph

从图片中可以看出。

  1. 你从双圈开始。
  2. 当你得到一个或零时,如果数字在圆圈内,那么你留在那个圆圈里。但是,如果数字在一条线上,那么你就行了。
  3. 重复步骤2,直到所有数字都被填充。
  4. 如果你最终以双圈结束,那么二进制数可以被3整除。
  5. 您也可以使用它来生成可被3整除的数字。我不会想象将其转换为电路很难。

    使用图表的示例...

    11000000000001011111111111101可以被3整除(再次以双圈结束)

    亲自试试。

    在将二进制数转换为基数为10时,您也可以执行类似的操作来执行MOD 10。 (10个圆圈,每个圆圈加倍圈出,表示模数得到的值0到9)

    编辑:这是针对从左到右运行的数字,虽然修改有限状态机以接受反向语言并不困难。

    注意:在图形的ASCII表示中()表示单个圆圈,(())表示双圆圈。在有限状态机中,这些被称为状态,双圆是接受状态(意味着它最终可被3整除的状态)

答案 2 :(得分:11)

LSB的状态表:

S I S' O
0 0 0  1
0 1 1  0
1 0 2  0
1 1 0  1
2 0 1  0
2 1 2  0

说明:0可被3整除。 0 << 1 + 0 = 0。如果S = (S << 1 + I) % 3,请重复使用O = 1S == 0

MSB状态表:

S I S' O
0 0 0  1
0 1 2  0
1 0 1  0
1 1 0  1
2 0 2  0
2 1 1  0

说明:0可被3整除。 0 >> 1 + 0 = 0。如果S = (S >> 1 + I) % 3,请重复使用O = 1S == 0

S'与上述不同,但O的作用相同,因为S'对于相同的情况(00和11)为0。由于在两种情况下O都相同,O_LSB = O_MSB,所以要使MSB像LSB一样短,反之亦然,只需使用两者中最短的。

答案 3 :(得分:8)

这是一种手工操作的简单方法。 由于1 = 2 2 mod 3,对于每个正整数,我们得到1 = 2 2n mod 3。 此外,2 = 2 2n + 1 mod 3.因此,可以通过计算奇数位位置的1位来确定整数是否可被3整除,将该数乘以2,加1的数量偶数位的位将它们添加到结果中并检查结果是否可被3整除。

示例:57 10 = 111001 2 。 奇数位有2位,偶数位有2位。 2 * 2 + 2 = 6可被3整除。因此,57可被3整除。

这也是解决问题c)的想法。如果一个反转二进制整数的位顺序,则所有位保持在偶数/奇数位置或所有位都改变。因此,反转整数n的位的顺序结果是一个整数,当且仅当n可被3整除时,该整数可以被3整除。因此问题a)的任何解决方案都可以在不改变问题b)的情况下工作,反之亦然。嗯,也许这有助于弄清楚哪种方法更快......

答案 4 :(得分:7)

您需要使用算术模3进行所有计算。这就是

的方式

MSB:

number=0
while(!eof)
    n=input()
    number=(number *2 + n) mod 3

if(number == 0)
    print divisible

LSB:

number = 0;
multiplier = 1;
while(!eof)
    n=input()
    number = (number + multiplier * n) mod 3
    multiplier = (multiplier * 2) mod 3

if(number == 0)
   print divisible

这是一般的想法......

现在,您的目的是了解为什么这是正确的。

是的,自己做作业;)

答案 5 :(得分:4)

这个想法是这个数字可以任意增长,这意味着你不能在这里使用mod 3,因为你的数字将超出你的整数类的容量。

想法是注意数字会发生什么。如果你在右边添加位,你实际做的是向左移一位并添加新位。

Shift-left与乘以2相同,添加新位是添加0或1.假设我们从0开始,我们可以根据最后一个数字的模3进行递归。

last | input || next | example
------------------------------------
0    | 0     || 0    | 0 * 2 + 0 = 0
0    | 1     || 1    | 0 * 2 + 1 = 1
1    | 0     || 2    | 1 * 2 + 0 = 2
1    | 1     || 0    | 1 * 2 + 1 = 0 (= 3 mod 3)
2    | 0     || 1    | 2 * 2 + 0 = 1 (= 4 mod 3)
2    | 1     || 2    | 2 * 2 + 1 = 2 (= 5 mod 3)

现在让我们看看当你向左边添加一些内容时会发生什么。首先,请注意:

2 2n mod 3 = 1

2 2n + 1 mod 3 = 2

所以现在我们必须根据当前迭代是奇数还是偶数来为mod添加1或2。

last | n is even? | input || next | example
-------------------------------------------
d/c  | don't care | 0     || last | last + 0*2^n = last
0    | yes        | 1     || 0    | 0 + 1*2^n = 1 (= 2^n mod 3)
0    | no         | 1     || 0    | 0 + 1*2^n = 2 (= 2^n mod 3)
1    | yes        | 1     || 0    | 1 + 1*2^n = 2
1    | no         | 1     || 0    | 1 + 1*2^n = 0 (= 3 mod 3)
1    | yes        | 1     || 0    | 2 + 1*2^n = 0
1    | no         | 1     || 0    | 2 + 1*2^n = 1

答案 6 :(得分:0)

实际上LSB方法实际上会使这更容易。在C:

MSB方法:

/* 
Returns 1 if divisble by 3, otherwise 0
Note: It is assumed 'input' format is valid
*/
int is_divisible_by_3_msb(char *input) {
  unsigned value = 0;
  char *p = input;
  if (*p == '1') {
    value &= 1;
  }
  p++;
  while (*p) {
    if (*p != ',') {
      value <<= 1;
      if (*p == '1') {
        ret &= 1;
      }
    }
    p++;
  }
  return (value % 3 == 0) ? 1 : 0;
}

LSB方法:

/* 
Returns 1 if divisble by 3, otherwise 0
Note: It is assumed 'input' format is valid
*/
int is_divisible_by_3_lsb(char *input) {
  unsigned value = 0;
  unsigned mask = 1;
  char *p = input;
  while (*p) {
    if (*p != ',') {
      if (*p == '1') {
        value &= mask;
      }
      mask <<= 1;
    }
    p++;
  }
  return (value % 3 == 0) ? 1 : 0;
}

我个人很难相信其中一个会与另一个明显不同。

答案 7 :(得分:0)

input  "0":       (0)  output 1
inputs "1,0,0":   (4)  output 0
inputs "1,1,0,0": (6)  output 1

这个最后的输入不应该是12,还是我误解了这个问题?

答案 8 :(得分:-1)

我认为Nathan Fellman在a和b部分的正确轨道上(除了b需要一个额外的状态:你需要跟踪你的数字位置是奇数还是偶数)。

认为 C部分的技巧是在每一步都否定last值。即0变为0,1变为2,2变为1。

答案 9 :(得分:-1)

如果数字的总和可以被3整除,则数字可以被3整除。

所以你可以添加数字并得到总和:

  • 如果总和大于或等于10,则使用相同的方法
  • 如果它是3,6,9那么它是可分的
  • 如果总和不同于3,6,9那么它就不可分割