今年の夏にふと始めて1問目からちびちび解いてきたHaskellでプロジェクトオイラー、20問目突破!
Problem 19. Counting Sundays
You are given the following information, but you may prefer to do some research for yourself.
  • 1 Jan 1900 was a Monday.
  • Thirty days has September,
    April, June and November.
    All the rest have thirty-one,
    Saving February alone,
    Which has twenty-eight, rain or shine.
    And on leap years, twenty-nine.
  • A leap year occurs on any year evenly divisible by 4, but not on a century unless it is divisible by 400.
How many Sundays fell on the first of the month during the twentieth century (1 Jan 1901 to 31 Dec 2000)?
Problem 19. Counting Sundays
次の情報が与えられている.
  • 1900年1月1日は月曜日である.
  • 9月, 4月, 6月, 11月は30日まであり, 2月を除く他の月は31日まである.
  • 2月は28日まであるが, うるう年のときは29日である.
  • うるう年は西暦が4で割り切れる年に起こる. しかし, 西暦が400で割り切れず100で割り切れる年はうるう年でない.
20世紀(1901年1月1日から2000年12月31日)中に月の初めが日曜日になるのは何回あるか? Problem 19 「日曜日の数え上げ」
少しゴチャゴチャしたけどこんな感じになった。
1900年1月1日(月)を基準に、次の月の1日が何曜日かを調べるようにし、
その曜日からさらに次の月を調べ…と2000年12月までの1日の曜日のリストを作り、
最後に日曜日の要素数を数えるようにした。
曜日を表すWeek型を作り、week関数ではfromEnumした数値と日数を足した値を7で割り、
その日数後の曜日を返すようにしている。
年と月を与えてその月の日数を返すdayNums関数はうるう年判定など愚直にやっているが、まぁ悪くないんじゃないかな。
メインとなるfirstOfTheMonthWeek関数が少しゴチャッたのをもう少し見通しよくできれば良かったんだが…。
Problem 20. Factorial digit sum
n! means n × (n − 1) × ... × 3 × 2 × 1
For example, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800,
and the sum of the digits in the number 10! is 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27.
Find the sum of the digits in the number 100!
Problem 20. Factorial digit sum
n × (n - 1) × ... × 3 × 2 × 1 を n! と表す.
例えば, 10! = 10 × 9 × ... × 3 × 2 × 1 = 3628800 となる.
この数の各桁の合計は 3 + 6 + 2 + 8 + 8 + 0 + 0 = 27 である.
では, 100! の各桁の数字の和を求めよ.
Problem 20 「階乗の数字和」
…あれっ、これだけでいいんだろうか…?
何だか既存の問題の少し簡単バージョンなような印象が。
各桁の和を求める関数はeuler16.hsから再利用。
Problem 21. Amicable numbers
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.
For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284.
The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.
Evaluate the sum of all the amicable numbers under 10000.
Problem 21. Amicable numbers
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未満の友愛数の和を求めよ.
Problem 21 「友愛数」
友愛数…(ゴクリ
うーむ、これはもっと速くもっと綺麗に書けそうな気がするが…今のところこんな形になった。
最適化オプションつきでコンパイルして3秒程度かかる。
divSum関数で真の約数の和を求め、1から10000-1まで真の約数の和となる値を求め、さらにその値の真の約数の和も求め、
友愛数であればリストxsに追加していき最後にxsの和を求めている。
ある値において真の約数の和を計算してその値の真の約数の和も求めているので
何も考えず順に計算していくと無駄に二倍の計算を行なってしまうため、
友愛数でない場合もリストysに追加していき、すでに計算済みであれば(xsかysに含まれていれば)
その値での計算は行わず飛ばすことで計算量の節約を計ったが…
もう少し何とかなりそうかなぁ。

Copyright© 2011-2021 Shunsuke Otani All Right Reserved .