ClojureでProject Eulerやってます-7

相変わらずProject Eulerやってます。

問題の和訳はここ

Problem 39

辺の長さが{a,b,c}と整数の3つ組である直角三角形を考え, その周囲の長さをpとする. p = 120のときには3つの解が存在する:

{20,48,52}, {24,45,51}, {30,40,50}

p ≦ 1000 で解の数が最大になる p を求めよ.

とりあえずpの条件を満たす数列を作ってみます。

;ピタゴラス数判定 
(defn pytha? [a b c]
 (let [x (int c)]
    (= (* x x) (+ ( * a a) (* b b)))))
(assert (pytha? 3 4 5.0))

;直角三角形の周囲の長さを返す
(defn pytha-length [x y]
  (let [z (Math/sqrt (+ (* x x)(* y y)))]
    (if (pytha? x y z)  (int(+ x y z)) 0 )) )
(assert (= 12 (pytha-length 3 4)))

;周囲がn未満のピタゴラス数の総和の数列
(defn pytha-coll [n]
  (take-while #(> n %) (sort (filter #(not (zero? %))(for [x (range 3 n) y (range (inc x) n)] (pytha-length  x y ))))))
(assert (= 3  (count (drop-while #(< % 120) (pytha-coll 121)))))

p=120の解をちゃんと見つけてくれたようです。

出現したpをkeyに、個数をvalueにとるmapを作ってみます。

;同じ値どおしをmapにまとめる
(defn make-count-map [c]
  (loop [map {} coll c]
    (cond
      (empty? coll) map
      :else (recur (assoc map (first coll) (inc (get map (first coll) 0))) (rest coll)))))

最大のvalueを探します。

;secondが大きいほうのcollを返す
(defn max-second-col [c1 c2]
  (if (> (second c1) (second c2) ) c1 c2))
(assert ( = [0 2] (max-second-col [0 1] [0 2])))
;最大のValueをもつkeyとvalueを返す
(defn find-max-value [m]
  (loop [map m coll [-1 -1]]
    (cond
      (empty? map) coll
      :else (recur (dissoc map (key (first map))) (max-second-col coll  (first map)) ))))
(println (first (find-max-value (make-count-map (pytha-coll 1001))) ))

遅いかなと思ったのですが、1秒もかかりませんでした。使い回ししやすそうな関数も書けたのでいい感じです。次。

Problem 40

正の整数を順に連結して得られる以下の10進の無理数を考える:

0.123456789101112131415161718192021… 小数第12位は1である.

dnで小数第n位の数を表す. d1 × d10 × d100 × d1000 × d10000 × d100000 × d1000000 を求めよ.

とりあえず1000000個の数列を作ります。

(def numbers (take 1000000 (flatten (map #(vec (str %)) (range 1 200000)))))    (assert (= 1000000 (count numbers) ))

rangeの終端は適当ですw

12桁目が1かどうか確かめます

;charをintに変換
(defn char-int[c] (- (int c) 48))
(assert (= 1 (char-int(nth numbers 11))))

あってるっぽい。解答を表示します。

(println (* (char-int(nth numbers 0)) (char-int(nth numbers 9)) (char-int(nth numbers 99))
           (char-int(nth numbers 999))(char-int(nth numbers 9999))(char-int(nth numbers 99999))(char-int(nth numbers 999999))))

てきた。次。

Problem 41

n桁Pandigitalであるとは, 1からnまでの数を各桁に1つずつもつこととする.

下のリンク先にあるような数学的定義とは異なる

例えば2143は4桁Pandigital数であり, かつ素数である. n桁(この問題の定義では9桁以下)Pandigitalな素数の中で最大の数を答えよ.

Problem32で9桁Pandigital数を出力するコードを書いているので、それを利用して9桁の範囲でまず探してみます。

(println (filter #(prime? (Integer/parseInt (apply str %))) (num-list) ))
()

Pandigitalな素数が見つかりませんでした。

8桁でも見つからず、7桁目で見つかりました。

(defn num-list7[]
  (for [c (range 7) d (range 6) e (range 5) f (range 4) g (range 3) h (range 2) ]
    (result-num [ c d e f g h 0] )))
(println (last (filter #(prime? (Integer/parseInt (apply str %))) (num-list7) )))

Problem 42

三角数のn項は tn = ½n(n+1)で与えられる. 最初の10項は

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, … である.

単語中のアルファベットを数値に変換した後に和をとる. この和を「単語の値」と呼ぶことにする. 例えば SKY は 19 + 11 + 25 = 55 = t10である. 単語の値が三角数であるとき, その単語を三角語と呼ぶ.

16Kのテキストファイル words.txt 中に約2000語の英単語が記されている. 三角語はいくつあるか?

words.txtは例によってカンマ区切りでした。 とりあえず読み込んでみます。

(def words-array (sort(re-seq #"\w+" (slurp "words.txt"))))

「単語の値」を取得する関数を書きます。

(defn word-value [s]
  (reduce + (map #(- (int %) 64) s)))
(assert = (55  (word-value "SKY")))
(def words-values (map #(word-value %) words-array ))
(println (first (sort > words-values )))

単語の値の最大は192らしいです。 192までの三角数を使って三角数の判定関数を書きます。

(defn tri [n] (quot (* n (inc n)) 2))
(def tri-until-192 (take-while #(< % 192) (map #(tri %) (iterate inc 1))))

(defn tri? [n]
  (loop [tri-coll tri-until-192 ]
    (cond
      (empty? tri-coll) false
      (= (first tri-coll) n) true
      :else (recur (next tri-coll) ))))

単語の値を全部なめてみる。

(println (count(filter #(tri? %) words-values )))

できた。次。

Problem43

数1406357289は0から9のPandigital数である (0から9が1度ずつ現れるので). この数は部分語が面白い性質を持っている.

d1を1桁目, d2を2桁目の数とし, 以下順にdnを定義する. この記法を用いると次のことが分かる.

d2d3d4=406は2で割り切れる
d3d4d5=063は3で割り切れる
d4d5d6=635は5で割り切れる
d5d6d7=357は7で割り切れる
d6d7d8=572は11で割り切れる
d7d8d9=728は13で割り切れる
d8d9d10=289は17で割り切れる

このような性質をもつ0から9のPandigital数の総和を求めよ.

Pandigital数を返す関数は以前書いたものを少し書き換えて使います。

(defn delete [n coll]  (flatten (conj  (drop (inc n) coll)  (take n coll) )))
(defn result-num [coll]
  (loop [result-num-coll [] base-coll [0 1 2 3 4 5 6 7 8 9] src-coll coll]
    (if (not( empty?  src-coll ))
      (recur (conj result-num-coll (nth base-coll (first src-coll))) (delete (first src-coll) base-coll) (rest src-coll))
      (Long/parseLong (apply str result-num-coll)))))
(assert (= (result-num [1 0 0 0 0 0 0 0 0 0] ) 1023456789))

(defn num-list[]
  (for [i (range 1 10) a (range 9) b (range 8) c (range 7) d (range 6) e (range 5) f (range 4) g (range 3) h (range 2) ]
    (result-num [i a b c d e f g h 0] )))

これを使って結果を表示します。

(defn d234? [i] (zero? (rem ( quot (rem i 1000000000) 1000000) 2)))
(defn d345? [i] (zero? (rem ( quot (rem i 100000000) 100000) 3)))
(defn d456? [i] (zero? (rem ( quot (rem i 10000000) 10000) 5)))
(defn d567? [i] (zero? (rem ( quot (rem i 1000000) 1000) 7)))
(defn d678? [i] (zero? (rem ( quot (rem i 100000) 100) 11)))
(defn d789? [i] (zero? (rem ( quot (rem i 10000) 10) 13)))
(defn d8910? [i] (zero? (rem (rem i 1000) 17)))


(assert (d234? 1406357289))
(assert (d345? 1406357289))
(assert (d456? 1406357289))
(assert (d567? 1406357289))
(assert (d678? 1406357289))
(assert (d789? 1406357289))
(assert (d8910? 1406357289 ))
(defn d?[n] (and (d234? n) (d345? n) (d456? n) (d567? n) (d678? n) (d789? n )(d8910? n)))
(println (reduce +  (filter #(d? %) (num-list))))

5分くらいかかりました(汗 1分ルールが気になるところですが、Scalaで同じようなアルゴリズムでやってみたら20秒くらいで終わりましたので、このままで。

せっかく遅いコードがあるのでwプロファイルのやり方とか調べてみようかな。

Problem 44

五角数は Pn = n(3n-1)/2で生成される. 最初の10項は

1, 5, 12, 22, 35, 51, 70, 92, 117, 145, … である.

P4 + P7 = 22 + 70 = 92 = P8である. しかし差 70 – 22 = 48は五角数ではない.

五角数のペア PjとPkについて, 差と和が五角数になるものを考える. 差を D = |Pk – Pj| と書く. 差 D の最小値を求めよ.

五角数って何かしらと思いWikipediaで調べてみました。なるほど。

とりあえず、五角数を求める関数を書きます。

(defn pentagonal [n] (/ (* n (- (* 3 n) 1)) 2))
(assert (= [1, 5, 12, 22, 35, 51, 70, 92, 117, 145] (map #(pentagonal %) (range 1 11))))

和と差が五角数か調べないといけないのですが、Problem 42の時のように調査範囲が決まっているわけではないので同じ手が使えそうにありません。 なので、値から逆に求める関数を書きたいのですが、2次方程式の公式とか忘れてしまいました(汗

”2次方程式 公式”でググってみると、仕事でもよくお世話になっている”物理のかぎしっぽ」さんのページが見つかりました。こちら

五角数Pを求める式を

  • P = n(3n-1)/2

とすると

  • 3n²-n-2p=0

と変形できます。nが正の整数であればpが五角数といえますので、こういう関数になります。

(defn pentagonal? [n]
  (let [x  (/ (+ 1 (Math/sqrt (+ 1 (* 24 n)))) 6)]
    (== x (int x))))
(assert (pentagonal? 145))

2つの五角数のペアを返す関数はこう

(defn add-pentagonals []
   (filter #(not (empty? %)) (for [x (iterate inc 1) y (range  1 x)] (if (pentagonal? (+ (pentagonal x) (pentagonal y))) [(pentagonal x)  (pentagonal y)] []))))

差が五角数になっているかを返す関数はこう

(defn sub-pentagolan? [c]
  (pentagonal? (-  (first c) (second c))))

結果を表示してみます。

(def result (first (take 1(filter #(sub-pentagolan? %) (add-pentagonals )))))
(println result)
(println (- (first result) (second  result)))

1秒かからずに結果が表示されました。思ったより大きい数字でしたが。

差と和が五角数になるペアがほかにもあるかと思って試してみたのですが、しばらく走らせてみたものの見つかりませんでした。ほかにもうペアがないのか、それともよほど大きい数字なのか。

Problem 45

三角数, 五角数, 六角数は以下のように生成される.

三角数 Tn=n(n+1)/2 1, 3, 6, 10, 15, … 五角数 Pn=n(3n-1)/2 1, 5, 12, 22, 35, … 六角数 Hn=n(2n-1) 1, 6, 15, 28, 45, … T285 = P165 = H143 = 40755であることが分かる.

次の三角数かつ五角数かつ六角数な数を求めよ.

五角数を作成する関数はProblem 44のを使います。 三角数と六角数の判定関数はこう。

(defn triangle? [n]
  (let [x  (/ (- (Math/sqrt (+ 1 (* 8 n))) 1) 2)]
    (== x (int x))))

(defn hexagonal? [n]
  (let [x  (/ (+ 1 (Math/sqrt (+ 1 (* 8 n)))) 4)]
    (== x (int x))))

(assert (triangle? 40755))
(assert (hexagonal? 40755))

表示させてみます。

(println (first (filter #(and (triangle? %) (hexagonal? %)) (map #(pentagonal %) (iterate inc 166)))))

思ったより早く表示されました。

ClojureでProject Eulerやってます-6

相変わらずProject Eulerやってます。

問題の和訳はここ

Problem 33

49/98は面白い分数である. 「分子・分母の9をキャンセルしたので 49/98 = 4/8 が得られた」と経験を積んでいない数学者が誤って思い込んでしまうかもしれないからである.

我々は 30/50 = 3/5 のようなタイプは自明な例だとする.

1より小さく分子・分母がともに2桁の数になるような自明でない分数は 4個ある.

その4個の分数の積が約分された形で与えられたとき, 分母の値を答えよ.

問題文の意味がよくわからないんですけどw

とりあえず「分子・分母がともに2桁の数になるような自明でない分数」とやらを探してみます。

(defn frac [x y i]
  (if (= (/ x y) (/ (+ i (* x 10)) (+ y (* i 10))) )
    [(+ i (* x 10)) (+ y (* i 10))] nil))
;分数のサーチ
(defn search []
  (filter #(not (empty? %)) (for [x (range 1 10) y (range 1 10)
     i (range 1 10) :when (> y x)] (frac x y i))))
(assert (= 4 (count (search))))

確かに4つですね。

あとはこれらを掛け合わせるだけです。Clojureは有理数型を持っているのでこういう時にはすごく楽できます。

user=> (/ 2 4)
1/2
user=> (class (/ 1 2))
clojure.lang.Ratio

こんな風になってます。

;引数を分数のcolとみなして、掛け合わせる。
(defn mul-frac [coll]
  (reduce * (map #(/ (first %) (second %)) coll)))
(println (denominator (mul-frac (search))))

なんだかすっきりした値になりましたので少し不安になりましたが、あっていました。すごく気になる事は気になるのですが。。

とりあえず、次

Problem 34

145は面白い数である. 1! + 4! + 5! = 1 + 24 + 120 = 145となる.

各桁の数の階乗の和が自分自身と一致するような数の総和を求めよ.

注: 1! = 1 と 2! = 2 は総和に含めてはならない.

総和を求めるにしても、上限がわからないと手に負えないので先にそちらを求めます。

;階乗を求める
(defn fact[n] (reduce * (range 1 (inc n))))
; あとで何度も使うのでメモ化
(def fact (memoize fact))

;上限がどこまでか調べる
(println (last (take-while #(> (* % (fact 9)) (expt 10 %))
  (iterate  inc 3))))

6と表示されました。最大7桁でいいようです。

各桁の数の階乗の和を求める関数を書きます。

(defn fact-each-digit[n] (reduce + (map #(fact (- (int %) 48))
    (vec (str n)))))
(assert (= 145 (fact-each-digit 145) ))

あってるっぽい。問題の条件を満たすかを判定する関数はこんな感じ

(defn is-answer [n](if (= n (fact-each-digit n)) n 0))

総和を求めればいいので、条件を満たさない(0になったところ)も含めて全部足せばいい。1と2は総和に含めてはいけないので、こんな感じで。

(println (reduce + (map #(is-answer %) (range 3 (expt 10 6)))))

できた、次。

Problem 35

197は巡回素数と呼ばれる. 桁を回転させたときに得られる数 197, 971, 719 が全て素数だからである.

100未満には巡回素数が13個ある: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, および97である. 100万未満の巡回素数は何個か?

まずは1回 桁を回転させた数を文字列で返す関数を書きます。文字列で返すのは、途中に0が含まれる数を回転させた時の処理を楽にするためです。

(defn rotate-left [n]
     (str (apply str (next (str n)) ) (str (first (str n)))))
(assert (= "719" (rotate-left 971) ))

全ケタ回転させた数を返す関数はこんな感じ

(defn all-rotate [n]
  (loop [result [(str n) ] next (rotate-left n) base (str n)]
    (if (= base next)
      result
      (recur (conj result next) (rotate-left next) base))))

含まれる数がすべて素数かどうかチェックする関数はこんな感じ

(defn all-prime? [c] (every? true? (map #(prime? (Integer/parseInt %)) c )))
(assert (= 13 (count (filter #(all-prime? (all-rotate %)) (range 100) ))))

strを数値に変換するのはInteger/parseIntでいいんでしょうか?いちいちJavaを呼び出すのかしら?

正解を求めてみます。

(println (count (filter #(all-prime? (all-rotate %)) (range 1000000) )))

17秒くらいかかりました。こんなもんかな。次。

Problem 36

585 = 1001001001₂ (2進) は10進でも2進でも回文数である.

100万未満で10進でも2進でも回文数になるような数の総和を求めよ.

(注: 先頭に0を含めて回文にすることは許されない.)

回文数判定はProblem4でもやってますね。

;回文数判定
(defn kaibun? [n]
  (= (str n) (apply str (reverse (str n)))))
(assert (kaibun? 585 ))

;回文数判定(2進数)
(defn bin-kaibun? [n]
  (= (Integer/toBinaryString n)
      (apply str (reverse (Integer/toBinaryString n)))))
(assert (bin-kaibun? 585 ))

問題文の注から、奇数だけ判定すればいいことがわかります。 回文数の数列を返す関数はこんな感じ

(defn kaibun-coll[]
  (filter kaibun? (range 1 1000000 2)))
(defn bin-kaibun-coll [c] (filter bin-kaibun? c))

総和を表示します。

(println (reduce + (bin-kaibun-coll (kaibun-coll ))))

できた。次。

Problem 37

3797は面白い性質を持っている. まずそれ自身が素数であり, 左から右に桁を除いたときに全て素数になっている (3797, 797, 97, 7). 同様に右から左に桁を除いたときも全て素数である (3797, 379, 37, 3).

右から切り詰めても左から切り詰めても素数になるような素数は11個しかない. 総和を求めよ.

注: 2, 3, 5, 7を切り詰め可能な素数とは考えない.

左から切り詰めた数列を返す関数、右から切り詰めた数列を返す関数を書きます。

;左から右に桁を除いた数列
(defn remove-to-right [n]
  (loop [result [] i n]
    (if (= 0 (quot i 10) )
      (conj result i)
      (recur (conj result i)
        (Integer/parseInt (apply str (next (str i))))))))
(assert (= [3797, 797, 97, 7] (remove-to-right 3797)))

;右から左に桁を除いた数列
(defn remove-to-left [n]
  (loop [result [] i n]
    (if (= 0 (quot i 10) )
      (conj result i)
      (recur (conj result i)
        (Integer/parseInt (apply str (take (dec(count (str i)))
          (str i)) ))))))
(assert (= [3797, 379, 37, 3] (remove-to-left 3797)))

すべて素数かチェックする関数は35で書いたばかりですね。

(defn all-prime? [c] (every? true? (map #(prime? %) c )))

右から切り詰めても左から切り詰めても素数になるような素数であれば自身を返す そうでなければ0を返す関数を書いてみます。

(defn two-sided-primes [n]
  (if (and (prime? n)(all-prime? (remove-to-right n))
    (all-prime? (remove-to-left n))) n 0))
(assert (= 3797  (two-sided-primes 3797)))

総和を表示します。注から20未満の素数は該当しないとわかりますので、23から始めて11個見つけたところで打ち切ります。

(println (reduce +(take 11 (filter #(not (zero? (two-sided-primes % )))
  (iterate inc 23)))))

ちなみにこういう素数のことを"two-sided primes"というそうです。 このへん に実際の数字が載っています。

Problem 38

192を1, 2, 3で掛けてみよう.

192 × 1 = 192 192 × 2 = 384 192 × 3 = 576 積を連結することで1から9のPandigital数 192384576 が得られる. 192384576を 192と(1,2,3)の連結積と呼ぶ.

同じようにして, 9を1,2,3,4,5と掛け連結することでPandigital数918273645が得られる. これは9と(1,2,3,4,5)との連結積である.

整数と(1,2,…,n) (n > 1) との連結積として得られる9桁のPandigital数の中で最大のものを答えよ.

Wikipediaさんによるとパンデジタル数っていうのは、「十進法では0から9までの全ての数字を使った」数字のことを言うそうです。

パンデジタル数判定を行う関数はこんな感じ。

(defn pandigital9? [n]
  (cond
    (< n 123456789) false
    (> n 987654321) false
    :else (= (sort (str n)) '(\1 \2 \3 \4 \5 \6 \7 \8 \9))))
(assert (pandigital9? 192384576 ))

連結積を返す関数はこんな感じ

(defn concatenated-num [n m]
  (Long/parseLong (apply str (flatten (map #(str (* % n))
    (range 1 (inc m)))))))
(assert (= 192384576 (concatenated-num 192 3) ))
(assert (= 918273645 (concatenated-num 9 5) ))

これでひたすらなめていけば答えは見つかるのですが、多少絞り込んで見ました。

(def num1 (filter #(pandigital9? %) (map #(concatenated-num % 5)
  (range 1 10)) ))
(def num2 (filter #(pandigital9? %) (map #(concatenated-num % 5)
  (range 10 100)) ))
(def num3 (filter #(pandigital9? %) (map #(concatenated-num % 4)
  (range 10 100)) ))
(def num4 (filter #(pandigital9? %) (map #(concatenated-num % 3)
  (range 100 1000)) ))
(def num5 (filter #(pandigital9? %) (map #(concatenated-num % 2)
  (range 1000 10000)) ))

(println (reduce #(max %1 %2) (flatten (conj num1 num2 num3 num4 num5))))

ここまで、割と簡単に書けるものが多かったです。

ClojureでProject Eulerやってます-5

clojureをやり始めて、1月以上経ちました。

課題としてProject Eulerをやったのは、自分に合っていたのかなと思います。関数の使い方、データの取り回し、実際にやってみないと身につかないことが自然に練習出来たと思います。一度使った関数をすぐ忘れてしまうほど自分の脳が劣化している件も自覚が持てました(苦笑

で、順調に身についているかというと、そんな感じではないですね(汗

例えばProblem31はClojure,Scala,Groovyでそれぞれこんなコードになりました。

Clojure

(defn result [max  coll]
  (let [x (first coll) c (count coll)]
  (cond
    (and (= 1 c)(= 0 (rem max x))) 1
    (= 1 c) 0
    :else (reduce + (map #(result  (- max (* x %)) (next coll))
      (range 0 (inc(quot max x))))))))

Scala

def result(max:Int, list:List[Int]):Int = list match {
    case x::Nil if(max % x == 0) =>1
    case x::Nil => 0
    case x::xs =>( 0 to max/x).map{i=>result(max-x*i, xs)}.sum
  }

Groovy

static final int[] coins=[200,100,50,20,10,5,2,1];
static int result(int max, int index) {
    if (index==coins.length-1) {
        if (max%coins[index] == 0) 1 else 0
    }
    else {
        int count=0
        for(int i=max ;i>=0; i-=coins[index]) {
            count += result(i,index+1)
        }
        count
    }
}

ClojureとScalaはほぼ同じアルゴリズムになっています。 以前Blogに掲載したときと違って、個数を返す関数にしてみました。 Groovyは最初Scalaと同じようなコードを書いていたのですが、あまりに遅かったので配列のインデックス操作で計算することにしました。Javaで書いても大差ないと思います。 実行時間はClojureで70ms程度、Scalaで15ms程度、Groovyで15ms程度。Groovy++では1ms程度でしたので、Javaでもほぼ同じ時間かと思います。

こうしてみると、Scalaのコードが一番すっきりしていてわかりやすいかなと思います。 余計な変数も少ないです。

Groovyは、結果が早いので、これはこれでいいかなと思います。変数countを使用していますが、この程度なら十分許容範囲でしょう。その代りに早いですし。

Clojureはletを使ってプログラムを短めにしていますが、それでも少し込み入りすぎていて、Scalaのコードと比べると美しさに欠けるかなと思います。

Scalaのパターンマッチングが有効な例とは思うのですが、Clojureでもっと上手く書ける方法があるのか、それとも元々こういうものなのか(汗 を早く見極められるようになりたいなと思っています。

もっと他人のコードを読まなくては。

ClojureでProject Eulerやってます-4

あけましておめでとうございます。

お正月もちょこちょこやってましたのでblog更新します。

Project Euler本サイト

問題の和訳はここ

Problem 23

完全数とは, その数の真の約数の和がそれ自身と一致する数のことである. たとえば, 28の真の約数の和は, 1 + 2 + 4 + 7 + 14 = 28であるので, 28は完全数である.

真の約数の和がその数よりも少ないものを不足数といい, 真の約数の和がその数よりも大きいものを過剰数と呼ぶ.

12は, 1+2+3+4+6=16となるので, 最小の過剰数である. よって2つの過剰数の和で書ける最少の数は24である. 数学的な解析により, 28123より大きい任意の整数は2つの過剰数の和で書けることが知られている. 2つの過剰数の和で表せない最大の数がこの上限よりも小さいことは分かっているのだが, この上限を減らすことが出来ていない.

2つの過剰数の和で書き表せない正の整数の総和を求めよ.

最初、「2つの過剰数の和」の「2つの」を見逃していて、こんなのどうやって解けばいいのかと悩んでいました(汗 問題はよく読まないといけませんね。

まずは真の約数の和を返す関数を書きます。

; 約数を返す
(defn div-coll  [^long n]
  (def sqrt-n (Math/sqrt n))
  (loop [result [] i 1  ]
    (cond
      (= 1 n ) [1]
      (> i sqrt-n) result
      (= (double i) sqrt-n) (conj result i)
      (zero? (rem n i)) (recur (conj result i (quot n i))(inc i) )
      :else              (recur result (inc i)))))
;nの真の約数の和を返す
(defn proper-divisors[n]  (reduce +(rest(sort >(div-coll n)))))

過剰数を判定する関数はこんな感じ

;過剰数判定
(defn abundant? [n] (> (proper-divisors n) n))

これを使って、過剰数のシーケンスを作ります

(defn abundant-seq  [n] (filter abundant? (range n )))

(def abundant-seq (memoize abundant-seq)) 
(defn abundant-sum-set [^long n]
  (set(for [x ^long (abundant-seq n) y ^long (abundant-seq n)
   :when (>= x y)  :when  (>= n (+ x y))] (+ x y))))

ここの処理にずいぶん時間がかかってしまいます。 最初書いた関数は40秒くらいかかってしまって、いろいろ見直して、今は7秒くらいで実行できます。 Scalaで同じことやらしたら、一瞬で終わるのですが。

とにかく、これで「2つの過剰数の和で書き表せる正の整数のset」が出来ましたので これを利用して「書き表せない正の整数」を表示させます。

(println (reduce +(difference (set(range 1 28124))
                                    (abundant-sum-set 28123 ))))

出来た。次。

Problem 24

順列とはモノの順番付きの並びのことである. たとえば, 3124は数1, 2, 3, 4の一つの順列である. すべての順列を数の大小でまたは辞書式に並べたものを辞書順と呼ぶ. 0と1と2の順列を辞書順に並べると

012 021 102 120 201 210 になる.

0,1,2,3,4,5,6,7,8,9からなる順列を辞書式に並べたときの100万番目を答えよ

総当たりで100万回計算しているとさすがに終わりそうにないので別の手を考えます。

9!=362880なので100万に届きません。最初の桁は"2"になります。 1000000-9!*2を更に8!で割ってみて…を繰り返してみます。

(defn f [n]
  (loop [i n f 9 ret-con []]
    (cond
      (= f 0) (conj ret-con 0)
      (= 0 (rem i (fact f)))  (recur (rem i (fact f)) (dec f)
         (conj ret-con 1))
      :else (recur (rem i (fact f)) (dec f)
         (conj ret-con (quot i (fact f)))))))

(f 1000000)を実行すると[2 6 6 2 5 1 2 1 1 0]という結果になりました。

これを使って

  • 0~9の数列の2番目の数字
  • 0,1,3~9の数列の6番目の数字

という順で数字を拾っていきます。

(defn delete [n coll]  (flatten (conj  (drop (inc n) coll)
 (take n coll) )))
(defn result [coll]
  (loop [result-coll [] base-coll [0 1 2 3 4 5 6 7 8 9] src-coll coll]
  (if (not( empty?  src-coll ))
      (recur (conj result-coll (nth base-coll (first src-coll)))
      (delete (first src-coll) base-coll) (rest src-coll))
      result-coll)))

表示させてみます

(println (apply str(result (f 1000000))))

できた。次。

Problem 25

フィボナッチ数列は以下の漸化式で定義される:

Fn = Fn-1 + Fn-2, ただし F1 = 1, F2 = 1. 最初の12項は以下である.

F₁ = 1 F₂ = 1 F₃ = 2 F₄ = 3 F₅ = 5 F₆ = 8 F₇ = 13 F₈ = 21 F₉ = 34 F₁₀ = 55 F₁₁ = 89 F₁₂ = 144 12番目の項, F12が3桁になる最初の項である.

1000桁になる最初の項の番号を答えよ.

Clojureなら1000桁くらい余裕で計算できますので。

;フィボナッチ数を求める
(defn fibo []
  (map first (iterate (fn [[a b]] [b (+' a b)]) [0 1])))
;べき乗を求める
(defn pow [n m] (reduce *' (repeat m n)))
(println (count (take-while #(< % (pow 10 999)) (fibo))))

おしまい。次。と行きたいのですが、まだできていません。 循環節を求めるアルゴリズムはどこかにあると思うので、まずそちらの調査をしようかなと思っています。

Problem 27

オイラーは以下の二次式を考案している:

n² + n + 41. この式は, nを0から39までの連続する整数としたときに40個の素数を生成する. しかし, n = 40のとき40²+ 40 + 41 = 40(40 + 1) + 41となり41で割り切れる. また, n = 41のときは41² + 41 + 41であり明らかに41で割り切れる.

計算機を用いて, 二次式 n² – 79n + 1601という式が発見できた. これはn = 0 から 79 の連続する整数で素数を生成する. 係数の積は, -79 × 1601 で -126479である.

さて, |a| < 1000, |b| < 1000 として以下の二次式を考える (ここで|a|は絶対値):

n² + an + b n=0から始めて連続する整数で素数を生成したときに最長の長さとなる上の二次式の, 係数a, bの積を答えよ.

オイラーさんがどうやってこれを見つけたのか気になります。聞いたところでまねできそうにもないですがw

とりあえずa=1,b=41とa=-79,b=1601の素数列を表示してみます

(defn prime? [m]
  (if (>= m 2)
    (loop [n 2]
      (if (<= n (Math/sqrt m))
        (cond
          (= (mod m n) 0) false
          :else  (recur (inc n)))
        true ))
    false))
(defn prime [a b]  (take-while prime? (map #(+ (* % %) (* a %) b)
  (range (dec b)))))

(println (prime 1 41))
(println (prime -79 1601))

2つ目の表示を見ていると 1601から始まってだんだん小さくなり、41で折り返して1601で終わっていることがわかります。

さて、総当たりでは少し厳しそうなので、もう少し条件を絞ってみます。

まず、n=0の時も素数を返さないといけない以上、bは素数に限られます。また、n=bの時も素数にはならないことから、ここが上限になります。bの数が大きいほど、長い素数列が期待できます。 正解は、a=1,b=41か、もしくはbがそれより大きいと考えていいでしょう。 aが負の値のほうが何となく有利な気がしますが、証明まで思いつかなかったのでaは全部なめてみます。 幸い41から999までの素数列は求め方がわかっていますので、それを利用します。

;素数列の数を返す
(defn prime-count [a b] (if (not= () (prime a b))
   [(count (prime a b)) a b] [] ))
;大きいほうの値を返す。
(defn max-vec [n] (reduce #(if (> (first %1) (first %2)) %1 %2) n))
(def prime-list (take-while #(> 999 %)(prime 1 41)))
(def max-prime-coll  (max-vec (for [x (range -999 999) y prime-list ]
  (prime-count x y))))
(println (* (second max-prime-coll ) (last max-prime-coll )))

Problem 28

1から初めて右方向に進み時計回りに数字を増やしていき, 5×5の螺旋が以下のように生成される:

21 22 23 24 25 20 7 8 9 10 19 6 1 2 11 18 5 4 3 12 17 16 15 14 13 両対角線上の数字の合計は101であることが確かめられる.

1001×1001の螺旋を同じ方法で生成したとき, 対角線上の数字の合計はいくつだろうか?

もうずいぶん前に解いたので、どうやって解いたのか覚えていなかったりします(汗

当時のメモを見ていると、9×9のらせんを書いて、そこから規則性を求めたみたいですね。

右上に伸びる数列は1²,3²,5²,7²,9²となっていますし、他の数列も右上を基準にして求められるようです。

(def square-size (* 1001 1001))
(defn upper-right [n]
  (take-while #(>= n %) (map #(* % %) (iterate #(+ 2 %) 1) )))


(defn down-right [n]
  (loop [result [] count 1 offset 1]
    (let [tail (last result)]
    (cond
      (= count 1 ) (recur [1] (inc count) 2)
      (= count 2 ) (recur (conj result 3 ) (inc count) (+ offset 8))
      (> n tail)      (recur (conj result (+ tail offset)) (inc count)
       (+ offset 8) )
      :else (take-while #(> n %) result)))))


(defn down-left-make [i n] (+ n (* 4 (inc i))) )
(defn down-left [n] (take-while #(> n %) (map-indexed down-left-make
(upper-right n) )))

(defn upper-left-make [i n] (- n (* 2 i)) )
(defn upper-left [n] (map-indexed upper-left-make (upper-right n) ))
(println (- (+ (reduce + (upper-right square-size))
           (reduce + (upper-left square-size))
           (reduce +(down-left square-size))
           (reduce + (down-right square-size)) ) 2 ))

なかなかみっともないプログラムですね(汗 メモの左下あたりは、四角形なので4という数字がどこかに出てくるのではないかと思い、解き終わった後に書いたものです。この辺の規則性を使えばもう少しスマートなプログラムになったかと思います。

Problem 29

2 ≤ a ≤ 5 と 2 ≤ b ≤ 5について, abを全て考えてみよう:

2²=4, 2³=8, 2⁴=16, 2⁵=32 3²=9, 3³=27, 3⁴=81, 3⁵=243 4²=16, 4³=64, 4⁴=256, 4⁵=1024 5²=25, 5³=125, 5⁴=625, 5⁵=3125 これらを小さい順に並べ, 同じ数を除いたとすると, 15個の項を得る:

4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125

2 ≤ a ≤ 100, 2 ≤ b ≤ 100 で同じことをしたときいくつの異なる項が存在するか?

まず15個の項が得られることを確認します。

;べき乗を求める
(defn pow [n m] (reduce *' (repeat m n)))
(assert (= '(4 8 9 16 25 27 32 64 81 125 243 256 625 1024 3125)
   (sort (set(for [x (range 2 6) y (range 2 6)] (pow x y)))) ))

これと同じことを100まででやれば正解が求められます。次

Problem 30

驚くべきことに, 各桁を4乗した和が元の数と一致する数は3つしかない.

1634 = 1⁴ + 6⁴ + 3⁴ + 4⁴ 8208 = 8⁴ + 2⁴ + 0⁴ + 8⁴ 9474 = 9⁴ + 4⁴ + 7⁴ + 4⁴ ただし, 1=1⁴は含まないものとする. この数たちの和は 1634 + 8208 + 9474 = 19316 である.

各桁を5乗した和が元の数と一致するような数の総和を求めよ.

まずは4乗した和で確かめてみます。

;べき乗を求める
(defn pow [n m] (reduce *' (repeat m n)))
(def pow (memoize pow))

;指定した一桁を取得する
(defn int-take1 [n i]
  (- (int ((vec (str n)) i)) 48 ))
(defn pow4 [n]
  (reduce + (map #(pow (int-take1 n %) 4) (range 0 4))))
(assert (= 1634 (pow4 1634)))
(assert (= 8208 (pow4 8208 )))
(assert (= 9474 (pow4 9474 )))

ここまでできれば5乗した和も簡単なのですが、最初解答は5ケタの数字のみと勝手に勘違いしていて、しばらく悩んでいました。9⁵×6=354294までは取りうるので、6ケタも有り得ますね。念のため2~354294でなめてみました。

;数値を6ケタの文字列に変換
(defn pad0-string [n] (format "%06d" n))
;指定された一桁を取得する
(defn int-take1 [n i]
  (- (int ((vec (pad0-string n)) i)) 48 ))
(defn pow5 [n]
  (reduce + (map #(pow (int-take1 n %) 5) (range 0 6))))
(def pow5-col (indexed (map #(pow5 %) (range 2 (inc (* 6 (pow 9 5)) )))))

;indexとの差が2であるか
(defn eq2?[n]  (= (+ (first n) 2) (last n)))
(println  (reduce + (map #(second %)(filter eq2? pow5-col ))))

できた、次

Problem 31

イギリスでは硬貨はポンド£とペンスpがあり,一般的に流通している硬貨は以下の8種類である.

1p, 2p, 5p, 10p, 20p, 50p, £1 (100p) and £2 (200p).

以下の方法で£2を作ることが可能である.

1×£1 + 1×50p + 2×20p + 1×5p + 1×2p + 3×1p

これらの硬貨を使って£2を作る方法は何通りあるか?

合計が£2なので£2硬貨は0枚、もしくは1枚使えます。

  • 0枚使用した場合は、残り£2を£1以下の硬貨で
  • 1枚使用した場合は、残り£1を£1以下の硬貨で

作ることになります。これを再帰的に繰り返せばすべての組み合わせがわかるはずです。

(def coins [200 100 50 20 10 5 2 1])

(defn coin-list [ max  coin-coll]
    (cond
      (and (= 1 (count coin-coll))(= 0 (rem max (first coin-coll)))) true
      (= 1 (count coin-coll)) false
      :else (map #(coin-list (- max (* (first coin-coll) %))
            (next coin-coll))
         (range 0 (inc(quot max (first coin-coll)))))))
(println (count (filter #(true? %)(flatten(coin-list  200 coins)))))

blogを書いていて気付いたのですが、coinsの最後は1ペニーなので、こんなに複雑な条件式にしなくても、もっと単純化できましたね。

末尾再帰にはならないですが、このくらいのコインの数ならスタックがあふれることもなさそうです。時間も1秒かかりませんので、このままで。

Problem 32

7254は面白い性質を持っている. 39 × 186 = 7254と書け, 掛けられる数/掛ける数/積に1から9の数が1回ずつ出現する.

掛けられる数/掛ける数/積に1から9の数が1回ずつ出現するような積の総和を求めよ.

HINT: いくつかの積は, 1通り以上の掛けられる数/掛ける数/積の組み合わせを持つが1回だけ数え上げよ.

まずは1から9の数が1回ずつ出てくる数を返す関数を書きます。Problem 24で使った関数の応用です。

(defn delete [n coll]  (flatten (conj (drop (inc n) coll) (take n coll) )))
(assert (= '(1 2 3 5 6) (delete 3 [1 2 3 4 5 6])))

(defn result-num [coll]
  (loop [result-num-coll [] base-coll [1 2 3 4 5 6 7 8 9] src-coll coll]
    (if (not( empty?  src-coll ))
      (recur (conj result-num-coll (nth base-coll (first src-coll)))
          (delete (first src-coll) base-coll) (rest src-coll))
    result-num-coll)))
(assert (= (result-num [0 0 0 0 0 0 0 0 0] ) [1 2 3 4 5 6 7 8 9] ))

(defn num-list[]
  (for [a (range 9) b (range 8) c (range 7) d (range 6) e (range 5)
       f (range 4) g (range 3) h (range 2) ]
    (result-num [a b c d e f g h 0] )))

見るからに遅そうですが、実際のところ遅いです(苦笑

さて、掛け算の組み合わせを考えますが、1桁×1桁=7桁とか7桁×1桁=1桁などという組み合わせはあり得ませんから、かなり絞り込めます。

調べていくと、1桁×4桁=4桁 と 2桁×3桁=4桁の2種類しかないので、この2つのチェック関数を作ります。

(defn coll-to-num [coll]
  (apply + (map #(* %1 (expt 10 %2)) (reverse coll) (iterate inc 0))))
(defn check144? [coll]
  (def a (first  coll))
  (def b (take 4 (drop 1 coll)))
  (def c (drop 5 coll))
  (= (* a (coll-to-num b)) (coll-to-num c)))
(defn check233? [coll]
  (def a (take 2 coll))
  (def b (take 3 (drop 2 coll)))
  (def c (drop 5 coll))
 (= (* (coll-to-num a) (coll-to-num b)) (coll-to-num c)))
(assert (true? (check233? [3 9 1 8 6 7 2 5 4])))

「39 × 186 = 7254」が正しく判定で来ているようです。

あとは、HINTに気を付けながら、積の総和を求めます

; チェック関数実行後、trueなら積を返す
(defn result-mul [coll]
  (if (or (check233? coll) (check144? coll) )
     (coll-to-num (drop 5 coll)) 0 ))
(println (reduce + (set (filter #(not(zero? %))
     (map  #(result-mul %) (num-list))))))

1分では終わらないので、いつか改良したいと思います。

ClojureでProject Eulerやってます-3

最近、すっかりProject Eulerblogになってきたw

問題の和訳はここ

problem 11

前回、どうしようか迷っていたのですが結局、以下のようにしました。

まず、問題の数字の1次元のシーケンスにします。

(def array ( 8  2 22 97 38 …

上下左右斜め、それぞれの4つの数字の積のうち最大を返す関数を作成

(def tate (last (sort(map #(mul4 20 %)(range 0 (- 400 60))))))
(def yoko (last (sort(map #(mul4 1 %)(range 0 (- 400 3))))))
(def naname-migi (last (sort(map #(mul4 21 %)(range 0 (- 400 63))))))
(def naname-hidari (last (sort(map #(mul4 19 %)(range 3 (- 400 57 3))))))

それぞれの最大値を表示。

(println (last (sort [tate yoko naname-hidari naname-migi ])))

おしまい。次

problem 12

三角数の数列は自然数の和で表わされ、7番目の三角数は 1 + 2 + 3 + 4 + 5 + 6 + 7 = 28 である。 三角数の最初の10項は

1, 3, 6, 10, 15, 21, 28, 36, 45, 55, … となる。

最初の7項について、その約数を列挙すると、以下のとおり。

1: 1

3: 1,3

6: 1,2,3,6

10: 1,2,5,10

15: 1,3,5,15

21: 1,3,7,21

28: 1,2,4,7,14,28

これから、7番目の三角数である28は、6個以上の約数をもつ最初の三角数であることが分る。

では、501 個以上の約数をもつ最初の三角数はいくらか。

まずは三角数を返す関数を書きます

(defn tri [n] (quot (* n (inc n)) 2))

表示させてみる

(println (map #(tri %)(range 1 10000)))

wikipediaの三角数にもいくつか例が載っているのですが、一致しているのであっているっぽい。

次に約数の数を数える関数を書きます。

(defn div-count [n]
  (def sqrt-n (Math/sqrt n))
  (loop [i 1 ret 0]
    (cond
      (> i sqrt-n) ret
      (= i sqrt-n) (inc ret)
      (zero? (rem n i)) (recur (inc i) (+ ret 2))
      :else              (recur (inc i) ret))))

最初は1からその数までループさせて計算していたのですが、さすがに時間がかかりすぎたのでsqrt(n)までのループで済むように処理を書き換えました。

問題例通りになるか試してみます。

(println (map div-count [1 3 6 10 15 21 28]))

あってるっぽい。

501 個以上の約数をもつ三角数を表示

(println (nth (map tri (iterate inc 1))  (count(tri-div-coll 500)) ))

ちなみに正解の約数の数は576だそうです。次

problem 13

以下の50桁の数字100個の総和の上位10桁を求めよ。  (略)

clojureはこのような大きな桁数を持つ整数も普通に計算できますので、足すだけでおっけーです。

user=> (class 53503534226472524250874054075591789781264330331690)
clojure.lang.BigInt

ちなみにもっと短い数だと

user=> (class 1)
java.lang.Long

とLong型になります。

user=> (class (int 1))
java.lang.Long

int型の1のリテラルを書きたいときにはどうするんだろう?

よくわからないので、これは置いておいて正解を。

(def col [37107287533902102798797998220837590246510135740250
          46376937677490009712648124896970078050417018260538
          74324986199524741059474233309513058123726617309629
          91942213363574161572522430563301811072406154908250
(略)
(println (apply str (take 10 (str (reduce + col)))))

でけた。

ついでなので、C言語でもやってみた。こんな長い整数はないけど、回答を求められているのは最初の10ケタだけなので、なにも全ケタ計算する必要はない。

#include <stdio.h>
    long double array[]= {
    0.37107287533902102798797998220837590246510135740250,
    0.46376937677490009712648124896970078050417018260538,
    0.74324986199524741059474233309513058123726617309629,
(略)

    0.53503534226472524250874054075591789781264330331690
};

void main(int argc,char**argv) {
    int i;
    long double count = 0.0;
    for ( i=0 ; i<100 ; i++ ) {
        count += array[i];
    }
    printf("%10Le\n",count);
}

problem 14

正の整数に以下の式で繰り返し生成する数列を定義する。

n → n/2 (n が偶数)

n → 3n + 1 (n が奇数)

13からはじめるとこの数列は以下のようになる。

13 → 40 → 20 → 10 → 5 → 16 → 8 → 4 → 2 → 1 13から1まで10個の項になる。 この数列はどのような数字からはじめても最終的には 1 になると考えられているが、まだそのことは証明されていない(コラッツ問題)

さて、100万未満の数字の中でどの数字からはじめれば一番長い数列を生成するか。

注意: 数列の途中で100万以上になってもよい

まずは問題文にある、13の時の数列を作ってみます。

(defn collatz  [n]
  (loop [result [] x (int n)]
    (cond
     (= 1 x) (conj result 1)
     (even? x) (recur (conj result x) (quot x 2))
     (odd?  x) (recur (conj result x) (+ 1 (* 3 x))))))
(println  (collatz 13))

あってるっぽい。 数列を数える関数はこう。

(defn collatz-count [n] (count (collatz n)))

ここまでできれば、一番長い数列の数は簡単に求められるのですが、それがどの数字から始まったものかがわからない(苦笑)

なのでindex付きのvecを作ってみることにしました。

;1から始まるインデックス付きのvecを返す。
(defn indexed  [c] (map vector (iterate inc 1) c))
;大きいほうの値を返す。
(defn max-vec [n] (reduce #(if (> (last %1) (last %2)) %1 %2) (indexed n)))

表示させてみます

(println (max-vec (map #(collatz-count %)(range 1 1000000))))

collatzはメモ化に向きそうな課題なのですが、実際にやってみたらOutOfMemoryErrorになってしまいました。キャッシュの数とかどこかで制限できないのかな?

Problem 15

2 × 2 のマス目の左上からスタートした場合、引き返しなしで右下にいくルートは 6 つある。 http://odz.sakura.ne.jp/projecteuler/index.php?plugin=attach&refer=Proble... では、20 × 20 のマス目ではいくつのルートがあるか。

わけわからんw

とりあえず、マス目の数より交点の数に注目して小さいパターンの経路数を求めてみます

1つの時
1

2つの時
1 1
1 2

3つの時
1 1 1
1 2 3
1 3 6

4つの時
1 1 1 1
1 2 3 4
1 3 6 10
1 4 10 20

5つの時
1 1 1 1 1
1 2 3 4 5
1 3 6 10 15
1 4 10 20 35
1 5 15 35 70

1,2,6,20,70という数列が出てきましたので、ここから先はgoogle先生に聞いてみますw するとこういうのが見つかりました。 組み合わせで解けるらしい。言われてみればその通りですね。

階乗を求める関数はこう

(defn fact[n] (reduce *(range 1N (inc n))))

でかい値になるのでBigIntを使ってます。 これを利用して今回の問題を解く関数を書きます

(defn c[n] (/ (fact (* 2 n)) (* (fact n) (fact n))))
(println (c 20))

できた。次。

problem 16

2¹⁵ = 32768 であり、これの各数字の合計は 3 + 2 + 7 + 6 + 8 = 26 となる。 同様にして、2¹⁰⁰⁰ の各数字の合計を求めよ。

べき乗を求める関数を書きます。

(defn pow [n m] (reduce *' (repeat m n)))
(println (str(pow 2 1000)))

「プログラミングClojure」だとOverflowが起きないという記述になっているのですが、実際には起きてしまいます。どうやら1.3で仕様変更があったらしいです。

いろいろ探していたらこのへんに記述がありました。"“のかわりに”‘“を使えばいいらしい。

あとは各桁を足すだけです。一度文字列に変換し、"123"–>6が返ってくるような関数をを使って合計を出します。

(defn str-add [s] (reduce + (map #(- (int % ) 48)(vec (seq s)))))
(println (str-add (str(pow 2 1000))))

できた。次

Problem 17

1 から 5 までの数字を英単語で書けば one, two, three, four, five であり、全部で 3 + 3 + 5 + 4 + 4 = 19 の文字が使われている。

では 1 から 1000 (one thousand) までの数字をすべて英単語で書けば、全部で何文字になるか。

注: 空白文字やハイフンを数えないこと。例えば、342 (three hundred and forty-two) は 23 文字、115 (one hundred and fifteen) は20文字と数える。なお、"and" を使用するのは英国の慣習。

めんどくさそうなので飛ばしますw

Problem 18

以下の三角形の頂点から下まで移動するとき、その数値の合計の最大値は23になる。

略 この例では 3 + 7 + 4 + 9 = 23

以下の三角形を頂点から下まで移動するとき、その最大の合計値を求めよ。

(略) 注: ここではたかだか 16384 通りのルートしかないので、すべてのパターンを試すこともできる。Problem 67 は同じ問題だが100行あるので、総当りでは解けない。もっと賢い方法が必要である。

せっかくなので、総当たり以外の方法でやってみましょう。 どういう経路をたどるにせよ、最後は15個の数字のどれかになるはずなので、ここから逆に頂点に向かって経路を探します。 ある地点に着目し、その直前の経路の最大値に自分自身の値を足して、自身の経路の最大値とする、という関数を作って、再帰的に求めてみます

(defn path [low col]
  (let [me (nth (nth triangle low) col)]
  (cond
    (= low 0) me
    (= low col)(+ me (path (dec low)(dec col)))
    (= col 0)  (+ me (path (dec low)col))
    :else (max (+ me (path (dec low)col))(+ me (path (dec low)(dec col)))
    ))))
(def path (memoize path))

この関数を使って、最下段すべてなめてみます。

(println (reduce max (map #(path 14 %)(range 14))))

できた、次。もめんどくさそうなのでその次。

problem 20

n × (n – 1) × … × 3 × 2 × 1 を n! と表す。

100! の各桁の数字の合計を求めよ。

階乗を求める関数はさっき書きましたのでそれを使い回しします。 桁の和を求める関数もさっき書きましたので、それを使い回しします。楽勝w

(println(str-add(str (fact 100))))

できた。次。

problem 21

d(n)をnの真の約数の和と定義する。(真の約数とはn以外の約数のことである。) もし、d(a) = b かつ d(b) = a (a ≠ b)を満たすとき、aとbは友愛数(親和数)であるという。

例えば、220の約数は1, 2, 4, 5, 10, 11, 20, 22, 44, 55, 110なのでd(220) = 284である。 また、284の約数は1, 2, 4, 71, 142なのでd(284) = 220である。

それでは10000未満の友愛数の合計を求めよ。

まずは、約数を返す関数を書きます。

(defn div-coll [n]
  (def sqrt-n (Math/sqrt n))
  (loop [result [] i 1 ]
    (cond
      (= 1 n ) [1]
      (> i sqrt-n) result
      (= (double i) sqrt-n) (conj result i)
      (zero? (rem n i)) (recur (conj result i (quot n i))(inc i) )
      :else              (recur result (inc i)))))

以前の約数の数を返す関数をちょっと手直しして見ました。 220の真の約数を表示させてみます

(println (sort(rest(sort >(div-coll 220)))))

最後の要素以外のシーケンスを返す方法がわからなかったので、逆順でソートして最初以外の要素を表示しています。

これを使って真の約数の和を返す関数を書きます。

(defn d[n]  (reduce +(rest(sort >(div-coll n)))))
(def d (memoize d))

ここから友愛数のペアを求めます。 [インデックス 要素]のシーケンスと[要素 インデックス]のシーケンスを作り、両方に含まれるものだけ抜け出せばいいはず。

(defn indexed  [c] (map vector (iterate inc 1) c))
(def col-a (indexed (map #(d %) (range 1 10000))))
(defn x-indexed  [c] (map vector c (iterate inc 1)))
(def col-b (x-indexed (map #(d %) (range 1 10000))))
;シーケンスから引数で与えられたターゲットと同じものを返す
(defn find-seq [src target] (first (filter  #(= target %) src)))
(def coll-c (map #(find-seq col-a %) col-b))

coll-cには、まだ(a ≠ b)が含まれているので、それを外します。

(def ans-col (filter #((comp not =) (first %)(last %)) coll-c))
(println (reduce +(map #(first %) ans-col )))

一応出来ましたが、1分超えちゃってます。無念。まあいいや、次。

problem 22

5000個以上の名前が書かれている46Kのテキストファイルnames.txt を用いる. まずアルファベット順にソートせよ.

のち, 各名前についてアルファベットに値を割り振り, リスト中の出現順の数と掛け合わせることで, 名前のスコアを計算する.

たとえば, リストがアルファベット順にソートされているとすると, COLINはリストの938番目にある. またCOLINは3 + 15 + 12 + 9 + 14 = 53という値を持つ. よってCOLINは938 × 53 = 49714というスコアを持つ.

ファイル中の全名前のスコアの合計を求めよ.

names.txtをダウンロードしましたが、CSVファイルになっていました。 ファイルの読み込みや正規表現による分割は「プログラミングClojure」に載ってるので楽勝です。

(use '[clojure.contrib.duck-streams :only (reader)])
(def names-array(with-open [rdr (reader "names.txt")]
                  (sort(re-seq #"\w+" (str(line-seq rdr))))))

ダブルクオーテーションまでうまく処理してくれたのは意外だったのですが、動いたのだから、まあいいやw

さて、”リストの938番目”というのが0オリジンか1オリジンか確認してみます。

(println (nth names-array  937))
(println (nth names-array  938))

COLIN
COLLEEN

1オリジンのようですね。 スコアを求める関数を書いてみます。

;スコアを求める map-indexedに合わせて、引数はインデックスが先
(defn score [n s] (* (inc n) (reduce + (map #(- (int % ) 64)(seq s)))))

COLINが53になるか確認します。

(println (score 0 (nth names-array 937)))

合ってるっぽい。全名前のスコアの合計を表示します。

(println (reduce +(map-indexed score names-array )))

map-indexedは非常に便利です。C/Javaなどのforでないとかけないような処理も、この関数があればなんとか対応できそうな気がします。

ClojureでProject Eulerやってます-2

退屈な会議のお供にProject Euler w。

もちろん冗談ですけどねw

和訳はここ

ともかく、捗りましたので続きを書きます。

problem 5

2520 は 1 から 10 の数字の全ての整数で割り切れる数字であり、そのような数字の中では最小の値である。

では、1 から 20 までの整数全てで割り切れる数字の中で最小の値はいくらになるか。

要するに1から20までの数字の最小公倍数を求めればいいらしい。

1から20までの素数の倍数には違いないはずなので、まず素数をかけ合わせてみます。

(defn prime? [m]
     (loop [n 2]
       (if (<= n (Math/sqrt m))
         (cond (= (mod m n) 0) false
             :else  (recur (inc n)))
         true )))
   (println (reduce * (filter prime? (range 1 20))))

9699690でした。

この数の倍数のうち、1から20までの素数以外の数で割り切れる最小の数を求めればいい。

素数以外の数を表示してみます

(defn not-prime?
      [n] (not (prime? n)))
   (println (filter not-prime? (range 1 20)))

ある数が、コレクションで渡したすべての数で割り切れるかを求める関数はこんな感じ

(defn every-divisible? [num divs]
      (every? #(zero? (rem num %)) divs))

9699690の倍数を表示させるのはこんな感じ。

(println (take 10 (map #(* 9699690 %) (iterate inc 1))))

先頭の10個だけ表示させてます。

nの倍数かつcollのすべての数の倍数のcollを返す関数を書いてみます。

(defn common-multiple [n coll]
      (filter #(every-divisible? % coll)   (map #(* n %) (iterate inc 1))))

これで、1 から 20 までの整数全てで割り切れる数字のcollを求める準備が出来ました。 最小の数だけ求めればいいので

(println
      (first
      (common-multiple
               (reduce * (filter prime? (range 1 20)))
               (vec (filter not-prime? (range 1 20)))
               )))

できた。

ある条件にマッチしたものだけ返すfilterがあるのなら、条件を満たす、満たさないで振り分ける関数もあるんじゃないかと思って、調べてみたらgroup-byというのが使えそうでした。

さっきのを書きなおしてみます。

(def prime-map (group-by prime? (range 1 20)) ) 
    (println
      (first
        (common-multiple
          (reduce * (prime-map true))
          (prime-map false)
          )))

動くのは動きますが、さっきのより遅かったw

まあいいや。次。

Problem 6

最初の10個の自然数について、その和の二乗と、二乗数の和は以下の通り。

1² + 2² + … + 10² = 385 (1 + 2 + … + 10)² = 3025 これらの数の差は 3025 – 385 = 2640 となる。

同様にして、最初の100個の自然数について和の二乗と二乗の和の差を求めよ。

Problem 5が難しかったので、この先どうなるのかと思いましたが、6はずいぶんやさしいですねw

最初の100個の自然数の二乗の和はこんな感じ

(def mm  (reduce + (map #(* % %) (range 1 101))))

最初の100個の自然数の合計は

(def nn (reduce + (range 1 101)))

和の二乗と二乗の和の差はこんな感じ。

(println (- (* nn nn) mm))

できた。次。

Problem 7

素数を小さい方から6つ並べると 2, 3, 5, 7, 11, 13 であり、6番目の素数は 13 である。

10001 番目の素数を求めよ。

こんなに易しいのばかりでいいんだろうか?w

(println (last (take 10001 (filter prime? (iterate inc 2)))))

できた。次。

Problem 8

以下の1000桁の数字から5つの連続する数字を取り出して その積を計算する。そのような積の中で最大のものの値はいくらか

73167176531330624919225119674426574742355349194934 96983520312774506326239578318016984801869478851843 85861560789112949495459501737958331952853208805511 12540698747158523863050715693290963295227443043557 66896648950445244523161731856403098711121722383113 62229893423380308135336276614282806444486645238749 30358907296290491560440772390713810515859307960866 70172427121883998797908792274921901699720888093776 65727333001053367881220235421809751254540594752243 52584907711670556013604839586446706324415722155397 53697817977846174064955149290862569321978468622482 83972241375657056057490261407972968652414535100474 82166370484403199890008895243450658541227588666881 16427171479924442928230863465674813919123162824586 17866458359124566529476545682848912883142607690042 24219022671055626321111109370544217506941658960408 07198403850962455444362981230987879927244284909188 84580156166097919133875499200524063689912560717606 05886116467109405077541002256983155200055935729725 71636269561882670428252483600823257530420752963450

EX 6桁の数123789なら、12378と23789の二通りとなり、後者の23789=3024が最大の積となる。

とりあえず1000桁の数字を用意します。

(def col [7 3 1 6 7 1 7 6 5 3 1 3 3 0 6 2 4 9 1 9 2 2 5 1 1 9 
               6 7 4 4 2 6 5 7 4 7 4 2 3 5 5 3 4 9 1 9 4 9 3 4
    

引数で指定された箇所から5つ取り出して、掛け合わせる関数はこんな感じ

(defn mul5 [ n ]
      (reduce * (take 5 (drop n col))))

これを先頭から順に舐めて行って、最大のものを求めます。

(println (last(sort (map mul5 (range 1000) ))))

できた。次。

Problem 9

ピタゴラスの三つ組(ピタゴラスの定理を満たす自然数)とはaa² + b² = c² を満たす数の組である.

例えば, 3² + 4² = 9 + 16 = 25 = 5²である.

a + b + c = 1000となるピタゴラスの三つ組が一つだけ存在する. このa,b,cの積を計算しなさい.

a,b,cがピタゴラス数で、かつその合計がnと一致するかを返す関数を書いてみます。

(defn pytha? [n a b c]
      (and
        (= n (+ a b c))
        (= (* c c) (+ ( * a a) (* b b)))))

これだとtrueかfalseしか帰ってこないので、trueだったら引数そのものを返す関数を書きます。

(defn x-pytha [n a b c]
      (cond
        (pytha? n a b c) [n a b c]))

3,4,5の組みを見つけられることを確認します。

(println
      (filter coll? 
         (for [x (range 1 10 ) y (range 1 10) z (range 1 10)] 
                                           (x-pytha 12 x y z))))

3 4 5と4 3 5が出てきました。合ってるっぽい。

合計が1000になる組み合わせを表示させてみます。

(println
      (first 
        (filter coll? (for [x (range 1 1000 ) y (range 1 1000)] 
          (x-pytha 1000 x y (- 1000 x y)))))))

求められているのはa,b,cの積なので

(println
      (reduce * (rest (first 
        (filter coll? (for [x (range 1 1000 ) y (range 1 1000)] 
           (x-pytha 1000 x y (- 1000 x y))))))))

できた。次。

Problem 10

10以下の素数の和は2 + 3 + 5 + 7 = 17である. 200万以下の全ての素数の和を計算しなさい.

(defn prime? [m]
      (loop [n 2]
        (if (<= n (Math/sqrt m))
          (cond
            (> 2 n) false
            (= (mod m n) 0) false
            :else  (recur (inc n)))
          true )))
    (println (reduce + (filter prime? (range 2 2000000))))

表示までにかなり時間がかかったので、もう少し短くしたいな。

とりあえず、次。

Problem 11

08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48

上の 20 × 20 の数字のなか、赤くマークされた数字の積は 26 × 63 × 78 × 14 = 1788696 となる。

上下左右斜めのいずれかの方向で連続する4つの数字の積のうち最大のものを求めよ。

ちょっと手こずってます。。

検算用のgroovyのコードはできているので、こちらだけ載っけておきます。

import com.google.common.base.CharMatcher
    import com.google.common.base.Splitter
    import org.apache.commons.lang3.math.NumberUtils
    import static com.google.common.collect.Lists.newArrayList

    class Problem11 {
        static String numsStr=
            "08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08 " +
            "49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00 " +
            "81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65 " +
            "52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91 " +
            "22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80 " +
            "24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50 " +
            "32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70 " +
            "67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21 " +
            "24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72 " +
            "21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95 " +
            "78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92 " +
            "16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57 " +
            "86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58 " +
            "19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40 " +
            "04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66 " +
            "88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69 " +
            "04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36 " +
            "20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16 " +
            "20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54 " +
            "01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48"
        public static void main(String[] args) {
            int[] nums = str2int(numsStr)
            println ((makeTate(nums)+makeYoko(nums)+makeNanameMigi(nums)+
                          makeNanameHidari(nums)).max())
        }

        static str2int = {String str ->
            CharMatcher matcher = CharMatcher.WHITESPACE
            Iterable splits = Splitter.on(matcher).split(str);
            List list = newArrayList()
            splits.each {list.add(NumberUtils.toInt(it))}

            return list
        }
        public static int[] getArray(){
            return str2int(numsStr)
        }

        static makeTate = {int[] nums ->
            List list = newArrayList()
            for (int i = 0; i < nums.length - 60; i++) {
                int mul = nums[i] * nums[i+20]*nums[i+40]*nums[i+60]
                list.add(mul)
            }
            return list
        }

        static makeYoko = {int[] nums ->
            List list = newArrayList()
            for (int i = 0; i < nums.length - 3; i++) {
    //            if (i % 20 >= 17) continue;
                int mul = nums[i]*nums[i+1]*nums[i+2]*nums[i+3]
                list.add(mul)
            }
            return list
        }

        static makeNanameMigi = {int[] nums ->
            List list = newArrayList()
            for (int i = 0; i < nums.length - 63; i++) {
    //            if (i % 20 >= 17) continue;
                int mul = nums[i]*nums[i+21]*nums[i+42]*nums[i+63]
                list.add(mul)
            }
            return list
        }

        static makeNanameHidari = {int[] nums ->
            List list = newArrayList()
            for (int i = 3; i < nums.length - 57; i++) {
    //            if (i % 20 <= 3) continue;
                int mul = nums[i]*nums[i+19]*nums[i+38]*nums[i+57]
                list.add(mul)
            }
            return list
        }
    }

Clojureでも、縦、横、斜め右、斜め左の4つのcollを作るところから始めたいのですが、やり方が思い浮かばない。。。

guavaのOptional

googleが作ったguavaというJavaライブラリがあります。 release 10.0.0からOptionalという型が追加されているので試してみました。ScalaのOptionっぽいことができるらしい。

「値がない」ことを表現するときにnullを使うことがありますが、使い方を間違えるとNullPointerExceptionが投げられてしまいます。Optionalを使って、「値がない」を定義できます。

Optional foo(String  str) {
    try {
        return Optional.of(Integer.parseInt(str));
    } catch(NumberFormatException e) {
        return Optional.absent();
    }
}

System.out.println(foo("123"));
System.out.println(foo("abc"));

Optionalの中身がabsentかどうかは、isPresentメソッドを使って判定できます。 中身を取り出すときは、getメソッドを使いますが、これはabsentを処理するとIllegalStateException が投げられるので、orメソッドを使ったほうがいいです。orは次の例で使ってます。

別の例として、Mapでの使い方を。JavaのMapは、keyが見つからなかった時もnull,keyはあるけどvalueがnullの時もnullが返ってくるので、両者は区別がつきません。Optionalを使えばこの2つを区別できます。

import com.google.common.base.Optional;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import static com.google.common.base.Optional.of;

public class OptionalSample {
    public static void main(String[] args ){
        Map<Integer,Optional<String>> map = makeMap();
        System.out.println(show(map.get(0)));
        System.out.println(show(map.get(1)));
        System.out.println(show(map.get(2)));
        System.out.println(show(map.get(-1)));

    }
    private static Map<Integer,Optional<String>> makeMap() {
        return Collections.unmodifiableMap(
                new HashMap<Integer,Optional<String>>() {
            {
                put(0, of("foo"));
                put(1,Optional.<String>absent());
                put(2, of("bar"));
            }
        });
    }

    private static String show(Optional<String> opt) {
        return opt== null ? "?" :opt.or("no value");
    }
}

ちなみにOptionalは@Betaアノテーションがついています。@Betaは仕様がベータバージョンであることを意味していて、将来仕様変更の可能性があります。

ClojureでProject Eulerやってます。

初心者向け課題はひとまずおいて、Project Eulerをやってみることにしました。 和訳はここ

Problem 1

“10未満の自然数のうち、3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり、 これらの合計は 23 になる。

同じようにして、1,000 未満の 3 か 5 の倍数になっている数字の合計を求めよ。

FizzBuzzが出来ればこれは簡単ですね。

(defn muitiple-3-5? [n]
 (cond
         (= (mod n 3) 0) true
         (= (mod n 5) 0) true
         :else false))

10未満の場合の正解が載っているので検算します。

(println (reduce + (filter muitiple-3-5? (range 10))))

結果合ってるっぽいので、1000未満の値をを表示します。

(println (reduce + (filter muitiple-3-5? (range 1000))))

できた。次。

Problem 2

フィボナッチ数列の項は前の2つの項の和である。 最初の2項を 1, 2 とすれば、最初の10項は以下の通りである。

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …

数列の項の値が400万を超えない範囲で、偶数値の項の総和を求めよ。

フィボナッチ数列を求めるプログラムは「プログラミングClojure」に載っているので、そのまま使わせて頂きます。 念のため、最初のほうを表示してみます。

(println (take 12 (fibo)))

あってるっぽい 400万を超えない範囲のフィボナッチ数列を表示してみます。

(println (take-while #(<= % 4000000) (fibo)))

偶数だけ表示させるのは

(println (filter even? (take-while #(<= % 4000000) (fibo))))

あとは、全部の合計を求めればいいだけ。

(println (reduce + (filter even? (take-while #(<= % 4000000) (fibo)))))

できた。次。

Problem 3

13195 の素因数は 5、7、13、29 である。

600851475143 の素因数のうち最大のものを求めよ。

まずは素数を求める関数を書いてみます。

(defn prime? [m]
  (loop [n 2]
    (if (<= n (Math/sqrt m))
      (cond (= (mod m n) 0) false
          :else  (recur (inc n)))
      true )))
(println (filter prime? (range 1 60)))

これらの素数列で割ってみて、余りが0のものだけでseqを作ればいいはず。

(defn prime-factor [n]
  (filter #(= (mod n  %) 0)(filter prime? (range 2 (Math/sqrt n )))))

13195で検算してみます。

(println (prime-factor 13195))

合っているっぽい。出題されているのは"600851475143 の素因数のうち最大のもの"なので

(println (last(prime-factor 600851475143)))

で求められます。おしまい。次。

Problem 4

“左右どちらから読んでも同じ値になる数を回文数という。 2桁の数の積で表される回文数のうち、最大のものは 9009 = 91 × 99 である。 では、3桁の数の積で表される回文数のうち最大のものはいくらになるか。”

まずは、回文数かどうかを判定する関数を書いてみます。

(defn kaibun? [n]
  (= (str n) (apply str (reverse (str n)))))

桁数の半分だけで比較すればおっけーですが、まあ、これでいいでしょう。

3桁の数の積で表される数はforで求められるはず。2桁の数で確認してみます。

(println (for [x (range 10 100) y (range 10 100)] (* x y)))

いけてそう。なので、3桁の数でやってみます。

(println (last(sort (filter kaibun?
  (for [x (range 100 1000) y (range 100 1000)] (* x y))))))

sortが必要な理由が、実はよくわかっていません。あとで調べないと。

clojureの初心者向け課題をやっています。

関数脳指向をを身につけたいと思い、「プログラミングClojure」を買ってきて、Clojureのお勉強をしています。 Clojure楽しいです。Clojure難しいです(笑

とりあえず一通り読んでみたので、実際にプログラムを自分で考えてみようと思い、初心者向けの課題をいくつか書いてみました。

「初心者用課題」をやってみる。

「プログラミングスレまとめ in VIP – 初心者用課題 」の課題をいくつかやってみました。

まず最初

“ループ練習 Hello World![改行]を5回表示させてください。 print(或いはprintf,cout等)を5回コピーすれば当然可能ですが、ループ構文(for,while等)を利用して、print等は1回の使用にとどめてみてください。”

せっかくなので再帰使って書いて見ました。

(defn printx [n]
  (println "Hello World!")
  (cond
   (= n 1) nil
   n (printx (- n 1))))
(printx 5)

末尾再帰ヴァージョンも

(defn printx2 [m]
  (loop [n m]
    (if (> n 0)
      (do (println "Hello World!")
      (recur (dec n))))))
(printx2 5)

できた。次は素数を求めます。

“素数を求める 1からnまでの範囲の整数から素数を見つけてすべて出力しなさい。”

こういうのはClojureは得意そうです。

例えば、偶数列を得たければ

(filter even? (range 10))

と書けるので、同じように素数を求める関数を書いてあげればいいはず。

(defn prime? [m]
  (loop [n 2]
    (if (<= n (Math/sqrt m))
      (cond (= (mod m n) 0) false
             n  (recur (inc n)))
        true )))

condの説明は「プログラミングClojure」の6ページに載っているんだけど、javaでいうswitch文のdefaultに当たる記述の仕方がわからない(汗 とりあえず書いたら動いたので、よしとします。あとで調べよう。

ちなみに表示の仕方はこう。

(println (filter prime? (range 100)))

できた。次。

“うるう年測定 入力された整数がグレゴリオ暦(いつも使ってるやつ)でうるう年であるか判定せよ”

(defn leap? [y]
  (cond (= (mod y 400) 0) "leap year"
         (= (mod y 100) 0) "normal year"
         (= (mod y 4)   0) "leap year"
         y "normal year"))

やる気のなさが伝わってくるコードですねw まあいいや、次

“ハノイの塔 与えられたn枚の円盤を用いたハノイの塔を再帰的アルゴリズムを用いて解くプログラムを作成せよ。出力は円盤の移動の記録及び手数とする。”

まったく書けそうにないので飛ばしますw

“線形合同法 線形合同法を用いて0<=x<1の範囲の乱数を発生させるプログラムを作成せよ。M=65536(=216),A=997,B=1,Xの初期値を12345として100個の乱数を発生させ,その値と平均を出力しなさい。"

これは、「プログラミングClojure」にフィボナッチ数列を求めるプログラムが載っていましたので、それを応用すれば簡単です。

(defn lcg [x a b m]
  (map #(/ % m) ;P103
          (rest ;P92
             (iterate (fn [n] (mod (+ b (* a n)) m)) x)))) ;P138

restが入っているのは、解答例が同様にf(0)に相当する値を飛ばしているから。最初気が付かなくてなんで答えが合わないのかしばらく悩みました。

(println (take 100 (lcg 12345 997 1 65536.0)))

で表示できます。

そういえば平均を求めていなかった。

(println (/ (reduce + (take 100 (lcg 12345 997 1 65536.0))) 100.0))

もう一度計算しなおすのが無駄っぽいですが、この修正は後でやろう。次。

“数当てゲーム これは答えの数を探すゲームです。適当な数を入れると正解よりも大きいか小さいか,または正解であるか出力されます。それを繰り返すことで答えを探すことができます。このゲームを作成しなさい。答えの数は乱数を使って毎回別の答えを用意しましょう。”

「プログラミングClojure」に、標準入力には"in“を使うと書いてあるのですが、その後の使い方がさっぱりわからない(汗 ここは他のサイトの記述を参考にさせて頂きました。

(import '(java.io BufferedReader InputStreamReader ))

(defn ans [a n]
  (cond (> a n) "小さすぎます"
        (< a n) "大きすぎます"
        n "正解です"))

(let [answer (rand-int 100)]
(try
(with-open [reader (BufferedReader. (InputStreamReader. (System/in)))]
  (doseq [line (take-while (comp not nil?) (repeatedly #(.readLine reader)))]
    (println (ans answer (Integer/parseInt line)))))
(catch Exception e nil)
(finally (println "終了します"))))

ほとんど、javaのプログラムですね(汗 プログラム自体は相当短くなりますが。 catchの時に何もしない処理をこう書きましたが、いいのかな? よくわからない。次。

“Fizz Buzz 1〜100までの数字を表示するプログラムを作成せよ。 ただし、3で割り切れる場合は「Fizz」、5で割り切れる場合は「Buzz」、3と5の両方で割り切れる場合は「FizzBuzz」を数字の代わりに表示すること。”

(defn fizz-buzz [n]
 (map #(cond (=(mod % 15) 0) "FizzBuzz"
         (= (mod % 3) 0) "Fizz"
         (= (mod % 5) 0) "Buzz"
         :else %) (range 1 n))) ; P133

ようやくdefault相当の書き方を見つけました。ちなみに表示はこう。

(println (fizz-buzz 100))

[android]assetsフォルダのファイルの読み込み。

JavaとAndroid初心者の方に簡単なプログラムを説明していたりしているのですが、 「Excelで作成したデータをAndroidで使いたい」という相談があったので、簡単なプログラム例を作ってみました。

Excelのファイルを直接読むのは難しいので、CSVファイルを読み込むことにします。
とりあえず
名前,住所,URL
こんな感じのファイルをassets/shop.txtにおいて、アプリに読み込ませます。住所に関する処理は実装していないのですが、その辺は気にしないということでw
EXCELが吐くファイルはダブルクオーテーションがついていたり漢字コードがShift_JISだったりするのですが、その辺はあらかじめエディタで置換することにしました(笑)

public class ShopListDemo extends ListActivity {
    private ArrayList<ShopInfo> arrayInfo;
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        arrayInfo=readFile();
        ArrayAdapter<ShopInfo> adapter=new ArrayAdapter<ShopInfo>(this,android.R.layout.simple_expandable_list_item_1, arrayInfo);
        setListAdapter(adapter);
    }


    @Override
    protected void onListItemClick(ListView l, View v, int position, long id) {
        super.onListItemClick(l, v, position, id);

        ShopInfo info=arrayInfo.get(position);
        String sUrl=info.getUrl();
        showWeb(sUrl);
    }

    private void showWeb(String url){
        Intent intent=new Intent(
                "android.intent.action.VIEW",
                Uri.parse(url));
        startActivity(intent);
    }

    private ArrayList<ShopInfo> readFile() {
        ArrayList<ShopInfo> shopArray=new  ArrayList<ShopInfo> ();

        AssetManager as = getResources().getAssets();
        try {
            InputStream is = as.open("shop.txt");
            BufferedReader br= new BufferedReader(new InputStreamReader(is));

            String sShop;
            while((sShop = br.readLine())!= null) {
                String[] shopData=sShop.split(",",3);
                if (shopData.length >= 3){
                    ShopInfo info= new ShopInfo(shopData[0],shopData[1],shopData[2]);
                    shopArray.add(info);
                }else {
                    Toast.makeText(this, "不正なデータです", Toast.LENGTH_LONG).show();
                }
            }
        }catch(IOException e){
            e.printStackTrace();
        }
        return shopArray;
    }
}
public class ShopInfo {
    private String name=null;
    private String address=null;
    private String url=null;
    
    ShopInfo (String name,  String address,String url){
        this.name=name;
        this.address=address;
        this.url=url;
    }
    
    public String getName(){
        return name;
    }

    public String getAddress(){
        return address;
    }
    public String getUrl(){
        return url;
    }
    
    public String toString(){
        return name;
    }
}