TOP   Ruby

『プログラマ脳を鍛える数学パズル』


解答例として自分で書いてみた Ruby コードです。

Q01

class String
  def palindrome?
    self == reverse
  end
end

counter = 10
begin
  next unless counter.to_s.palindrome?
  next unless counter.to_s(2).palindrome?
  next unless counter.to_s(8).palindrome?
  break
end while (counter += 1)
puts counter    #=>585

Q02

個人的に作った Utils という「野良Gem」を使っています。これについてはここを参照。使っているインスタンス・メソッドは String#separate です。

require 'bundler/setup'
require 'utils'

op = ["+", "-", "*", "/"]
ans = []
for i in 1000..9999
  for j in 2..4
    i.to_s.separate(j).each do |ar1|
      ar = ar1.map {|n| n.to_f.to_s}
      op.repeated_permutation(j - 1) do |o|
	st = ""
	o.each_with_index {|op1, k| st += ar[k] + op1}
	st += ar[-1]
	num = eval(st)
	next if num == Float::INFINITY or num == - Float::INFINITY or num.to_f.nan?
	next unless num.to_f == num.to_i
	ans << [i, st] if i.to_s == num.to_i.to_s.reverse
      end
    end
  end
end
p ans    #=>[[5931, "5.0*9.0*31.0"]]

Q03

card = Array.new(101, true)
card[0] = false
for i in 2..100
  i.step(100, i) {|j| card[j] = !card[j]}
end
ans = []
card.each_with_index {|b, i| ans << i if b}
p ans    #=>[1, 4, 9, 16, 25, 36, 49, 64, 81, 100]

Q05

class Float
  def integer?
    (self - self.to_i).zero?
  end
end

def money(x, y)
  counter = 0
  for a in 0..(x / 500)
    b = 0
    begin
      break if (c = (x - 10 * y - 490 * a - 90 * b) / 40.0) < 0
      next if c > y
      next unless c.integer?
      next if (d = y - a - b - c) < 0 or d > y
      counter += 1
    end while (b += 1)
  end
  counter
end

#main
counter = 0
for y in 1..15
  counter += money(1000, y)
end
puts counter    #=>20

Q06

class Integer
  def return?
    num = even? ? self / 2 : self * 3 + 1
    return true if num == $i
    return false if num == 1
    num.return?
  end
end

counter = 0
2.step(10000, 2) do |i|
  $i = i
  counter += 1 if (i * 3 + 1).return?
end
puts counter    #=>34

Q07

ans = []
t = Time.mktime(1964, 10, 10)
while t <= Time.mktime(2020, 7, 24)
  num = t.strftime("%Y%m%d").to_i.to_s(2)
  ans << t.strftime("%Y%m%d") if num == num.reverse
  t += 60 * 60 * 24
end
p ans
#=>["19660713", "19660905", "19770217", "19950617", "20020505", "20130201"]

Q08

地図を作ってしらみつぶしに解いています。野良Gem 'Utils' を使っています。メソッド Array.make で、多重配列を簡単に作成するものです(参照)。

require 'bundler/setup'
require 'utils'

class Position
  def initialize
    @x = @y = 0
  end
  attr_accessor :x, :y
  
  def goto(n)
    x = @x; y = @y 
    case n
    when 0 then y += 1
    when 1 then x += 1
    when 2 then y -= 1
    when 3 then x -= 1
    else raise "Error: "
    end
    po = Position.new
    po.x = x; po.y = y
    po 
  end
  
  def movable?(map, n)
    !map.get(goto(n))
  end
  
  def x1; Limit + @x; end
  def y1; Limit - @y; end
end

class Map
  def initialize
    @field = Array.make([Limit * 2 + 1, Limit * 2 + 1], false)
    @field[Limit][Limit] = true
  end
  
  def get(po)
    @field[po.x1][po.y1]
  end
  
  def set(po)
    @field[po.x1][po.y1] = true
    self
  end
  
  def deep_copy
    Marshal.load(Marshal.dump(self))
  end
end

def move(map, po, num)
  return 1 if num >= Limit
  counter = 0
  4.times do |i|
    if po.movable?(map, i)
      po1 = po.goto(i)
      counter += move(map.deep_copy.set(po1), po1, num + 1)
    end
  end
  counter
end


#main
Limit = 12
puts move(Map.new, Position.new, 0)    #=>324932


#user	0m29.404s

全部で 759860 ステップ探索しています。

Q09

問題の意味を取り違えていました。わかりにくい書き方だと思います。

Q10

これは簡単でした。

def max_sum(ar, i)
  ar1 = ar.dup
  max = 0
  ar.size.times do
    sum = ar1[0, i].inject(:+)
    max = sum if sum > max
    ar1.rotate!
  end
  max
end

#main
eu = [0, 32, 15, 19, 4, 21, 2, 25, 17, 34, 6, 27, 13, 36, 11, 30, 8, 23,
      10, 5, 24, 16, 33, 1, 20, 14, 31, 9, 22, 18, 29, 7, 28, 12, 35, 3, 26]
am = [0, 28, 9, 26, 30, 11, 7, 20, 32, 17, 5, 22, 34, 15, 3, 24, 36, 13,
      1, 0, 27, 10, 25, 29, 12, 8, 19, 31, 18, 6, 21, 33, 16, 4, 23, 35, 14, 2]
counter = 0
for i in 2..36
  counter += 1 if max_sum(eu, i) < max_sum(am, i)
end
puts counter    #=>9

inserted by FC2 system