Project Eueler: problem21

問題

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.

回答

def calc_dev(n)
  y = [1]
  i = 2
  while i <= n/2
    y << i if n % i == 0
    i += 1
  end
  return y
end

def chk_ami(n)
  sum1 = calc_dev(n).inject(0){|sum,x| sum + x}
  sum2 = calc_dev(sum1).inject(0){|sum,x| sum + x}
  if n == sum2 && n != sum1
    return true
  else
    return false
  end
end

def find_total_ami(n)
  total = 0
  1.upto(n).each do |v|
    total += v if chk_ami(v)
  end
  return total
end

p find_total_ami(10000)

備考

遅い。
Finished in 45.48 seconds

Project Eueler: problem20

問題

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!

回答

def kai(n)
  return n if n == 1
  return n * kai(n-1)
end

def kai_sum(n)
  t = kai(n).to_s.split(//).map{|x| x}
  return t.inject(0){|sum,x| sum + x.to_i}
end

p kai_sum(100)

Android: 別パッケージのActivityを起動する

問題

別パッケージのActivityを起動する

Intent intent = new Intent(this,jp.leontec.coresaap.CSFrameBrowser.class);
				intent.setClassName("jp.ex", "jp.ex.myactivity");
				intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
				startActivity(intent);

という書き方で、ActivityNotFoundExceptionが出てはまる

解決

以下を参考に
http://www.yaunix.com/2011/02/24/%E5%88%A5%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%81%AEactivity%E3%82%92%E8%B5%B7%E5%8B%95%E3%81%95%E3%81%9B%E3%82%8B%E6%96%B9%E6%B3%95/

Intent intent= new  Intent(this, AlarmGui.class);
startActivity(intent);

参考URLのサイトにも記載されていますが、何故かintent.setClassName("com.x.y", "className");のようにsetClassNameを利用するとActivityNotFoundExceptionになります。

とのこと。
かなり時間使った。。

Project Eueler: problem18

問題

By starting at the top of the triangle below and moving to adjacent numbers on the row below, the maximum total from top to bottom is 23.

3
7 4
2 4 6
8 5 9 3

That is, 3 + 7 + 4 + 9 = 23.

Find the maximum total from top to bottom of the triangle below:

75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23

NOTE: As there are only 16384 routes, it is possible to solve this problem by trying every route. However, Problem 67, is the same challenge with a triangle containing one-hundred rows; it cannot be solved by brute force, and requires a clever method! ;o)

テスト

require "p18.rb"

pil4 = <<"PIL4"
3
7 4
2 4 6
8 5 9 3
PIL4

pil15 = <<"PIL15"
75
95 64
17 47 82
18 35 87 10
20 04 82 47 65
19 01 23 75 03 34
88 02 77 73 07 63 67
99 65 04 28 06 16 70 92
41 41 26 56 83 40 80 70 33
41 48 72 33 47 32 37 16 94 29
53 71 44 65 25 43 91 52 97 51 14
70 11 33 28 77 73 17 78 39 68 17 57
91 71 52 38 17 14 91 43 58 50 27 29 48
63 66 04 68 89 53 67 30 73 16 69 87 40 31
04 62 98 27 23 09 70 98 73 93 38 53 60 04 23
PIL15

describe "max" do
  it "pil4" do
    calcMax(pil4) == 23
  end
  
  it "pil15" do
    calcMax(pil15) == 1074
  end
end

回答

$ar = Array.new

def sum(col,row,cache)
  return cache[col][row] if cache[col][row]
  if $ar.size-1 <= col
    return cache[col][row] = $ar[col][row]
  end
  lnode, rnode = sum(col+1,row,cache), sum(col+1,row+1,cache)
  if lnode >= rnode
     return cache[col][row] = lnode + $ar[col][row]
  else
     return cache[col][row] = rnode + $ar[col][row]
  end
end

def calcMax(pil)
  cnt = 0
  pil.each do |line|
    $ar[cnt] = line.chomp!.split(/ /).map{|x| x.to_i}
    cnt += 1
  end
  cache = Array.new(cnt)
  0.upto(cnt-1){|x| cache[x] = Array.new(x+1)}
  p rslt = sum(0,0,cache)
  return rslt
end

備考

テスト駆動でやってみた。
深さ優先探索、メモ化再帰
深さが増えるバージョンのproblem67もこのコードでOK。

Project Eueler: problem19

問題

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)?

回答

$nor = [31,28,31,30,31,30,31,31,30,31,30,31]
$nor_s = [1, 32, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335]
$nor_d = 365
$leap = [31,29,31,30,31,30,31,31,30,31,30,31]
$leap_s = [1, 32, 61, 92, 122, 153, 183, 214, 245, 275, 306, 336]
$leap_d = 366

$sum = 0

def chk_leap(year)
  if year % 4 == 0 && year % 100 != 0
    return true
  elsif year % 400 == 0
    return true
  else
    return false
  end
end

def calc_leap(st)
  while st <= $leap_d do
    $sum += 1 if $leap_s.include?(st)
    st += 7
  end
  return st - $leap_d
end

def calc_nor(st)
  while st <= $nor_d do
    $sum += 1 if $nor_s.include?(st)
    st += 7
  end
  return st - $nor_d
end

dp = 6
1901.upto(2000).each do |x|  
  if chk_leap(x) then
    dp = calc_leap(dp)
  else
    dp = calc_nor(dp)
  end
end
p $sum

備考

無理矢理。

Dateモジュールを使ったり、
http://d.hatena.ne.jp/tmr_kohei/20110704/p1
ツェラーの公式を使うとスマートにできるのね。。
http://d.hatena.ne.jp/htz/20090303/1236048043

Project Eueler: problem16

問題

215 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.

What is the sum of the digits of the number 21000?

回答

def calc(num)
  2**num
end

def do_sum(num)
  sum = 0
  str = num.to_s
  0.upto(str.size.to_i-1) do |n|
    sum += str[n,1].to_i
  end
  sum
end

n = 15
p "sum = #{do_sum(calc(n))}"
n = 1000
p "sum = #{do_sum(calc(n))}"

備考

無駄が多いなぁ。
他の人を参考に。

(2**1000).to_s.split(//).inject(0){|sum,x| sum + x.to_i}

Rubyだと桁数大きくてもOKだけど、CやJavaだと大変みたい。

Project Eueler: problem17

問題

If the numbers 1 to 5 are written out in words: one, two, three, four, five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total.

If all the numbers from 1 to 1000 (one thousand) inclusive were written out in words, how many letters would be used?


NOTE: Do not count spaces or hyphens. For example, 342 (three hundred and forty-two) contains 23 letters and 115 (one hundred and fifteen) contains 20 letters. The use of "and" when writing out numbers is in compliance with British usage.

回答

ここを参考にまずRSpecでテストを作ってみた
http://d.hatena.ne.jp/myokoym/20100919/1284887970

require "p17.rb"

describe "#toEng" do
  it "1 to one" do
    toEng(1).should == "one"
  end
  
  it "14 to fourteen" do
    toEng(14).should == "fourteen"
  end
  
#略

  
  it "999" do
    toEng(999).should == "nine hundred and ninety-nine"
  end
  
  it "1000" do
    toEng(1000).should == "one thousand"
  end
end

describe "#sumLetters" do
  it "342" do
    sumLetters(toEng(342)) == 23
  end
  
  it "115" do
    sumLetters(toEng(115)) == 20
  end
  
  it "999" do
    sumLetters(toEng(999)) == 25
  end
end

describe "#sumTotal" do
  it "5" do
    sumTotal(5).should == 19
  end
  
  it "1000" do
    sumTotal(1000).should == 21124
  end
end

回答はこうなった。

$data = <<"NUMDATA"
0,zero
1,one
2,two
3,three
4,four
5,five
6,six
7,seven
8,eight
9,nine
10,ten
11,eleven
12,twelve
13,thirteen
14,fourteen
15,fifteen
16,sixteen
17,seventeen
18,eighteen
19,nineteen
20,twenty
30,thirty
40,forty
50,fifty
60,sixty
70,seventy
80,eighty
90,ninety
NUMDATA

$dic = Hash.new

def makedic()
  $data.each do |line|
    ar = line.split(/,/)
    $dic[ar[0].to_i] = ar[1].chomp!
  end
end

def toEng(num)  
  tar = num.to_s.split(//)
  str = ""
  
  if tar[-4] != nil
    return "one thousand"
  end
  
  if tar[-3] != nil
    str += $dic[tar[-3].to_i]
    str += " hundred"
    str += " and " unless num % 100 == 0
  end
    
  if tar[-2] == "0" || tar[-2] == nil
    unless tar[-1] == "0"
      str += $dic[tar[-1].to_i]
    end
  elsif tar[-2] == "1"
    str += $dic[(tar[-2]+tar[-1]).to_i]
  else
    str += $dic[(tar[-2]+"0").to_i]
    unless tar[-1] == "0"
      str += "-" + $dic[tar[-1].to_i]
    end
  end
  
  return str
end

def sumLetters(str)
  str.gsub("-"," ").split(/ /).inject(0){|sum,s| sum + s.size}
end

def sumTotal(upper)
  makedic()
  1.upto(upper).inject(0){|sum,x| sum + sumLetters(toEng(x))}
end

p sumTotal(1000)

備考

リファクタリングにはテスト有効。
テスト駆動で書く習慣をつけよう。