質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

Q&A

解決済

4回答

2327閲覧

剰余演算を使わずに最大公約数を求めるには

syoutaro22

総合スコア1

Python

Pythonは、コードの読みやすさが特徴的なプログラミング言語の1つです。 強い型付け、動的型付けに対応しており、後方互換性がないバージョン2系とバージョン3系が使用されています。 商用製品の開発にも無料で使用でき、OSだけでなく仮想環境にも対応。Unicodeによる文字列操作をサポートしているため、日本語処理も標準で可能です。

0グッド

1クリップ

投稿2021/02/02 22:24

編集2021/02/04 08:41

前提・実現したいこと

プログラミング超初心者で初めて質問させていただきました。
題名にもある通り剰余計算を使わずに2変数x,yの最大公約数を求めるにはどのようにすればよいですか?
最大公約数をgcd(x,y)とし、
x、yがともに偶のとき,gcd(x,y)→2*gcd(x/2,y/2)
xが奇,yが偶のとき、gcd(x,y)→gcd(x,y/2)
x,yどちらも奇のとき、gcd(x,y)→gcd((x,y)/2,y)
という要領で計算します。
できれば演算子%を使わずにお願いします。
ここに質問の内容を詳しく書いてください。

発生している問題・エラーメッセージ

def gcd(x, y): if y==0: return x elif x&1==0 and y&1==0 and y!=0: x, y=x//2, y//2 return 2*gcd(x, y) elif x&1==0 and y&1==1 and y!=0: x=x//2 return gcd(x, y) elif x&1==1 and y&1==0 and y!=0: y=y//2 return gcd(x, y) elif x&1==1 and y&1==1 and x<y and y!=0: x, y=x, (y-x)//2 return gcd(x, y) else: x, y=(x-y)//2, y return gcd(x, y) print(f'gcd(123456, 65432)={gcd(123456, 65432)}') に対してエラーが出ます。

該当のソースコード

python

試したこと

自分なりにコードを書いたのですが初歩的なところでつまずいているのかもしれずエラーコードを見ても分かりません。
とてもレベルの低い質問かもしれませんが、回答してくださればと思います。
ここに問題に対して試したことを記載してください。

補足情報(FW/ツールのバージョンなど)

ここにより詳細な情報を記載してください。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/02/02 22:28

「剰余計算を使わずに」とありますが、他に禁止されているものはありますか?
syoutaro22

2021/02/02 22:32 編集

自分は再帰を使って書こうと思うので再帰を使っていただけたらなと 加えてx、yは正の整数が前提です。
maisumakun

2021/02/02 22:38

> 自分は再帰を使って書こうと思うので再帰を使っていただけたらなと 剰余演算を使っても再帰で書けますが、他に条件はないのですか?
syoutaro22

2021/02/02 22:42

x、yの大小は決まってなくて、時間はo(logx)でという条件があります。
maisumakun

2021/02/02 23:41

我流で実装を行うのであれば、タイトルの「ユークリッドの互除法」は不適当です。消しておきましょう。
syoutaro22

2021/02/02 23:48

申し訳ないです。修正させていただきました。
tiitoi

2021/02/03 01:51

なぜ質問を消したのでしょうか?
guest

回答4

0

ベストアンサー

アルゴリズムについて

最大公約数をgcd(x,y)とし、
x、yがともに偶のとき,gcd(x,y)→2*gcd(x/2,y/2)
xが奇,yが偶のとき、gcd(x,y)→gcd(x,y/2)
x,yどちらも奇のとき、gcd(x,y)→gcd((x,y)/2,y)
という要領で計算します。

これが間違っています。
まず、互除法をちゃんと理解しましょう。

gcd((x-y)/2,y)が正しいとのことなので、互除法はわかっていますね。

プログラムのデバッグについて

突然、大きな数字でやるのではなく、小さな数字から始めましょう。
2と3の最大公約数は求められるでしょうか。

python

1def gcd(x, y): 2 if y==0: 3 return x 4 elif x&1==0 and y&1==0 and y!=0: 5 x, y=x/2, y/2 6 return 2*gcd(x, y) 7 elif x&1==0 and y&1==1 and y!=0: 8 x=x/2 9 return gcd(x, y) 10 elif x&1==1 and y&1==0 and y!=0: 11 y=y/2 12 return gcd(x, y) 13 elif x&1==1 and y&1==1 and x<y and y!=0: 14 x, y=x, (y-x)/2 15 return gcd(x, y) 16 else: 17 x, y=(x-y)/2, y 18 return gcd(x, y) 19 20print(gcd(2,3))

実行するとエラーが出ますね。

python

1> python gcd.py 2Traceback (most recent call last): 3 File "gcd.py", line 20, in <module> 4 print(gcd(2,3)) 5 File "gcd.py", line 9, in gcd 6 return gcd(x, y) 7 File "gcd.py", line 4, in gcd 8 elif x&1==0 and y&1==0 and y!=0: 9TypeError: unsupported operand type(s) for &: 'float' and 'int'

4行目、つまり elif x&1==0 and y&1==0 and y!=0:のところで、float型とint型で&はできませんというエラーメッセージが出ています。これは理解できますか。

自力で解決できたようなので、いくつかのサンプルを載せておきます。

syoutaro22さんのアルゴリズムですが、これは互減法のアルゴリズムに2だけ割るアルゴリズムを付け加えたものです。こういう発想はプロ的なのですが、syoutaro22さんは初心者ということですので、たまたま思いつけたということなのでしょう。一般的には、数字が小さいときには互減法は互除法より高速なのです。python3の場合は、64ビットを超えると整数の割り算が極端に遅くなるので、その場合も有効かもしれません。

互減法は、割り算機能のついていない組み込み用の小さなCPUなどでも使います。
互減法については最大公約数をもっと高速に求めるなどを見てみてください。

python

1def gcd(x, y): 2 if y==0: 3 return x 4 elif x < y: 5 return gcd(y, x) #こうやっておいたほうが間違いが少ない。 6 elif x&1==0 and y&1==0: 7 return 2*gcd(x//2, y//2) 8 elif x&1==0 and y&1==1: 9 return gcd(x//2, y) 10 elif x&1==1 and y&1==0: 11 return gcd(x, y//2) 12 elif x&1==1 and y&1==1: 13 return gcd(x-y, y) # 大きいほうから小さいほうを引く 14 else: 15 print('proguram error') # ここに来るならプログラムの間違い 16 raise(Exception)

一方、互除法でやるには、y_waiwaiさんが勧めていたようにやります。以下をご覧下さい。

python

1def gcd(x, y): 2 if x < y: 3 return gcd(y, x) 4 elif y==0: 5 return x 6 elif x == x // y*y: 7 return y 8 else: 9 return gcd(x-y, y)

投稿2021/02/02 23:12

編集2021/02/03 01:42
ppaul

総合スコア24670

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

syoutaro22

2021/02/02 23:20

理解できました。
ppaul

2021/02/02 23:32

見直していて、gcd((x,y)/2,y)が意味不明であることに気が付きました。 これは何を意味していますか?
syoutaro22

2021/02/02 23:40

ごめんなさい、、 最初の説明のgcd((x,y)/2,y)は間違いです。 正しくはgcd((x-y)/2,y)です。 申し訳ないです。
ppaul

2021/02/02 23:54

なるほど、それならなら互除法はわかっていますね。 ひょっとしてアセンブラの互除法のアルゴリズムを考えるためにpythonとかで組んでみなさいという課題なのかな。だとしたら、剰余だけじゃなく、割り算も使っちゃだめですね。2による割り算をシフトで済ませれば、割り算なしでできるし、アセンブラレベルで考えれば、そのほうが高速にできますね。
syoutaro22

2021/02/03 00:01

この考え方で組んでみたのですが、コードの書き方が悪いのか、考え方が間違っているのかが分かりません。
ppaul

2021/02/03 00:24 編集

考え方は、(無駄は多いですが)ほぼ合っています。 今のままだと、整数型割り算を使っても、無限ループに入る可能性があります。 問題は、x-yが負の数になった場合をどう考えるかです。 それさえクリアすれば動きますよ。 そこまで行ったら、無駄のないコードを回答に追加しますので、やってみてください。
syoutaro22

2021/02/03 00:32

ifの前に if x<y: x,y=y,x を付け加える。 で合っているでしょうか?
ppaul

2021/02/03 00:40

そう思ってやってみたら、gcd(9,3)が無限ループに入りました。 これはアルゴリズムの問題かな。
ppaul

2021/02/03 00:48

いや、元のプログラムの/を//に変えて、 ifの前に if x<y: x,y=y,x を付け加える。 なら動きますね。 あとで、私が修正したプログラムにコメントを付けて載せます。
syoutaro22

2021/02/03 00:49

自分の書いたコードに if x<y: x,y=y,x を付け加えたらエラーが出ずに計算できました。
syoutaro22

2021/02/03 00:51

了解しました。 長々とした質問に回答してくださりありがとうございました。
guest

0

まず、プログラムを実行するとエラーになります。
割り算を使っているため、変数がfloatと判断されているようです。
ビットシフトなどに変更しないとだめです。
がしかし、ある意味、bit演算も剰余演算だと思います。

剰余演算を使わずにGCDを求めるには、以下の公式を使えばよいと思います。
GCDは以下の公式が成り立ちます。

GCD(a, b) = GCD(a - b, b) ここで、a > b

これを使えば、引き算と数の大小比較でGCDを求めることができると思います。

投稿2021/02/03 00:06

akiruno-oneone

総合スコア815

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

x、yがともに偶のとき,gcd(x,y)→2*gcd(x/2,y/2)

xが奇,yが偶のとき、gcd(x,y)→gcd(x,y/2)
x,yどちらも奇のとき、gcd(x,y)→gcd((x,y)/2,y)
という要領で計算します。

ユークリッドの互除法は偶数でも奇数でも関係なく適用できますので、このような場合分けは冗長なだけです。

投稿2021/02/02 22:52

maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

maisumakun

2021/02/02 22:56

ユークリッドの互除法の本質とは関係ない、「できれば演算子%を使わず」といった条件や、上の場合分けなどを考えると、(普通にコードを書くことはできても)「syoutaro22さんの期待するコードがどんなものなのかわからない」というのが正直なところです。
syoutaro22

2021/02/02 23:00

剰余演算を使うと楽に計算できるのは重々承知ですが、このプログラミングでエラーがでる理由だけでも教えていただければと思います。
maisumakun

2021/02/02 23:01

どのようなエラーメッセージが出たのですか?
maisumakun

2021/02/02 23:02 編集

> 剰余演算を使うと楽に計算できるのは重々承知ですが それとは無関係に、偶数か奇数かの場合分けをして処理するようなものは「ユークリッドの互除法」とは呼びません。
syoutaro22

2021/02/02 23:04

以下のようなエラーが出ました。 unsupported operand type(s) for &: 'float' and 'int'
maisumakun

2021/02/02 23:08

Python 3の/演算子は結果をfloatで返します。
syoutaro22

2021/02/02 23:11

つまり//演算子を使うということでしょうか?
maisumakun

2021/02/02 23:17

とりあえずは、そうですね。
maisumakun

2021/02/02 23:20

まずもってですが、ゴールはどこなのでしょうか。 正しく「ユークリッドの互除法」を実装したいのか、それとも「場合分けなどを入れた我流のアルゴリズム」に進みたいのか、そこをはっきりさせてください。
syoutaro22

2021/02/02 23:27

学校の授業の問題の一つで、 def euclid_itr(x, y): while y != 0: x, y = y, x % y return x print(euclid_itr(6,9)) のようなものを習ったのですが、今度は再帰でなおかつ剰余を使わないで書いてみるとどうなるかとなったので、試したところエラーが出て質問しました。つまり早くはない我流のやり方です。
maisumakun

2021/02/02 23:29 編集

> 今度は再帰でなおかつ剰余を使わないで書いてみるとどうなるかとなったので 剰余を使わなくてもユークリッドの互除法は実装できますが、あえて場合分けを取り入れたいのですか?
maisumakun

2021/02/02 23:36 編集

でしたら、もうコメントすることはありません(というか、できません)。 通常の互除法ではない以上、これ以降、どのようにアルゴリズムを組んでいくかについて、 syoutaro22さんの構想がわからないので、自分で頑張ってください。
syoutaro22

2021/02/02 23:41

分かりました。 いろいろ説明不足ですいませんでした。 回答いただきありがとうございました。
guest

0

整数で除算した結果をそのまま掛けて、元の数値と比較すれば、割り切れてるのかどうかは見れますね

投稿2021/02/02 22:30

y_waiwai

総合スコア88042

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問