[QUIZ][SOLUTION]Re: Magic Squares (#124)
- From: akbarhome <akbarhome@xxxxxxxxx>
- Date: 20 May 2007 05:52:22 -0700
On May 18, 6:57 pm, Ruby Quiz <j...@xxxxxxxxxxxxxxxxxxx> wrote:
The three rules of Ruby Quiz:
1. Please do not post any solutions or spoiler discussion for this quiz until
48 hours have passed from the time on this message.
2. Support Ruby Quiz by submitting ideas as often as you can:
http://www.rubyquiz.com/
3. Enjoy!
Suggestion: A [QUIZ] in the subject of emails about the problem helps everyone
on Ruby Talk follow the discussion. Please reply to the original quiz message,
if you can.
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
A magic square of size N is a square with the numbers from 1 to N ** 2 arranged
so that each row, column, and the two long diagonals have the same sum. For
example, a magic square for N = 5 could be:
+------------------------+
| 15 | 8 | 1 | 24 | 17 |
+------------------------+
| 16 | 14 | 7 | 5 | 23 |
+------------------------+
| 22 | 20 | 13 | 6 | 4 |
+------------------------+
| 3 | 21 | 19 | 12 | 10 |
+------------------------+
| 9 | 2 | 25 | 18 | 11 |
+------------------------+
In this case the magic sum is 65. All rows, columns, and both diagonals add up
to that.
This week's Ruby Quiz is to write a program that builds magic squares. To keep
the problem easy, I will say that your program only needs to work for odd values
of N. Try to keep your runtimes pretty reasonable even for the bigger values of
N:
$ time ruby magic_square.rb 9
+--------------------------------------------+
| 45 | 34 | 23 | 12 | 1 | 80 | 69 | 58 | 47 |
+--------------------------------------------+
| 46 | 44 | 33 | 22 | 11 | 9 | 79 | 68 | 57 |
+--------------------------------------------+
| 56 | 54 | 43 | 32 | 21 | 10 | 8 | 78 | 67 |
+--------------------------------------------+
| 66 | 55 | 53 | 42 | 31 | 20 | 18 | 7 | 77 |
+--------------------------------------------+
| 76 | 65 | 63 | 52 | 41 | 30 | 19 | 17 | 6 |
+--------------------------------------------+
| 5 | 75 | 64 | 62 | 51 | 40 | 29 | 27 | 16 |
+--------------------------------------------+
| 15 | 4 | 74 | 72 | 61 | 50 | 39 | 28 | 26 |
+--------------------------------------------+
| 25 | 14 | 3 | 73 | 71 | 60 | 49 | 38 | 36 |
+--------------------------------------------+
| 35 | 24 | 13 | 2 | 81 | 70 | 59 | 48 | 37 |
+--------------------------------------------+
real 0m0.012s
user 0m0.006s
sys 0m0.006s
For extra credit, support even values of N. You don't need to worry about N = 2
though as it is impossible.
Here it is:
# magic_square.rb
# Magic Square with Odd Number
class OddMagicSquare
attr_reader :square
def initialize(n)
@square = Array.new(n)
@square.each_index {|i| @square[i] = Array.new(n)}
middle = n/2
@square[0][middle] = 1
@pos = [0,middle]
@len = n
end
def printing_magic_square
v_border = '+' + '-' * (6 * @len - 1) + '+'
@square.each do |row|
puts v_border
row.each do |r|
if r then
print format('|' + "%4d" + ' ', r)
else
print '| nil '
end
end
print "|\n"
end
puts v_border
end
def iterate_square
value = 2
last_value = @len ** 2
while true do
move
fill value
break if value == last_value
value = value + 1
end
end
private
def fill(value)
@square[@pos[0]][@pos[1]] = value
end
def move
move_down if not move_diagonal_up
end
def move_diagonal_up
# get future position
future_pos = Array.new(2)
@pos[0] == 0 ? future_pos[0] = @len - 1 : future_pos[0] = @pos[0]
- 1
@pos[1] == @len - 1 ? future_pos[1] = 0 : future_pos[1] = @pos[1]
+ 1
# check if it is empty or not
if @square[future_pos[0]][future_pos[1]] then
return false
else
@pos = future_pos
end
return true
end
def move_down
@pos[0] == @len - 1 ? @pos[0] = 0 : @pos[0] = @pos[0] + 1
end
end
The test case:
#tc_magic_square.rb
require 'test/unit'
require 'magic_square'
class TestOddMagicSquare < Test::Unit::TestCase
def setup
@n = 5
@odd_magic_square = OddMagicSquare.new(@n)
@odd_magic_square.iterate_square
@square = @odd_magic_square.square
@sum = (@n ** 2 / 2 + 1) * @n
end
def test_sum_row_and_col
@n.times do |t|
assert_equal @sum, get_sum_column(t)
assert_equal @sum, get_sum_row(t)
end
assert_equal @sum, get_sum_diagonal('left')
assert_equal @sum, get_sum_diagonal('right')
end
private
def get_sum_column(i)
sum = 0
@n.times do |t|
sum += @square[t][i]
end
sum
end
def get_sum_row(i)
sum = 0
@n.times do |t|
sum += @square[i][t]
end
sum
end
def get_sum_diagonal(alignment)
if alignment == 'left' then
sum = i = 0
@n.times do |t|
sum += @square[i][i]
i = i + 1
end
return sum
elsif alignment == 'right' then
sum = 0
i = @n - 1
@n.times do |t|
sum += @square[i][i]
i = i - 1
end
return sum
else
raise 'Alignment must be left or right.'
end
end
end
Here how it is run:
# use_magic_square.rb
require 'magic_square'
# getting input
n = ARGV[0].to_i
# input must be odd and bigger than 2
raise 'Argument must be odd and bigger than 2' if n % 2 == 0 or n < 3
odd_magic_square = OddMagicSquare.new(n)
odd_magic_square.iterate_square
odd_magic_square.printing_magic_square
$ ruby use_magic_square.rb 5
+-----------------------------+
| 17 | 24 | 1 | 8 | 15 |
+-----------------------------+
| 23 | 5 | 7 | 14 | 16 |
+-----------------------------+
| 4 | 6 | 13 | 20 | 22 |
+-----------------------------+
| 10 | 12 | 19 | 21 | 3 |
+-----------------------------+
| 11 | 18 | 25 | 2 | 9 |
+-----------------------------+
.
- Follow-Ups:
- [SOLUTION]Re: Magic Squares (#124)
- From: Lloyd Linklater
- [SOLUTION]Re: Magic Squares (#124)
- References:
- [QUIZ] Magic Squares (#124)
- From: Ruby Quiz
- [QUIZ] Magic Squares (#124)
- Prev by Date: Re: Parser or Programmer? (was: Re: Why not adopt "Python Style" indentation for Ruby?)
- Next by Date: Re: Library to create MS Excel / OpenOffice calc files?
- Previous by thread: Re: Magic Squares (#124)
- Next by thread: [SOLUTION]Re: Magic Squares (#124)
- Index(es):
Relevant Pages
|