您的位置:首页 > 编程语言 > Ruby

My Solution to Project Euler in Ruby

2009-03-20 21:35 405 查看
Problem 11

求横、竖、对角方向上连续四个数乘积的最大值。

暴力解,没什么技术含量。

ary2d =
"08 02 22 97 38 15 00 40 00 75 04 05 07 78 52 12 50 77 91 08
49 49 99 40 17 81 18 57 60 87 17 40 98 43 69 48 04 56 62 00
81 49 31 73 55 79 14 29 93 71 40 67 53 88 30 03 49 13 36 65
52 70 95 23 04 60 11 42 69 24 68 56 01 32 56 71 37 02 36 91
22 31 16 71 51 67 63 89 41 92 36 54 22 40 40 28 66 33 13 80
24 47 32 60 99 03 45 02 44 75 33 53 78 36 84 20 35 17 12 50
32 98 81 28 64 23 67 10 26 38 40 67 59 54 70 66 18 38 64 70
67 26 20 68 02 62 12 20 95 63 94 39 63 08 40 91 66 49 94 21
24 55 58 05 66 73 99 26 97 17 78 78 96 83 14 88 34 89 63 72
21 36 23 09 75 00 76 44 20 45 35 14 00 61 33 97 34 31 33 95
78 17 53 28 22 75 31 67 15 94 03 80 04 62 16 14 09 53 56 92
16 39 05 42 96 35 31 47 55 58 88 24 00 17 54 24 36 29 85 57
86 56 00 48 35 71 89 07 05 44 44 37 44 60 21 58 51 54 17 58
19 80 81 68 05 94 47 69 28 73 92 13 86 52 17 77 04 89 55 40
04 52 08 83 97 35 99 16 07 97 57 32 16 26 26 79 33 27 98 66
88 36 68 87 57 62 20 72 03 46 33 67 46 55 12 32 63 93 53 69
04 42 16 73 38 25 39 11 24 94 72 18 08 46 29 32 40 62 76 36
20 69 36 41 72 30 23 88 34 62 99 69 82 67 59 85 74 04 36 16
20 73 35 29 78 31 90 01 74 31 49 71 48 86 81 16 23 57 05 54
01 70 54 71 83 51 54 69 16 92 33 48 61 43 52 01 89 19 67 48"
ary2d = ary2d.split("/n").collect { |line|
line.split(' ').collect { |n| n.to_i }
}
max_prod = 0
(0 ... ary2d.size).each do |r|
(0 ... ary2d[r].size).each do |c|
prod_h, prod_v, prod_d, prod_d_r = 0, 0, 0, 0
prod_h = (0 ... 4).inject(1) {|prod, idx| prod * ary2d[r][c+idx]} if c + 4 <= ary2d[r].size
prod_v = (0 ... 4).inject(1) {|prod, idx| prod * ary2d[r+idx][c]} if r + 4 <= ary2d.size
prod_d = (0 ... 4).inject(1) {|prod, idx| prod * ary2d[r+idx][c+idx]} if c + 4 <= ary2d[r].size and r + 4 <= ary2d.size
prod_d = (0 ... 4).inject(1) {|prod, idx| prod * ary2d[r-idx][c+idx]} if r >= 3 and c + 4 <= ary2d[r].size
max_prod = [max_prod, prod_h, prod_v, prod_d, prod_d_r].max
end
end
p max_prod # => 70600674


Problem 18

简单的动态规划。同样的题还有Problem 67。

triangle =
"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"
triangle = triangle.split("/n").collect { |line|
line.split(' ').collect { |n| n.to_i }
}
# [[3], [7, 5], [2, 4, 6], [8, 5, 9, 3]]
tri = []
tri[0] = triangle[0]
(1 ... triangle.size).each do |h|
tri[h] = []
tri[h][0] = triangle[h][0] + tri[h-1][0]
(1 ... triangle[h].size-1).each do |i|
tri[h][i] = triangle[h][i] + [tri[h-1][i-1], tri[h-1][i]].max
end
tri[h] << triangle[h][-1] + tri[h-1][-1]
end
max = 0
(0 ... tri[-1].size).each do |x|
max = [max, tri[-1][x]].max
end
p max # => 1074


Problem 22

先排序,再算score。

s = []
f = File.open("f:/names.txt").each do |line|
line.scan(//"(/w*)/"/) {s << $1}
end
s.sort!
letters = {}
("a".."z").zip(1..26) { |a, b| letters[a] = b }
sum = 0
(0 ... s.size).each do |x|
score = 0
s[x].each_byte do |c|
score += letters[c.chr.downcase]
end
sum += score*(x+1)
end
p sum # => 871198282


Problem 26

找循环节。本来是我最得意的一题,看了别人的解答以后,狂汗!

由于是模d,所以1/d 的循环节最多不超过d 位(鸽笼原理)。

def cycle(d)
divisor, remainder = [], []
r, n = 1, 0
while true
old_r = r
q, r = 10*r/d, 10*r%d
#puts "#{old_r}*10/#{d}=#{q}, #{r}"

if remainder.include? r
divisor << q
remainder << r
break;
end
divisor << q
remainder << r
n += 1
end

#p divisor, remainder
if remainder[-1] == 0
#puts "0.#{divisor[0 .. -2]}"
return 0
else
idx = remainder.index(r)
a, b = "", ""
if divisor[-1] == divisor[idx]
#puts "0.#{divisor[0 ... idx]}(#{divisor[idx .. -2]})"
a = divisor[0 ... idx].to_s
b = divisor[idx .. -2].to_s
else
#puts "0.#{divisor[0 .. idx]}(#{divisor[idx+1 .. -1]})"
a = divisor[0 .. idx].to_s
b = divisor[idx+1 .. -1].to_s
end
puts d if b.to_i != (10**a.size - d*a.to_i)*(10**b.size - 1)/d
return b.size
end

end
=begin
1/1 = 1.0
1/2 = 0.5
1/3 = 0.(3)
1/4 = 0.25
1/5 = 0.2
1/6 = 0.1(6)
1/7 = 0.(142857)
1/8 = 0.125
1/9 = 0.(1)
1/10 = 0.1
1/11 = 0.(09)
1/12 = 0.08(3)
1/13 = 0.(076923)
1/14 = 0.0(714285)
1/15 = 0.0(6)
1/16 = 0.0625
1/17 = 0.(0588235294117647)
1/18 = 0.0(5)
1/19 = 0.(052631578947368421)
1/20 = 0.05
=end
len = [0, 0]
2.upto(1000) do |d|
#puts d.to_s.center 40, "-"
len << cycle(d)
#p len[-1]
end
p len.index(len.max) # => 983


看高手是怎么做的!

ary = [0]
1.upto(999) do |i|
ary << /(/w+?)/1/.match(((10**2000)/i).to_s)[1].length
end
puts ary.index(ary.max)


稍稍解释一下/1 的意思,这是一个回溯引用,引用前面划分出来的那个子表达式(/w+?)。/1 表示引用模式里的第一个子表达式,/2 代表第二个,/3 代表第三个 ……。

Problem 29

算不同的幂的个数。

terms = []
(2 .. 100).each do |a|
(2 .. 100).each do |b|
terms << a**b
end
end
p terms.uniq.size # => 9183


Problem 40

这里

Problem 42

class Integer
def triangle?
delta = Math.sqrt(1 + 8*self)
delta % 1 == 0 and delta % 2 == 1
end
end
File.open("f://words.txt") do |file|
words = file.gets.split(",").inject([]) {|w, s| w << s[1...-1]}
count = 0
words.each do |w|
sum = 0
w.each_byte {|b| sum += (b - ?A + 1)}
count += 1 if sum.triangle?
end
p count # 162
end


Problem 43

先写了一个ugly 版本:

def genarate_mult(n)
rtn =

rtn << (rtn[-1] + n) while rtn[-1] <= 1_000
rtn
end
def get_digits(n)
d2, d1, d0 = n/100, (n/10)%10, n%10
end
def problem_43
sum = 0
genarate_mult(17).each do |x|
d = []
d[8], d[9], d[10] = get_digits(x)
if (d[8] != d[9]) and (d[9] != d[10]) and (d[10] != d[8])
(0..9).each do |d7|
if not d[8..10].include?(d7) and ("#{d7}#{d[8]}#{d[9]}".to_i % 13 == 0)
d[7] = d7
(0..9).each do |d6|
if not d[7..10].include?(d6) and ("#{d6}#{d[7]}#{d[8]}".to_i % 11 == 0)
d[6] = d6
(0..9).each do |d5|
if not d[6..10].include?(d5) and ("#{d5}#{d[6]}#{d[7]}".to_i % 7 == 0)
d[5] = d5
(0..9).each do |d4|
if not d[5..10].include?(d4) and ("#{d4}#{d[5]}#{d[6]}".to_i % 5 == 0)
d[4] = d4
(0..9).each do |d3|
if not d[4..10].include?(d3) and ("#{d3}#{d[4]}#{d[5]}".to_i % 3 == 0)
d[3] = d3
(0..9).each do |d2|
if not d[3..10].include?(d2) and ("#{d2}#{d[3]}#{d[4]}".to_i % 2 == 0)
d[2] = d2
(0..9).each do |d1|
if not d[2..10].include?(d1)
d[1] = d1
num = ""
(1..10).each { |i| num += d[i].to_s }
puts num
sum += num.to_i
break
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
end
sum
end
p problem_43 # 16695334890


后来实在看不下去了,写了一个递归版本,但不知道怎么把结果累加起来,只好用了全局变量-_-!!

def genarate_mult(n)
rtn =

rtn << (rtn[-1] + n) while rtn[-1] <= 1_000
rtn
end
def get_digits(n)
d2, d1, d0 = n/100, (n/10)%10, n%10
end
$sum = 0
def find_num(k, d, primes)
if k == 1
(0..9).each do |d1|
if not d[2..10].include?(d1)
d[1] = d1
p n = (1..10).inject("") { |num, i| num += d[i].to_s }
$sum += n.to_i
end
end
else
(0..9).each do |dk|
if not d[(k+1)..10].include?(dk) and ("#{dk}#{d[k+1]}#{d[k+2]}".to_i % primes[k] == 0)
d[k] = dk
find_num(k-1, d, primes)
end
end
end
end
def problem_43
genarate_mult(17).each do |x|
d = []
d[8], d[9], d[10] = get_digits(x)
if (d[8] != d[9]) and (d[9] != d[10]) and (d[10] != d[8])
primes = [nil, nil, 2, 3, 5, 7, 11, 13]
find_num(7, d, primes)
end
end
$sum
end
p problem_43


Problem 44

require 'benchmark'
class Integer
def pentagonal?
r = Math.sqrt(1 + 24*self)
(r % 1 == 0) and ((1 + r) % 6 == 0)
end
end
def problem_44(n)
p = [1]
(2..n).each do |d|
p << (p[-1] + 3*d - 2)
end
p.each do |d|
p.each do |x|
#idx = (1 + Math.sqrt(1 + 24*x)) / 6
#break if d < 3*idx - 2
if (x + d).pentagonal? and (2*x + d).pentagonal?
p "yeah, #{x}, #{x + d}"
return d
end
end
end
end
p Benchmark.realtime {
p problem_44(2_000)
}


这个程序的最大问题就是你必须先蒙一个范围,比如2000。

一个叫dfg59 的哥们这样写道:

class Integer
def pent?
((1+Math.sqrt(1+24*self))/6)%1 == 0
end
end

a = 1
while true
pa = a*(3*a-1)/2
(1..a).each do |b|
pb = b*(3*b-1)/2
if (pa+pb).pent? && (pa-pb).abs.pent?
puts (pa-pb).abs
exit
end
end
a += 1
end


Problem 46

require 'mathn'
class Prime
@@primes = []
@@gen = Prime.new

def prime(n)
while n >= @@primes.size do
@@primes << @@gen.next
end
@@primes

end

def prime?(n)
loop do
@@primes << @@gen.next
break if n <= @@primes[-1]
end
@@primes.include?(n)
end

end
require 'benchmark'
p Benchmark.realtime {
gen = Prime.new
3.step(1.0/0, 2) do |n|
n = n.to_i
if not gen.prime?(n)
find_it = false
0.upto(1.0/0) do |i|
p = gen.prime(i)
break if p > n
x = ((n - p)/2).to_i
if Math.sqrt(x).floor ** 2 == x
find_it = true
# puts "#{n} = #{p} + 2*#{Math.sqrt(x)}**2"
break
end
end
if not find_it
puts "yeah! #{n}"
break
end

end

end
}


Problem 47

记得mathn 库有个叫prime_division() 的函数,可以把自然是分解成素因子的幂的积的形式(当然,是用数组表示的)。

require 'mathn'
require 'benchmark'
p Benchmark.realtime {
factors_c = [0, 0, 1]
(3..1000000).each do |x|
factors_c << x.prime_division.size
if factors_c[x] == factors_c[x-1] and
factors_c[x-1] == factors_c[x-2] and
factors_c[x-2] == factors_c[x-3] and
factors_c[x] == 4
p "#{x-3}, #{x-2}, #{x-1}, #{x}"
break
end
end
} # 195.125 s


会不会太简单了!自己写一个prime_division():

class Integer
@@factors = {2=>{2=>1}}
def prime_division
return @@factors[self] if @@factors[self]
2.upto(Math.sqrt(self)) do |x|
if self % x == 0
lh, rh = x.prime_division, (self/x).prime_division
keys = lh.keys + rh.keys
hash = {}
keys.each do |k|
hash[k] = (lh[k] || 0) + (rh[k] || 0)
end
return @@factors[self] = hash
end
end
@@factors[self] = {self=>1}
end
end


嗯,速度还行,花了31.75秒。

看后面的论坛,看到了一位老兄四行搞定:

# slow but compact and readable ruby
require "mathn"
require "enumerator"

p (1..1.0/0.0).each_cons(4) { |g|
break g if g.all? { |nbr| nbr.prime_division.length == 4 }
}


Problem 49

为什么又是3330呢?巧合??

def sieve(n)
sieve = Array.new(n, true)
sieve[0..1] = [false, false]

2.upto(Integer(Math.sqrt(n))) do |i|
if sieve[i]
(2*i).step(n, i) do |j|
sieve[j] = false
end
end
end
primes = []
sieve.each_with_index {|x, i| primes << i if x}
primes
end
def permutation(ary)
return ary if ary.size == 1
res = []
ary.each do |e|
permutation(ary - [e]).each do |x|
res << (e.to_s + x.to_s).to_i
end
end
res
end
def permute(str)
ary = []
(1..str.length).each {|x| ary << x}
res = []
permutation(ary).each do |x|
s = ""
x.to_s.each_byte do |b|
s << str[b - 49]            # 1's ASCII code is 49.
end
res << s
end
res
end
def in_a_line(ary)
n = ary.size
(0..(n-3)).each do |i|
((i+1)..(n-2)).each do |j|
((j+1)..(n-1)).each do |k|
return [ary[i], ary[j], ary[k]] if (ary[j] - ary[i]) == (ary[k] - ary[j])
end
end
end
[]
end
require 'set'
def solve_problem_49(d)
n = 10**d
primes = sieve(n)
four_digit = primes.find_all { |x| x > 10**(d-1) }
four_digit.each do |x|
perm = permute(x.to_s).map {|e| e.to_i}.find_all {|e| e if four_digit.include? e}
set = Set.new(perm)
if set.size >= 3
ary = []
set.each {|x| ary << x}
ary.sort!
res = in_a_line(ary)
p res if res != []
end
end
# [2969, 6299, 9629]
end
require 'benchmark'
puts Benchmark.realtime {
solve_problem_49(4)
}


Problem 50

第50题想了好久,一开始随手写了一个遍历所有可能的算法,跑了一晚也没算出来。后来重读了一遍题目,一想,既然是找最大的,何不从最长的区间开始搜呢。结果只花了不到4秒就算出来了,997651。算法的威力啊!!!
筛法是抄Bob大叔的

固定区间长度,先找到最靠右、其和又没超过1,000,000的区间,不断左移。只要找到,它一定是最长最大的。

def sieve(n)
sieve = Array.new(n, true)
sieve[0..1] = [false, false]

2.upto(Integer(Math.sqrt(n))) do |i|
if sieve[i]
(2*i).step(n, i) do |j|
sieve[j] = false
end
end
end
primes = []
sieve.each_with_index {|x, i| primes << i if x}
primes
end
def find_such_a_prime(primes, n)
sum = 0
a, b = 0, n
primes.each_with_index do |x, i|
sum += x
if sum > n
a , b = 0, i - 1
sum -= x
break
end
end
# hints: 2 + ... + 3931 = 997661; 3931 is the 547th prime number.
#          2 + ... + 3931 + 3943 = 1001604.
(b - a + 1).downto 1 do |len|
l, r, s = 0, len - 1, sum

loop do
new_s = s + primes[r + 1] - primes[l]
break if new_s > n
# shift right
s = new_s
l += 1
r += 1
end

l.downto(0) do |u|
v = u + len - 1
if primes.index(s) != nil
puts "#{primes[u]} + ... + #{primes[v]} = #{s} (#{len} items)"
return s
end
# shift left
s = s - primes[v] + primes[u - 1]
end
sum -= primes[len - 1]
end
end
require 'benchmark'
puts Benchmark.realtime {
n = 1_000_000
primes = sieve(n)
p find_such_a_prime(primes, n)
}


Problem 67

和Problem 18 一个味。

triangle = ""
f = File.open("f:/triangle.txt").each do |line|
triangle << line
end
triangle = triangle.split("/n").collect { |line|
line.split(' ').collect { |n| n.to_i }
}
# [[3], [7, 5], [2, 4, 6], [8, 5, 9, 3]]
tri = []
tri[0] = triangle[0]
(1 ... triangle.size).each do |h|
tri[h] = []
tri[h][0] = triangle[h][0] + tri[h-1][0]
(1 ... triangle[h].size-1).each do |i|
tri[h][i] = triangle[h][i] + [tri[h-1][i-1], tri[h-1][i]].max
end
tri[h] << triangle[h][-1] + tri[h-1][-1]
end
max = 0
(0 ... tri[-1].size).each do |x|
max = [max, tri[-1][x]].max
end
p max # => 7273
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: