web-dev-qa-db-ja.com

数値が3で割り切れるかどうかを確認します

数値が3で割り切れるかどうかを判断するコードを記述します。関数への入力はsingleビット、0または1であり、これまでに受け取った数値がのバイナリ表現である場合、出力は1である必要があります。 3で割り切れる数、それ以外の場合はゼロ。

例:

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

これはインタビューの質問に基づいています。論理ゲートの描画をお願いしますが、これはスタックオーバーフローであるため、任意のコーディング言語を受け入れます。ハードウェア実装(verilogなど)のボーナスポイント。

パートa(簡単):最初の入力はMSBです。

パートb(少し難しい):最初の入力はLSBです。

パートc(難しい):どちらが速くて小さいですか、(a)または(b)? (理論的にはBig-Oの意味ではありませんが、実際には高速/小型です。)次に、低速/大型のものを使用して、高速/小型のものと同じくらい高速/小型にします。

17
Eyal

ふふ

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 = 0O = 1の場合は、S = (S << 1 + I) % 3S == 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 = 0O = 1の場合は、S = (S >> 1 + I) % 3S == 0を使用して繰り返します。

S'は上記とは異なりますが、同じケース(00と11)でS'が0であるため、Oは同じように機能します。 Oはどちらの場合も同じであるため、O_LSB = O_MSBです。したがって、MSBをLSBと同じくらい短くするには、またはその逆にするには、両方の最短を使用します。

11
Tordek

10進数を交互に加算および減算することにより、数値が11の倍数であるかどうかを判断するためのかなりよく知られたトリックがあります。最後に取得する数が11の倍数である場合、最初に取得した数も11の倍数です。

 47278 4-7 + 2-7 + 8 = 0、11の倍数(47278 = 11 * 4298)
 52214 5-2 + 2-1 + 4 = 8、11の倍数ではない(52214 = 11 * 4746 + 8)

同じトリックを2進数に適用できます。 2進数は、そのビットの交互の合計も3の倍数である場合に限り、3の倍数です。

 4 = 100 1-0 + 0 = 1、3の倍数ではない
 6 = 110 1-1 + 0 = 0、3の倍数
 78 = 1001110 1-0 + 0 --1 + 1 -1 + 0 = 0、3の倍数
 109 = 1101101 1 --1 + 0 -1 + 1-0 + 1 = 1、3の倍数

MSBとLSBのどちらから始めても違いはないため、次のPython関数は、どちらの場合も同じように機能します。ビットを1つずつ返すイテレータが必要です。multiplierは、負の数のモジュロを取ることを避けるために、1と-1ではなく1と2を交互に繰り返します。

def divisibleBy3(iterator):

    multiplier = 1
    accumulator = 0

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

    return accumulator == 0
27
Luke Woodward

ここで...何か新しい...任意の長さ(数千桁でも)の2進数が3で割り切れるかどうかを確認する方法。

divisible-by-3 state machine

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

写真から。

  1. あなたは二重の円から始めます。
  2. 1または0を取得したときに、数字が円の内側にある場合は、その円にとどまります。ただし、数字が線上にある場合は、線を横切って移動します。
  3. すべての桁が消費されるまで、手順2を繰り返します。
  4. 最終的に二重円になってしまうと、2進数は3で割り切れます。

これを使用して、3で割り切れる数を生成することもできます。これを回路に変換するのは難しいとは思いません。

グラフを使用した1つの例...

11000000000001011111111111101は3で割り切れます(再び二重円になります)

自分で試してみてください。

2進数を基数10の数値に変換する場合に、MOD10を実行するための同様のトリックを実行することもできます。 (10個の円、それぞれが二重の円で囲まれ、モジュロから得られる0から9の値を表します)

EDIT:これは左から右に実行される数字用ですが、逆言語を受け入れるように有限状態マシンを変更することは難しくありません。

注:ASCIIグラフの表現では、()は単一の円を示し、(())は二重の円を示します円。有限状態マシンでは、これらは状態と呼ばれ、二重円は受け入れ状態(最終的に3で割り切れる状態を意味する状態)です。

20
clinux

手作業で行う簡単な方法は次のとおりです。 1 = 2なので2 mod 3、1 = 2を取得します2n すべての正の整数に対してmod3。さらに2 = 22n + 1 mod 3.したがって、奇数ビット位置の1ビットを数え、この数に2を掛け、偶数ビット位置の1ビットの数を加算して結果に加算し、結果にそれらを加算して、整数が3で割り切れるかどうかを判断できます。結果は3で割り切れます。

例:5710= 1110012。奇数の位置に2ビット、偶数の位置に2ビットがあります。 2 * 2 + 2 = 6は3で割り切れます。したがって、57は3で割り切れます。

質問c)を解決するための考えもここにあります。 2進整数のビット順序を逆にすると、すべてのビットが偶数/奇数の位置に留まるか、すべてのビットが変化します。したがって、整数nのビットの順序を逆にすると、nが3で割り切れる場合に限り、3で割り切れる整数になります。したがって、質問a)の解は、質問b)の変更なしで機能し、その逆も同様です。うーん、多分これはどちらのアプローチが速いかを理解するのに役立つかもしれません...

8
Accipitridae

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

これは一般的な考え方です...

さて、あなたの部分は理解することですなぜこれは正しいです。

そして、はい、自分で宿題をします;)

7
Artyom

数値は任意に長くなる可能性があるという考え方です。つまり、整数クラスの容量を超えて数値が大きくなるため、ここではmod 3を使用できません。

アイデアは、番号に何が起こるかに気付くことです。右にビットを追加する場合、実際に行っているのは、左に1ビットシフトして、新しいビットを追加することです。

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)

次に、少し左に追加するとどうなるか見てみましょう。まず、次のことに注意してください。

22n mod 3 = 1

そして

22n + 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
4
Nathan Fellman
input  "0":       (0)  output 1
inputs "1,0,0":   (4)  output 0
inputs "1,1,0,0": (6)  output 1

この最後の入力は12であるべきではありませんか、それとも私は質問を誤解していますか?

0
jim

実際、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;
}

個人的には、これらの1つが他の1つと大幅に異なるとは信じられません。

0
cletus