Skip to content

Instantly share code, notes, and snippets.

@grantfree035
Created November 28, 2018 07:17
Show Gist options
  • Save grantfree035/2d623a06646dd3fb1961add0b74a7983 to your computer and use it in GitHub Desktop.
Save grantfree035/2d623a06646dd3fb1961add0b74a7983 to your computer and use it in GitHub Desktop.
Magic Matrix in Python 3
"""
magic square is a n x n matrix of distinct positive integers from 1 to n^2.
where the sum of any row, column, or diagonal of length n is always equal to
the same number: the magic constant.
The magic square has been well studied in history, so the magic constant can
be calculated: Magic Constant (M) = n * ((n^2 + 1) / 2).
In this problem n will be 3, which means we now know the magic constant:
n = 3
M = 15
Knowing that every sum is 15 we can come up with a configuration.
Since the center affects all sums, we know it must be 5 and the opposing edges
will add to 10, summing to 15. Here is an initial configuration:
5 5 5 x 1 x 8 1 6
5 5 5 => 3 5 7 => 3 5 7
5 5 5 x 9 x 4 9 2
All configurations are really the same order just flipped in some way.
Using reflections here are all of the possible configurations:
| 8 3 4 | 4 3 8 |
| 1 5 9 | 9 5 1 |
| 6 7 2 | 2 7 6 |
-------|-------|-------|-------
8 1 6 | | | 6 1 8
3 5 7 | | | 7 5 3
4 9 2 | | | 2 9 4
-------|-------|-------|-------
4 9 2 | | | 2 9 4
3 5 7 | | | 7 5 3
8 1 6 | | | 6 1 8
-------|-------|-------|-------
| 6 7 2 | 2 7 6 |
| 1 5 9 | 9 5 1 |
| 8 3 4 | 4 3 8 |
Problem:
You will be given a 3x3 matrix S of integers in the inclusive range [1,9].
We can convert any digit A to any other digit B in the range [1,9] at cost
of |A - B|. Given S, convert it into a magic square at minimal cost. Print
this cost on a new line.
Note: The resulting magic square must contain DISTINCT integers in the
inclusive range [1,9].
Solution:
It would take too much time to create an algorithm that would tweak the
numbers until it found an optimal solution of minimal cost. Instead, since
there are only 8 configurations, we are going to use a LOOKUP TABLE! fast
and efficient. Using the lookup table, we can calculate the cost of
converting to each configuration and pick the MINIMAL cost.
"""
class Magic(object):
# All 8 configurations
magic_square_lookup = [
[[8, 1, 6], [3, 5, 7], [4, 9, 2]],
[[6, 1, 8], [7, 5, 3], [2, 9, 4]],
[[4, 9, 2], [3, 5, 7], [8, 1, 6]],
[[2, 9, 4], [7, 5, 3], [6, 1, 8]],
[[8, 3, 4], [1, 5, 9], [6, 7, 2]],
[[4, 3, 8], [9, 5, 1], [2, 7, 6]],
[[6, 7, 2], [1, 5, 9], [8, 3, 4]],
[[2, 7, 6], [9, 5, 1], [4, 3, 8]],
]
@classmethod
def evaluate(cls, s):
"""
Evaluate the minimal cost to convert the muggle square into
a magic square
:param s: The input matrix S
:return: Minimal Cost
"""
# list of each configurations cost
costs = []
# iterate through each configuration
for m in cls.magic_square_lookup:
# cost to convert to this magic square
cost = 0
# group the rows of magic & muggle square and iterate
for m_row, s_row in zip(m, s):
# within grouped rows, group individual elements of
# magic & muggle and iterate
for m_el, s_el in zip(m_row, s_row):
# calculate conversion cost of each element
cost += abs(m_el - s_el)
# append conversion cost for given configuration
costs.append(cost)
# return minimal cost
return min(costs)
if __name__ == '__main__':
# Test Magic class
# test input
inputs = [
[[4, 9, 2], [3, 5, 7], [8, 1, 5]],
[[4, 8, 2], [4, 5, 7], [6, 1, 6]],
[[5, 3, 4], [1, 5, 8], [6, 4, 2]]
]
# expected output
expected = [1, 4, 7]
# test loop and console output
for test_case in range(len(expected)):
result = Magic.evaluate(inputs[test_case])
str_result = "Passed" if result == expected[test_case] else "Failed"
print(f'Test Case #{test_case}: {str_result}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment