-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprob15.rb
More file actions
114 lines (96 loc) · 3.49 KB
/
prob15.rb
File metadata and controls
114 lines (96 loc) · 3.49 KB
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# http://projecteuler.net/problem=15
#
# Starting in the top left corner of a 2x2 grid, and only being able to move to
# the right and down, there are exactly 6 routes to the bottom right corner.
#
# (image from URL)
#
# How many such routes are there through a 20x20 grid?
# Solution
#
# Traverse an (N+1)x(N+1) matrix representing the nodes in an NxN grid,
# calculate the number of paths to the endpoint starting from each point.
# Begin from the lower-right corner (the end) and work backwards towards the
# top-left corner, traversing each antidiagonal* until the top-left value is
# reached. Within each antidiagonal, the same answer results regardless of
# order of elements visited.
#
# The solution is the value in (0,0) of the array.
#
# *Antidiagonal: The diagonal of a square matrix from the top right to the
# bottom left corner
# =============================== Support Code ================================
class SolutionGrid
attr_accessor :size
def initialize grid_size
@size = grid_size + 1
# Creates a size x size 2d array with each value initialized to nil. A
# Ruby matrix isn't used here as matrices are immutable; each change to an
# element requires copying the entire matrix and changing one element.
@grid = Array.new(@size) { Array.new(@size) }
populate_grid
end
def number_of_paths
@grid[0][0]
end
#private
class MatrixPosition
attr_accessor :row, :column
def initialize(row, column)
@row = row
@column = column
end
end
def populate_grid
antidiagonal_starts.each do |antidiag_start|
populate_antidiagonal antidiag_start
end
end
# Returns an array of matrix positions, beginning with the lower-right and
# moving left until the left edge, then upward along the left edge.
def antidiagonal_starts
result = []
# Insert the antidiagonal starts along the bottom edge first, beginning on
# the right side
(1..size).each do |n|
result.push MatrixPosition.new(size-1, size-n)
end
# Start at 2 since the last iterator covered (size-1, 0)
(2..size).each do |n|
result.push MatrixPosition.new(size-n, 0)
end
result
end
def populate_antidiagonal antidiagonal_start
current_position = antidiagonal_start
if current_position.row == @size - 1 && current_position.column == @size - 1
@grid[current_position.row][current_position.column] = 1
else
loop do
value_below = if current_position.row + 1 >= @size
0
else
@grid[current_position.row + 1][current_position.column]
end
value_right = if current_position.column + 1 >= @size
0
else
@grid[current_position.row][current_position.column + 1]
end
@grid[current_position.row][current_position.column] = value_below + value_right
if last_in_antidiagonal?(current_position)
break
else
current_position = MatrixPosition.new(current_position.row-1, current_position.column + 1)
end
end
end
end
def last_in_antidiagonal? matrix_position
matrix_position.row - 1 < 0 || matrix_position.column + 1 >= size
end
end
# ============================== Solution Code ===============================
solution_grid = SolutionGrid.new 20
puts "The number of routes following the rules in the prompt for a 20 x 20 grid is: "
puts solution_grid.number_of_paths