[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: Reiko TAKAHASHI <HFC03614@nifty.ne.jp> (高橋玲子)
- Date: Thu, 12 Jul 2001 16:00:52 +0900
- Delivered-To: mailing list bep@argv.org
- Mailing-List: contact bep-help@argv.org; run by ezmlm
r高橋です。
ひさよしまへの道はたいへん険しく……ではなくて、まだその前のところにい
ます(^^;;;)。
昼間&放課後がちょっとだけ立て込んでいたのと、夜遅くスターバックスのアイ
スグランデラテを一気飲みしておなかを壊したのと、夜よりは強いはずの朝なの
に全然起きられなかったのと……いろいろあって遅くなりました。
Reply TAKAHASHI Naoto <ntakahas@...>'s message:
} > (let ((x 1000) (y 3))
} > (while
} > (or (> 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)))
}
} 外側の while における条件 (or (> x 3) (/= (% x 2) 0)) は、多分望んでらっ
} しゃるのとは違う働きをしていると思います。多分意図としては、「3より大
} きくてかつ奇数の場合」だけループを実行しようということじゃないかと思う
} んですが、違うでしょうか。
きゃあっ、そうでした!
} 実際には、外側の while は次のように動くことになります。
}
} 1. xが1000から4の間は、xを1ずつ小さくしながらループを回る。
}
} # 関数 or は、最初に t になった時点で残りを評価せずに終了します。です
} # からxが3より大きい場合 (> x 3) は、後ろの /= は無視されます。
}
} 2. xが3のときは、or の第2式が t になるのでやはりループを実行する。
}
} 3. xが2になると、or の第2式が nil を返すので、外側の while が終了する。
}
} つまり外側の while の条件式は、結局 (> x 2) と同じことになっています。
ほんと、そうですね。
ちゃんと覚えてはいないのですが、最初、ここを
(and (> x 3) (/= (% x 2) 0))
にしたらおかしなことになって、もう時間もないのでどさくさで(or)にしたら一
応それらしい答えを表示してくれたので、「えい、これでいいや!」とポストし
てしまいました。
でも、どうして(and)だとだめだったんでしょう……? 今ゆっくり見てもわかり
ません。
でも、答えはおかしくなります(3 と 2だけになる?)。
} もし「3より大きくてかつ奇数の場合」だけを調べたいのなら、
}
} (let ((x 999) (y 3))
} (while (> x 3)
} 内側の while
} (setq x (- x 2))
} (setq y 3))
} 最後の insert 2つ)
}
} のようにします。最初を999という奇数で始め、それから2ずつ引いていけば奇
} 数だけをチェックするようになります。
なるほど、わかりました。
} 次に内側のループですが、ここには無駄があります。最後の when とそれに続
} く insert は、while の外に出せます。
}
} (while
} (and (<= y (+ (/ x 2) 1))
} (/= (% x y) 0))
} (setq y (+ y 1)))
} (when (= y (+ (/ x 2) 1))
} (insert (format "%s\n" x))
}
} この while ループは、y が上限値以下で、かつ x が y で割り切れない間ずっ
} と続きます。反対に言うと、この while が終了するのは y が上限に達したか、
} あるいは x が y で割り切れた場合です。while が終了した後も y は最後の
} 値を保持したままですから、ループの中で毎回 when のチェックをする必要は
} ありません。ループが終わった後で1回やれば充分です。
ああ! そうですね!
(and (<= y (+ (/ x 2) 1)) ...) と
(when (= y (+ (/ x 2) 1)) ...) は、同じことをチェックしてた……。
気も知的には、(and ...)のほうは、まだ続けるかどうかを判断するためのチェッ
ク、(when ...)のほうは、素数として表示するかどうかのチェックだったのです
が……。すごい目から鱗です! でも、私はこれからもこういう無駄をたくさん
してしまいそうです……。
} それから他のスレッドで話題になっていますが、y で割り切れるかどうかチェッ
} クするときの上限値は (+ (/ x 2) 1) ではなくて、(sqrt x) です。sqrt は
} square root (平方根) の略です。x が2以上の場合、(sqrt x) は常に
} (+ (/ x 2) 1) よりも小さくなりますから、ループを回る回数をさらに減らす
} ことができます。
これ、どうしてそう言えてしまうのかが未だによくわかりません。
割る数が平方根になったとき、答えも平方根になるから、それ以上大きな数で割
っても、前に割ったことのある小さな数が答えになるだけで、結局同じことを二
度やってしまっている……ということでしょうか??
……あっ、それなら今なんとなくわかったような気が……。
でも、平方根って整数ではないですよね。その場合は、切り上げした整数を使え
ばいいですか?
平方根を整数にするには、どうすればいいでしょう?(って、みなさんが書いた
のを見ればいいですね(^^;;;))。
} えーと、2とか3の場合ってそんなに面倒ですか? 素直に書けると思うのです
} が…。ちょっとやってみましょう。
}
} ;; n高橋バカ正直バージョン
} ;; 2から1000まで全部調べる
} ;; xが素数かどうかは、2からx-1までの全部の数で割って調べる
} (let ((x 2) y) ; 素数かどうか調べる数(x)の初期値は2
} (while (<= x 1000) ; xの最大値は1000
} (setq y 2) ; 最初に割ってみる数(y)は2
} (while (and (< y x) (/= 0 (% x y))) ; yがxより小さくてかつ割り切れない間は
} (setq y (1+ y))) ; yを1増やして次のチェック
} (when (= y x) ; y=xならば全部のyで割り切なかったはず
} (insert (format "%s\n" x))) ; つまりxは素数
} (setq x (1+ x)))) ; 次のxを調べる
こんなにすっきりできるんですね。私が書くと、頭の中のぐちゃぐちゃがその
まま出てきちゃうみたいです。ずるをしなくても大丈夫だし、づごい……!!
ところで、(let ...)で、使う変数を指定するとき、そこに値を当てはめなくて
もいいんですね。私は、必ずなにかの値を入れなければならないと思っていまし
た。
(let ((x 2) y) ...) → (let ((x 2) (y 0)) ...) みたいに。
} ;; 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で割り切れたときは合成数
というのがわかりません。
「合成数」というのは、「1 と自分自身以外の約数をもつ整数。二つ以上の素数
の積。非素数」なんですね(初めて知りました(^^;))。
「limitで割り切れたとき」というのは、たとえば9とか16とか25とか、そういう
場合のことですか?
あっ、もしかして、さっき書いた、平方根が整数だった時とそうじゃなかった時
の処理に関係している……?
} 調子に乗ってさらに高速化。
これは難しいので30年後に(^_^)。
なんだか、考えたことをだらだら書いてしまってすみません。
でも、もしかして、どこかですごーく道を誤っているかもと思うので、そのまま
ポストさせてください。
次は、ほんとうに、
} それじゃあ次に、(primes n) とするとnまでの素数を全部求め、それをリスト
} にして返すような関数 primes を作ってみて下さい。最初の方で
をやってみたいと思います。
**-***-***-***-***-***-***-***-***-***-***-***-**
Reiko TAKAHASHI (高橋玲子)
E-mail: HFC03614@...
ICQ UIN: 85924121 (Twinkle)
**-***-***-***-***-***-***-***-***-***-***-***-**