TOP   Ruby

プロジェクト・オイラー

プログラムで解く数学の問題集です。

サイト: https://projecteuler.net/
日本語訳サイト: http://odz.sakura.ne.jp/projecteuler/index.php?Project%20Euler

参考になるサイト: 桃の天然水

以下は自分用の覚え書きです。

Q54

class Array
  def count_same_objects
    h = {}
    each do |ob|
      a = count(ob)
      h[ob] = a if a > 1
    end
    h
  end
end
 
class String
  def to_intg
    Value.find_index(self) + 2
  end
end

class PokerHands
  def initialize(data)
    a = eval("%w(#{data})")
    hd = []
    a.each {|h| b = h.split(""); hd << [b[0].to_intg, b[1]]}
    @hand = hd.transpose
  end
  
  def hi    #Hash inverted
    @hand[0].count_same_objects.invert
  end
  
  def high_card?
    @hand[0].sort {|a, b| b <=> a}
  end
  
  def one_pair?
    return false unless @hand[0].count_same_objects.values.count(2) == 1
    a = @hand[0].count_same_objects.keys
    a + (@hand[0].uniq - a).sort {|a, b| b <=> a}
  end
  
  def two_pairs?
    return false unless @hand[0].count_same_objects.values.count(2) == 2
    c = @hand[0].count_same_objects.keys.sort
    d = [c[-1], c[-2]]
    d + (@hand[0].uniq - d)
  end
  
  def three_of_a_kind?
    return false unless @hand[0].count_same_objects.values.include?(3)
    c = (@hand[0] - [hi[3]]).sort
    [hi[3], c[1], c[0]]
  end
  
  def straight?
    a = @hand[0].sort
    a.each_with_index {|v, i| return false unless v == a[0] + i}
    a[-1]
  end
  
  def flush?
    a = @hand[1].count_same_objects
    a.each_key {|k| return @hand[0].sort {|a, b| b <=> a} if a[k] == 5}
    false
  end
  
  def full_house?
    a = @hand[0].count_same_objects
    return false unless a.values.include?(3) and a.values.include?(2)
    [hi[3], hi[2]]
  end
  
  def four_of_a_kind?
    return false unless @hand[0].count_same_objects.values.include?(4)
    [hi[4], (@hand[0].uniq - [hi[4]])[0]]
  end
  
  def straight_flush?
    return false unless (a = straight?) and flush?
    a
  end
  
  def royal_flush?
    straight? == 14 and flush?
  end
  
  def open_hand
    Fn.each_with_index do |mthd, i|
      a = send(mthd)
      return i, a if a
    end
  end
end

def player1_won?(hand1, hand2)
  i1, a1 = hand1.open_hand
  i2, a2 = hand2.open_hand
  puts "hand1: #{Fn[i1][0..-2]},  hand2: #{Fn[i2][0..-2]}"
  return true if i1 < i2
  return false if i1 > i2
  a1.size.times do |j|
    return true if a1[j] > a2[j]
    return false if a1[j] < a2[j]
  end
  raise "error"
end

Value = %w(2 3 4 5 6 7 8 9 T J Q K A)
Fn = %i(royal_flush? straight_flush? four_of_a_kind? full_house?
            flush? straight? three_of_a_kind? two_pairs? one_pair? high_card?)
counter = 0
File.open("p054_poker.txt") do |io|
  io.each_line do |hand|
    hand.chomp!
    hand1 = PokerHands.new(hand[0..13])
    hand2 = PokerHands.new(hand[15..28])
    counter += 1 if player1_won?(hand1, hand2)
  end
end
puts counter

Q76

自然数の分割についてはこちら

フォーラムのutkarsh_23氏の Python コードです。数式
     
を使っています(参照)。

def pent(n):
    return (n * (3 * n - 1)) / 2
    
def dyf(num):
    if num < 0:
        return 0
    elif num in memo:
        return memo[num]
    else:
        f = 0
        for a in range(len(L)):
            f += L[a][0] * dyf(num - L[a][1])
    memo[num] = f
    return f
    
global L,memo
memo = {0:1}
number = 100
n = 1
L = []
while True:
    sign = 1
    if n % 2 == 0:
        sign = -1
    if number - pent(n) >= 0:
        L.append((sign , pent(n)))
    else:
        break
    if number - pent(-1 * n) >= 0:
        L.append((sign , pent(-1 * n)))
    else:
        break
    n += 1
L = L[::-1]
print (dyf(number) - 1)

上を Ruby で書き直しました。

def pent(n)
  (n * (3 * n - 1)) / 2
end

def dyf(num)
  return 0 if num < 0
  return @memo[num] if @memo.has_key?(num)
  f = 0
  @l.size.times {|a| f += @l[a][0] * dyf(num - @l[a][1])}
  @memo[num] = f
end

#main
@memo = {0=>1}
number = 100
n = 1
@l = []
loop do
  sign = n.even? ? -1 : 1
  break unless number >= (a = pent(n))
  @l << [sign , a]
  break unless number >= (a = pent(-1 * n))
  @l << [sign , a]
  n += 1
end
@l.reverse!
puts dyf(number) - 1    #=>190569291

Q80

BigDecimalライブラリを使えば簡単です。

require 'bigdecimal'
require 'utils'

co = 0
for i in 1..100
  next if Math.sqrt(i).integer?
  BigDecimal(i).sqrt(100).to_s[2..101].each_char {|s| co += s.to_i}
end
puts co    #=>40886

inserted by FC2 system