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

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

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

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

3回答

1139閲覧

平方根関数を自作しています。Over flow errorがLeetCode内で出ます。

alizona

総合スコア126

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

1クリップ

投稿2021/09/24 03:47

編集2021/09/24 07:14

inputされたxの値の平方根をreturnする関数を作成しています。sqrt()や、**0.5は使えません。
LeetCode内で、Overflowエラーになります。アドバイスをいただきたいです。

問題のリンク(leetcode)

C

1int mySqrt(int x){ 2 3 int answer=0; 4 5 if(x<2)return x; 6 7 8 for(int i=1; i<x; i++){ 9 10 if(i*i>x){ 11 i=x; 12 }else{ 13 answer=i; 14 } 15 } 16 return answer; 17}

C

1Line 29: Char 13: runtime error: signed integer overflow: 46341 * 46341 cannot be represented in type 'int' [solution.c]

//このコードではなぜoverflowが起きないのでしょうか?

C

1int mySqrt(int x){ 2 3 double xn; 4 int i,n; 5 6 n=10; 7 xn=0; 8 9 do { /* 平方根に最も近い大きい方の整数を探す */ 10 11 xn+=1; 12 13 } while (xn*xn<x); 14 15 for (i=0;i<n;i++) { 16 xn=(xn+x/xn)/2; /* 交点のx 座標を求める */ 17 } 18 return xn; /* 求めた近似値を返す */ 19} 20

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

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

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

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

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

guest

回答3

0

アドバイスをいただきたいです。

スピード不問なら奇数列(1,3,5,7...)を引いていくのが単純かしら...

投稿2021/09/24 04:51

episteme

総合スコア16614

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

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

fana

2021/09/24 06:02

恥ずかしながら, 数分間ほど「これ何の話?」ってなっちゃいました…
rubato6809

2021/09/24 07:36

> スピード不問なら 質問者の方法と比較すると、ループ回数はほぼ同じでも、足し算・引き算だけで済むから、毎回二乗するより速いはずです。
alizona

2021/09/24 08:14

ピタゴラ数ですね。勉強になりました。
guest

0

ベストアンサー

※見間違いによる不要部分を取り消しています。

32ビットint型の最大値は2147483647です。
その上で、forループ内では解が出た後もx*xの計算が不必要に行われてしまうので、
46341以上の数値を引数xに渡すと、46341*46341が行われた時点でて2147488281となりint型の最大値を超えてしまいオーバーフローが発生してしまいます。

xの最大値を制限する必要もありますが、
そもそも解が出た時点でbreakでループを終了して無駄な計算をさせないようにすべきです。

c

1 for(int i=1; i<x; i++){ 2 if(i*i>x){ 3 break; 4 } 5 answer = i; 6 }

(訂正による追記)エラーとならないようにxの最大値を制限するすべきと思いますが、
このアルゴリズムのままLeetCodeで通すためにはilong longなどの型にして巨大な数値を扱えるようにする必要があると思います。

ちなみに効率良く平方根の近似値を求める方法の一つとしてニュートン法というのもありますので、ご参考ください。

投稿2021/09/24 04:21

編集2021/09/24 05:53
surface_0

総合スコア497

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

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

fana

2021/09/24 04:32

> forループ内では解が出た後もx*xの計算が不必要に行われてしまう… > そもそも解が出た時点でbreakでループを終了して無駄な計算をさせないようにすべき 質問者のコードでの i=x; というのは,breakの意味合いで書かれている(iの値を,forの繰り返し条件を満たさない値にする)のだと見えます. (まぁ,だったら break って書けよ,とは思いますが)
surface_0

2021/09/24 04:41 編集

そうでした。 すいません、ちょっと見当違いの回答書いてました。
alizona

2021/09/24 04:45

breakの使い方あやふやだったので、わざわざ代入させてました。break;でいいんですね。 ありがとうございます。 binary treeでやってるとこなんですが、どちらにせよx*xでオーバーフローしてるので、オーバーフローさせないアルゴリズムがニュートン法ということでしょうか?
fana

2021/09/24 04:45

i=x; の後は, for(int i=1; i<x; i++){ ... } の i++ によって iの値が x+1 になりますね. で,この時点で繰り返し条件 i<x を満たさないので,{...} のところはもはや実行されません. なので,素直に break と書いた場合との差としては, x++ i<x の判定 の2つを余計にやることになる,くらいでしょう.
surface_0

2021/09/24 04:52

不等号の見間違えというアホなことででちょっと回答内容がズレてしまいました。失礼しました。 すでに削除リクエストしてしまっているので、コメント消えてしまうかもしれません。。。 ニュートン法だとxより大きな値は出てこないので、オーバーフローの心配が無いですが、割り算で少数を扱うことになるので、どちらが効率がいいかは場合によりけりです。
fana

2021/09/24 04:52

> どちらにせよx*xでオーバーフローしてるので 素朴な疑問なのですが, 「2乗計算がフローするなら,しなきゃいいんじゃないの?」っていう話が既に別の方の回答に書かれているわけですが,それでは何かがダメなんでしょうか?
alizona

2021/09/24 04:54

数値Aを2乗しないで、どうやって数値Aがxの平方根だと証明するのですか?
fana

2021/09/24 04:58

素人考えですが, if( i*i >x ){ ... } っていうのがやりたいけども i*i がフローする,という話であれば,例えば if( i > (x/i) ){ ... } とか if( 1 > ((x/i)/i) ){ ... } とかにしたらダメなのか?(それだと支障が出ることがあるのか?) という.
alizona

2021/09/24 05:11

普通にleetcodeにsubmit したら通りましたw。ありがとうございます。 ニュートン法とか、バイナリーツリーとかの使用を意図して作られた問題かどうかは知らないですが、そっちも把握したいので勉強してみます。
alizona

2021/09/24 05:12

Runtime: 68 ms, faster than 5.29% of C online submissions for Sqrt(x). Memory Usage: 5.5 MB, less than 92.12% of C online submissions for Sqrt(x).
fana

2021/09/24 05:36

昔,平方根を求める手続きの話をどこかで見たよなぁ… と思って探したら Babylonian Method という名前で, 手続きはニュートン法と完全一致だった.
surface_0

2021/09/24 05:44

> if( 1 > ((x/i)/i) ){ ... } とかにしたらダメなのか? 割り算で絞り込んでいくというのはまさにニュートン法に近い考え方ですね。 回答削除は却下されましたので、ノイズになってしまう部分は取り消し編集しておきました。 お騒がせ致しましたが、今後の参考になれば幸いです。
alizona

2021/09/24 07:18 編集

ニュートン法でもやったのですが、ニュートン法では } while (xn*xn<x); を使ってもover flowにな離ませんでした。 whileで xnがxを超えるまでループしてるので、overflowになると思ったのですが。なぜなんでしょうか? codeは質問文の最後に載せました。
rubato6809

2021/09/24 07:41

> 数値Aを2乗しないで、どうやって数値Aがxの平方根だと証明する たとえば「平方数 奇数の和」で検索してみるとか。
surface_0

2021/09/24 07:53 編集

引数のxが上限となっているため、xn*xnは最大でもその付近の値で終了するので、大抵はオーバーフローしません。 逆に言えばxがint最大値に近いとオーバーフローになることはあり得ます。
alizona

2021/09/24 07:59

while (xn*xn<x); この文では、while(xn*(xn < x)); つまり、xnとxの比較が先に行われてますか?
surface_0

2021/09/24 08:04

優先度は < より * の方が高いので while((xn*xn) < x)と同等です。 逆に while(xn*(xn < x)) だと意味が分からなくなると思います。
alizona

2021/09/24 08:05

変なこと言いました。 while(xn*xn<x){ で xn*xn が x よりも大きい場合には、whileループを抜けますが、whileループを抜けるかどうかを判断した時にover flowが起こっているのではないですか? 最初に質問を投稿した時のコードでは、if(i*i>x){ break; としているのですが、何が違うのですか?
fana

2021/09/24 08:05 編集

> int型の最大値を超えてしまいオーバーフローが発生してしまいます っていう話をしてたのに > double xn; って,型が変わってるやん,っていう.
alizona

2021/09/24 08:10

最初の質問した時点のコードをdouble にしたらover flow なしに提出できました。
alizona

2021/09/24 08:12

みなさんありがとうございました。 2乗せずに平方根を見つける方法、ニュートン法、ピタゴラ数、int doubleの最大値と色々勉強になりました。
fana

2021/09/24 08:17

「そもそもの問題が何だったのか?」をちゃんと把握できてるんですかね? 「intとintとで計算した結果値の型はintってことにするぜ!」っていうルールがまずあって, なんだけども,あなたのコードを実行したら計算した結果の値がintで表せる範囲を超えちゃったから, これどうなん? やばくね? マジムリ って話になってたわけね. int型であることをやめちゃったら当然この話は変わってくるよね. --- で, ・型自体を変えたら? ・フローしちゃう掛け算自体をやめたら? の両方が maisumakun 氏によって真っ先に回答されてるわけで.
alizona

2021/09/24 08:42

フローしちゃう掛け算自体をやめたら?こっちを先に解決しようとしてて、型を変えたら?というアドバイスを忘れていました。returnする型がint ってこと以外は決まりはありませんでした(leet code内で)
surface_0

2021/09/24 08:44

> 最初に質問を投稿した時のコードでは、if(i*i>x){ break; としているのですが、何が違うのですか? このコードだと整数部に関してはやってることはまったく同じだと思います。 型がdoubleになって許容値が大きくなったのでオーバーフローしなくなりました。 すいません、自分も的外れな事言って失礼しました。 結局今回の問題では整数部だけ返せばいいようなので、小数部を計算する後半は必要ではないと思います。 ただ、自分が想像していたニュートン法は2乗する式が出てこないものだったので、そのあたりの区別まで考えていませんでした。
alizona

2021/09/24 08:45

Since the return type is an integer, the decimal digits are truncated, and only the integer part of the result is returned. return も doubleで良かったみたいです。ただ、2乗せずに平方根を見つける方法、ニュートン法、ピタゴラ数などと教えていただいたことを理解したかったので、どちらも理解できて良かったです。
guest

0

エラーメッセージのとおりです。このままでは、2**31-1のような巨大な数を引数とした場合に、intからオーバーフローします。

対策としては、

  • 途中の計算にlong longを使って、溢れないように変数の幅を広げておく
  • i*i>xの条件判定を変えて、掛け算せずにループ範囲に収まるかをチェックする

投稿2021/09/24 03:56

maisumakun

総合スコア145208

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

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

alizona

2021/09/24 04:56

ありがとうございます。やってみます。
maisumakun

2021/09/24 08:05

> このコードではなぜoverflowが起きないのでしょうか? 一般に、doubleの値の範囲はintより大きいです。
alizona

2021/09/24 08:12

ありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.46%

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

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

質問する

関連した質問