Working with Ruby
Occasional blog posts about Ruby updates, tools, editor tweaks, and random snippets. You might also be interested in my newer project that docuements lesser-known features in Ruby: Idiosyncratic Ruby.

Project Euler 19, 20, 21, 22, 23, 24, 25 (Ruby)

The next pack of Project Euler solutions.

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# http://projecteuler.net/index.php?section=problems&id=19
#  count, which sundays are the first of the month from 1901..2000

class << SundayCounter = Module.new  # same as module SundayCounter; class << self
  START_YEAR = 1900
  END_YEAR   = 2000

  def leap_year?(y)
    y % 400 == 0  or
    ( y % 4 == 0 and y % 100 != 0 )
  end

  def month_length_of(m)
    [31, @february, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][m]
  end

  def set_february(year)
     @february = 28 + ( leap_year?(year) ? 1 : 0 )
  end

  def count
    sundays  = 0
    day      = 0
    month    = 0
    count_activated = false
    year     = START_YEAR
    set_february( year )

    while year <= END_YEAR
      sundays += 1  if day == 6 && count_activated

      day = (day + 7) % month_length_of(month)

      if day < 7 # new month reached
        month = (month + 1) % 12

        if month.zero? # new year reached
          year += 1
          set_february( year )
          count_activated = true # don't count the first year
        end

      end
    end

    sundays
  end
end

puts SundayCounter.count

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# http://projecteuler.net/index.php?section=problems&id=20
#  find the sum of the digits in the number 100

puts (1..100).             # get all numbers from 1 to 100
     inject(:*).           # multiply them
     to_s.chars.           # get characters
     inject(0){ |sum, cur| # transform to_i and sum them up
       sum + cur.to_i
     }

# There are so many more ways to get the factorial ;)
#  (1..100).inject(:*)
#  (1..100).inject(0){|acc, cur| acc * cur}
#  (1..100).inject(&:*)
#  [*1..100].reduce(:*)
#  1.upto(100).reduce(:*)
#  100 * 100.times.inject(:*)
#  eval [*1..100]*'*'
#  f=proc{|x| x<1?1:f[x-1]}; f[100]
#  f=->(x){if x<1 then 1 else f[x-1] end}; f.call 100
#  [recursive method]
#  [while loop]
#  ...

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# http://projecteuler.net/index.php?section=problems&id=21
#  evaluate the sum of all the "amicable" numbers under 10000

require 'mathn'

class Integer
  def dsum
    return 1 if self < 2

    pd = prime_division.flat_map{ |n, p| [n]*p } # get all prime divisors

    ([1] + (1...pd.size).flat_map{ |e|
      # get all possible prime divisor combinations
      pd.combination(e).map{ |f| f.reduce(:*) }
    }).uniq.reduce(:+) # sum up
  end
end

res = 10_000.times.inject{ |sum, cur|
  other = cur.dsum
  (cur != other && other.dsum == cur) ? sum + cur : sum
}

puts res

 1
2
3
4
5
6
7
8
9
10
11
# http://projecteuler.net/index.php?section=problems&id=22
#  sum of the 'scores' of all words of the file

get_score = proc{ |word, position|
  (position + 1) *      # position begins with 0
  word[1..-2].          # do not use ""
    bytes.map{|e|e-64}. # - "A".ord + 1
    inject(:+)          # sum
}

puts File.read( 'names.txt' ).split(',').sort.map.with_index( &get_score ).inject(:+)

 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# http://projecteuler.net/index.php?section=problems&id=23
#  all numbers under 28123 that cannot be written as "abundant" number

require 'mathn'
require 'set'

MAX = 28123

class Integer
  def dsum # calculate divisor sum
    return 1 if self < 2

    pd = prime_division.flat_map{ |n,p| [n]*p } # get all prime divisors

    Set[ 1, *(1...pd.size).flat_map{ |e|
      # get all possible prime divisor combinations
      pd.combination(e).map{ |f| f.reduce(:*) }
    }].reduce(:+) # sum up
  end
end

can_be_written    = Set.new
cannot_be_written = (1..MAX).to_set
abundants         = (1..MAX).select{ |n| n.dsum > n }

abundants[0..MAX/2].each.with_index{ |x,i|
  abundants[i..-1].each{ |y|
    break if MAX < a = x + y
    can_be_written << a
  }
}

puts (cannot_be_written - can_be_written).reduce(:+)

 1
2
3
4
5
# http://projecteuler.net/index.php?section=problems&id=24
#  what is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?

# this is the "I don't care about performance an let Ruby manage it"-approach
puts [*0..9].permutation.to_a[ 1_000_000 - 1 ].join # yeah! they are sorted automatically

 1
2
3
4
5
6
7
8
9
10
11
12
# http://projecteuler.net/index.php?section=problems&id=25
#  fibonacci again ;)

MAX = 10**999 # the next number has 1000 digits
fib = [1,1]   # the first two digits are given
i = 0

while fib[-1] < MAX
  fib << fib[i] + fib[i+=1]
end

puts fib.size

Level 1 (25 solutions) reached ;)

Other solutions: 1-5, 6-18

Creative Commons License