patternswiftMinor
Swift Hackerrank 2D Array
Viewed 0 times
swiftarrayhackerrank
Problem
I solved this question in Swift, but was wondering if there is a better way or if my code can be improved:
Given a 6x6 2D Array:
We define an hourglass in to be a subset of values with indices
falling in this pattern in's graphical representation:
There are hourglasses in, and an hourglass sum is the sum of an hourglass' values.
Given a 6x6 2D Array:
1 1 1 0 0 0
0 1 0 0 0 0
1 1 1 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0
0 0 0 0 0 0We define an hourglass in to be a subset of values with indices
falling in this pattern in's graphical representation:
a b c
d
e f gThere are hourglasses in, and an hourglass sum is the sum of an hourglass' values.
import Foundation
var inputAsArray = [Int]()
for _ in 0...5 {
inputAsArray += readLine()!.componentsSeparatedByString(" ").map{ Int($0)! }
}
//var inputAsArray = [
// 1, 1, 1, 0, 0, 0,
// 0, 1, 0, 0, 0, 0,
// 1, 1, 1, 0, 0, 0,
// 0, 9, 2, -4, -4, 0,
// 0, 0, 0, -2, 0, 0,
// 0, 0, -1, -2, -4, 0
//]
// method creates all possible "hourglasses" and returns a nested array of hourglasses
typealias hourglass = [Int]
private func hourGlasses(input: [Int]) -> [hourglass] {
var hourglasses = [hourglass]() // initializes hourglass to hold array of hourglass
for index in 0.. Int {
let hourglasses: [hourglass] = hourGlasses(input)
// reduce the nested arrays to get sum in each. then compare that sum to maxSum
var maxSum = Int?() // set to optional instead of 0 because all numbers in input can be negative
for singleHourglass in hourglasses {
let singleHourglassSum = singleHourglass.reduce(0, combine: +) // reduce adds up sum for a single hourglass
if maxSum == nil || singleHourglassSum > maxSum {
maxSum = singleHourglassSum
}
}
return maxSum!
}
print(maxHourglassSum(inputAsArray))Solution
Your code works correctly, but can be improved (simplified and made clearer and faster).
Let't start with
it be too complicated.
actual index.
How would you extend this to 15x20 arrays? – Better iterate
over the permitted rows and columns:
Even better, replace the integer literals by descriptive constants:
Your
the sum of each inner array is computed and the maximal element determined.
It is more effective to sum the values of an "hour glass" immediately instead of
storing it as an array. To compute the maximum value, use the existing method
(Remark: The sum of is computed in three separate steps because otherwise the compiler
fails with an "Expression is too complex ..." error.)
This is short, clear, works with other dimentions as well, and is faster than your original code.
If even more speed is required then you can compute the maximum "on the fly" instead of storing the
values in an array first:
More suggestions: If you plan to use the code for other shapes as well
then use an array with the indices describing the shape:
Finally: The challenge is called "2D Array", so you might want to work with
a two-dimensional (i.e. a nested) array in Swift as well:
The sum calculation for a single hourglass then becomes more intuitive:
Test code:
Results (on a MacBook, compiled in Release mode):
The values did not change significantly when using a 2D (nested) array instead of a
1D (simple) array.
Let't start with
func hourGlasses(input: [Int]) -> [hourglass] {
var hourglasses = [hourglass]() // initializes hourglass to hold array of hourglass
for index in 0..<22 { // there are 36 indexes (6x6) but we +14 in the loop below when creating the hourglass so we only need to go up to 24. We can't make hourglass with 23 and 24, so we go up to 22
guard [4,5,10,11,16,17].indexOf(index) == nil else {continue} // continue to next iteration
// ...
}- There are far too many magic numbers.
- The fact that extened comments are necessary indicates that the approach
it be too complicated.
- I would consider this usage of
guarda bit artificial.
- Membership in a collection can be checked with
contains(), you don't need the
actual index.
How would you extend this to 15x20 arrays? – Better iterate
over the permitted rows and columns:
for row in 0 ..< 4 {
for col in 0 ..< 4 {
index = row * 4 + col
// ...
}
}Even better, replace the integer literals by descriptive constants:
let numRows = 6
let numCols = 6
for row in 0 ..< numRows - 2 {
for col in 0 ..< numCols - 2 {
let index = row * numCols + col
// ...
}
}Your
hourGlasses() function computes an array of arrays. Then in maxHourglassSum(),the sum of each inner array is computed and the maximal element determined.
It is more effective to sum the values of an "hour glass" immediately instead of
storing it as an array. To compute the maximum value, use the existing method
maxElement().func hourGlassSum(input: [Int], atIndex index: Int) -> Int {
var sum = input[index] + input[index + 1] + input[index + 2]
sum += input[index + numCols + 1]
sum += input[index + 2*numCols] + input[index + 2*numCols + 1] + input[index + 2*numCols + 2]
return sum
}
func maxHourglassSum(input: [Int]) -> Int {
var values: [Int] = []
for row in 0 ..< numRows - 2 {
for col in 0 ..< numCols - 2 {
let index = row * numCols + col
values.append(hourGlassSum(input, atIndex: index))
}
}
return values.maxElement()!
}(Remark: The sum of is computed in three separate steps because otherwise the compiler
fails with an "Expression is too complex ..." error.)
This is short, clear, works with other dimentions as well, and is faster than your original code.
If even more speed is required then you can compute the maximum "on the fly" instead of storing the
values in an array first:
func maxHourglassSum(input: [Int]) -> Int {
var maxSum = -100 // Any initial value maxSum {
maxSum = sum
}
}
}
return maxSum
}More suggestions: If you plan to use the code for other shapes as well
then use an array with the indices describing the shape:
let hourGlassShape = [0, 1, 2, 7, 12, 13, 14]
func hourGlassSum(input: [Int], atIndex index: Int) -> Int {
return hourGlassShape.reduce(0) { $0 + input[index + $1] }
}Finally: The challenge is called "2D Array", so you might want to work with
a two-dimensional (i.e. a nested) array in Swift as well:
let inputArray2D = [
[ 1, 1, 1, 0, 0, 0 ],
[ 0, 1, 0, 0, 0, 0 ],
[ 1, 1, 1, 0, 0, 0 ],
[ 0, 0, 2, 4, 4, 0 ],
[ 0, 0, 0, 2, 0, 0 ],
[ 0, 0, 1, 2, 4, 0 ]
]The sum calculation for a single hourglass then becomes more intuitive:
var sum = input[row][col] + input[row][col + 1]] + input[row][col + 2]
sum += input[row + 1][col + 1]
sum += input[row + 2][col] + input[row + 2][col + 1] + input[row + 2][col + 2]Test code:
var sum = 0
let startTime = NSDate()
for _ in 1 ... 100 {
sum += maxHourglassSum(inputAsArray)
}
let endTime = NSDate()
print(endTime.timeIntervalSinceDate(startTime) * 1000.0, "ms")Results (on a MacBook, compiled in Release mode):
- Original code: 0.8 ms
- First variation: 0.25 ms
- Second variation: 0.008 ms
The values did not change significantly when using a 2D (nested) array instead of a
1D (simple) array.
Code Snippets
func hourGlasses(input: [Int]) -> [hourglass] {
var hourglasses = [hourglass]() // initializes hourglass to hold array of hourglass
for index in 0..<22 { // there are 36 indexes (6x6) but we +14 in the loop below when creating the hourglass so we only need to go up to 24. We can't make hourglass with 23 and 24, so we go up to 22
guard [4,5,10,11,16,17].indexOf(index) == nil else {continue} // continue to next iteration
// ...
}for row in 0 ..< 4 {
for col in 0 ..< 4 {
index = row * 4 + col
// ...
}
}let numRows = 6
let numCols = 6
for row in 0 ..< numRows - 2 {
for col in 0 ..< numCols - 2 {
let index = row * numCols + col
// ...
}
}func hourGlassSum(input: [Int], atIndex index: Int) -> Int {
var sum = input[index] + input[index + 1] + input[index + 2]
sum += input[index + numCols + 1]
sum += input[index + 2*numCols] + input[index + 2*numCols + 1] + input[index + 2*numCols + 2]
return sum
}
func maxHourglassSum(input: [Int]) -> Int {
var values: [Int] = []
for row in 0 ..< numRows - 2 {
for col in 0 ..< numCols - 2 {
let index = row * numCols + col
values.append(hourGlassSum(input, atIndex: index))
}
}
return values.maxElement()!
}func maxHourglassSum(input: [Int]) -> Int {
var maxSum = -100 // Any initial value < -9*7 = -63
for row in 0 ..< numRows - 2 {
for col in 0 ..< numCols - 2 {
let index = row * numCols + col
let sum = hourGlassSum(input, atIndex: index)
if sum > maxSum {
maxSum = sum
}
}
}
return maxSum
}Context
StackExchange Code Review Q#135451, answer score: 4
Revisions (0)
No revisions yet.