Created
December 22, 2012 01:48
-
-
Save bouzuya/4356980 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;;; Project Euler #15 | |
;;; http://projecteuler.net/problem=15 | |
;;; http://odz.sakura.ne.jp/projecteuler/index.php?cmd=read&page=Problem%2015 | |
(require '[clojure.test :refer [deftest is]]) | |
(defn fact | |
[n] | |
(loop [n n r 1] | |
(if (zero? n) | |
r | |
(recur (dec n) (*' r n))))) | |
(defn problem-15 | |
([] (problem-15 20 20)) | |
([m n] (/ (fact (+ m n)) (*' (fact m) (fact n))))) | |
(deftest fact-test | |
(is (= (fact 0) 1)) | |
(is (= (fact 1) 1)) | |
(is (= (fact 4) 24)) | |
(is (= (fact 20) 2432902008176640000)) | |
(is (= (fact 40) 815915283247897734345611269596115894272000000000N))) | |
(deftest problem-15-test | |
(is (= (problem-15 1 1) 2)) | |
(is (= (problem-15 1 2) 3)) | |
(is (= (problem-15 2 2) 6)) | |
(is (= (problem-15) 137846528820N))) | |
@bouzuya
無駄無く簡潔で理想的な解答と思います.
@tnoda, @bouzuya
2nCn ですから, 先に約分して,
(defn problem-15
([] (problem-15 20N))
([n]
(let [p (inc n) q (inc (* 2 n))]
(/ (apply * (range p q))
(apply * (range 2 p))))))
でも良いかも知れません.
何処で BigInt に昇格させるのが良いのかは悩みます.
@ypsilon-takai, @tnoda
動的計画法かっこいいですね.
時間があるときに調べてみたいです.
@tnoda の dp のソースは、かなり衝撃です。 こんなん書けるようになりたいですねぇ。
この式の意味するところを理解できていないんですが、全てが遅延シーケンスでできているあたりは、すごくclojureっぽいですね。
私も解いてみました。
組み合わせについての知識を使わずに(というか式が立てられると思ってませんでした……)、再帰してます。
同じ計算を何度もすることになるので、別の問題で紹介されていたmemoize
を使ってみました。
(def solve
(memoize
(fn [w h]
(if (or (zero? w)
(zero? h))
1
(+ (solve (dec w) h)
(solve w (dec h)))
))))
(solve 20 20)
と使います。
僕も解いてみました。
@bouzuya と殆ど同じですが、階乗の処理は recur
による明示的な再帰ではなく、計算に必要な数列を予め用意しています。
(defn factorial
[n]
(apply *' (range 1 (inc n))))
(defn combination
[n m]
(/ (factorial n)
(*' (factorial m)
(factorial (- n m)))))
(defn solve
[n m]
(combination (+ n m) n))
(solve 20 20)
deftest
は何に対するテストなのかが明確になるのでいいですね。ネストすることも可能なので重宝しています。
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
動的計画法 (DP; Dynamic Programming) 私は苦手です.@ypsilon-takai の解答はメモ化テーブルに
atom
を使っていたり,通れない点があっても計算できたりと一般化できるので参考になりました.ただし,この問題に限れば,ある点への経路数を計算するのに左の点への経路数とと上の点への経路数だけあれば十分なので,で DP できてしまいます.自分で書いていて何だか嘘っぽいプログラムだと思っていたのですが,実行してみたら正解を出せました.しかも,nm 個のオブジェクト生成と nm 回の加算だけなので実行時間は <1ms です.
これで「Clojure すごい」と言いたいところなのですが,私が DP を苦手としているせいもあって,Clojure では DP をうまく書けません.私が DP するときには多重ループを回して配列を更新していくように書くのですが,Clojure では Java や C++ と比較して配列計算を短く書けないのでいつも難儀しています.DP に限っては Clojure より Java のほうが書き易いです.Immutable なデータ構造を使って速い DP を書ければよいのですが,その方法の見当すらつきません.
というわけで,
緩募