[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [bep] 割り算の答えを小数で出すには? <Re: learning lisp: while
- To: bep@argv.org
- Subject: Re: [bep] 割り算の答えを小数で出すには? <Re: learning lisp: while
- From: TAKAHASHI Naoto <ntakahas@m17n.org>
- Date: Thu, 12 Jul 2001 17:18:21 +0900 (JST)
- Delivered-To: mailing list bep@argv.org
- Mailing-List: contact bep-help@argv.org; run by ezmlm
- User-Agent: SEMI/1.14.3 (Ushinoya) FLIM/1.14.2 (Yagi-Nishiguchi) APEL/10.2 Emacs/21.0.104 (sparc-sun-solaris2.8) MULE/5.0 (SAKAKI)
ここ数日、英語論文の校閲と、Cプログラムの検証に明け暮れているn高橋です。
Reiko TAKAHASHI (高橋玲子)writes:
> 昼間&放課後がちょっとだけ立て込んでいたのと、夜遅くスターバックスのアイ
> スグランデラテを一気飲みしておなかを壊したのと、夜よりは強いはずの朝なの
> に全然起きられなかったのと……いろいろあって遅くなりました。
自発的にやってるんですから、ゆっくり行きましょう。
> ちゃんと覚えてはいないのですが、最初、ここを
> (and (> x 3) (/= (% x 2) 0))
> にしたらおかしなことになって、もう時間もないのでどさくさで(or)にしたら一
> 応それらしい答えを表示してくれたので、「えい、これでいいや!」とポストし
> てしまいました。
> でも、どうして(and)だとだめだったんでしょう……? 今ゆっくり見てもわかり
> ません。
while は、条件が成立し続ける間だけループを繰り返すからです。つまり、一
度条件が成立しなくなると、その時点でループは終了します。たとえ後もう一
回まわればまた成立するような場合でも同じです。確認のため、r高橋版プロ
グラムの or を and に変えたものを下に示します。
(let ((x 1000) (y 3))
(while
(and (> x 3) (/= (% x 2) 0))
(while
(and (<= y (+ (/ x 2) 1))
(/= (% x y) 0))
(setq y (+ y 1))
(when (= y (+ (/ x 2) 1))
(insert (format "%s\n" x))))
(setq x (- x 1))
(setq y 3))
(insert (format "%s\n" 3))
(insert (format "%s\n" 2)))
xは最初1000で、これは (and (> x 3) (/= (% x 2) 0)) を満たします。次にx
が1だけ減って998になると、(> x 3) は満たされても (/= (% x 2) 0) は満た
されません。ですからここでループが終了してしまい、残の数はチェックされ
なくなってしまうのです。
> } それから他のスレッドで話題になっていますが、y で割り切れるかどうかチェッ
> } クするときの上限値は (+ (/ x 2) 1) ではなくて、(sqrt x) です。sqrt は
> } square root (平方根) の略です。x が2以上の場合、(sqrt x) は常に
> } (+ (/ x 2) 1) よりも小さくなりますから、ループを回る回数をさらに減らす
> } ことができます。
> これ、どうしてそう言えてしまうのかが未だによくわかりません。
> 割る数が平方根になったとき、答えも平方根になるから、それ以上大きな数で割
> っても、前に割ったことのある小さな数が答えになるだけで、結局同じことを二
> 度やってしまっている……ということでしょうか??
> ……あっ、それなら今なんとなくわかったような気が……。
そうです。たとえば12を2個の正の整数の積で表すことを考えてみます。この
場合可能な組み合わせは 1*12, 2*6, 3*4, 4*3, 6*2, 12*1 の6通りです。が、
掛算は*記号の右と左を入れ替えても答が同じになりますから、実際には*記号
の左側の数が右側の数より大きくなったら、もうそれ以上調べる必要はありま
せん。
> でも、平方根って整数ではないですよね。その場合は、切り上げした整数を使え
> ばいいですか?
切り上げまで計算する必要はありません。切り捨てで充分です。今、素数かど
うか調べたい数がnで、nの平方根の切り上げをi、さらに n/i=x とします。最
後の式を変形すると n=x*i ですが、iがnの平方根より大きいのですから、xは
当然nの平方根より小さくなります。つまりたとえxが整数であっても、もうす
でに調べてあることになりますし、xが整数でなかったらそもそも調べる必要
ありません。
nが12のときの例で言うと、ルート12は約3.46です。これを切り上げると4にな
りますが、上で見たように切り捨ての3まで調べれば充分なことがわかります。
> 平方根を整数にするには、どうすればいいでしょう?(って、みなさんが書いた
> のを見ればいいですね(^^;;;))。
(floor (sqrt x)) ですね。でもわざわざ整数に直す必要はありません。単に
(sqrt x) と試し割りする整数の大小比較すればいいだけの話です。
> ところで、(let ...)で、使う変数を指定するとき、そこに値を当てはめなくて
> もいいんですね。私は、必ずなにかの値を入れなければならないと思っていまし
> た。
はい、必ずしも初期値を与える必要はありません。初期値が与えられない場合
は暗黙のうちに nil が初期値になります。ただし、本当に nil を初期値にし
たい場合は明示的に (let ((v nil)) ..) のように書いておいた方がはっきり
していいと思います。今回は y を使う前に毎回必ず setq で明示的に値をセッ
トしているので、let の先頭では初期値を与えませんでした。
> } ;; n高橋スマートバージョン
> } ;; 2は特別扱いして、奇数だけ調べる
> } ;; xが素数かどうかは、(sqrt x) までの奇数で割って調べる
> } (let ((x 3) y limit) ; 3から調べ始める
> } (insert "2\n") ; ここだけずるをする
> } (while (<= x 1000)
> } (setq y 3 limit (sqrt x)) ; 試し割りは3からルートxまで
> } (while (and (<= y limit) (/= 0 (% x y))) ; limitも試すので<=と等号が必要
> } (setq y (+ y 2))) ; 試し割りは奇数だけなので+2
> } (when (> y limit) ; limitで割り切れたときは合成数
> } (insert (format "%s\n" x))) ; だから>=でなくて>
> } (setq x (+ x 2)))) ; 次の奇数を試す
> (when (> y limit) ; limitで割り切れたときは合成数
> というのがわかりません。
…
> あっ、もしかして、さっき書いた、平方根が整数だった時とそうじゃなかった時
> の処理に関係している……?
そうです。xの平方根が整数になるとき、内側のループは y=limit まで回る可
能性があります。たとえばxが25だと、yは5まで大きくなります。ですから素
数かどうかのチェックはyがlimitよりも大きいかどうかで判断することになり
ます。
と、ここまで書いたところでスマートバージョンの無駄に気付きました。もし
xの平方根が整数になるならば、xは絶対に素数じゃありませんね。つまり内側
のループの終了判定は (< y limit) でいいことになります。人生、勉強だ。
--
TAKAHASHI Naoto
ntakahas@...
http://www.m17n.org/ntakahas/