This challenge is lifted from RubyQuiz #131. You can visit that site if you need spoilers. :D
Given an array of integers, find the sub-array with maximum sum.
For example:
array: [-1, 2, 5, -1, 3, -2, 1] maximum sub-array: [2, 5, -1, 3]
You can use any language you want.
Go.
[jop]
my solution:
public class MaxSubArray { public int[] GetMaxSubArrayFor(int[] input) { int offsetLength = 0; int offset = 0; int maxSum = int.MinValue; for (int candidateOffset = 0; candidateOffset < input.Length; candidateOffset++) { int currentSum = 0; for (int candidateLength = 1; candidateLength <= input.Length - candidateOffset; candidateLength++) { currentSum += input[candidateOffset + candidateLength - 1]; if ((currentSum > maxSum) || ((currentSum == maxSum) && (candidateLength > offsetLength))) { maxSum = currentSum; offsetLength = candidateLength; offset = candidateOffset; } } } return GetSubsetOf(input, offset, offsetLength); } public static int[] GetSubsetOf(int[] input, int offset, int length) { int[] outputArray = new int[length]; for (int index = offset; index < offset + length; index++) { outputArray[index - offset] = input[index]; } return outputArray; } }
note: edited to take into account leading/trailing zeroes
test suite:
[TestFixture] public class TestMaxSubArray { private MaxSubArray subFinder; [SetUp] public void Init() { subFinder = new MaxSubArray(); } [Test] public void ShouldHaveZeroSubsetIfBlank() { int[] sub = subFinder.GetMaxSubArrayFor(GetBlankInput()); Assert.That(sub.Length, Is.EqualTo(0)); } [Test] public void ShouldHaveOnlyOneIfAllNegative() { int[] sub = subFinder.GetMaxSubArrayFor(GetAllNegativeInput()); Assert.That(sub.Length, Is.EqualTo(1)); Assert.That(sub[0], Is.EqualTo(-1)); } [Test] public void ShouldHaveWholeIfAllPositive() { int[] allPositive = GetAllPositiveInput(); int[] sub = subFinder.GetMaxSubArrayFor(allPositive); Assert.That(sub, Is.EquivalentTo(allPositive)); } [Test] public void ShouldGetJopsResult() { int[] jopSolution = {2, 5, -1, 3}; int[] sub = subFinder.GetMaxSubArrayFor(GetJopTestInput()); Assert.That(sub, Is.EquivalentTo(jopSolution)); } private int[] GetBlankInput() { int[] blankInput = new int[0]; return blankInput; } private int[] GetAllNegativeInput() { int[] allNegative = {-1, -2, -3, -4, -5}; return allNegative; } private int[] GetAllPositiveInput() { int[] allPositive = {1, 2, 3, 4, 5, 6}; return allPositive; } private int[] GetJopTestInput() { int[] jopTest = {-1, 2, 5, -1, 3, -2, 1}; return jopTest; } }
here's my more OOP solution, utilizing two classes named ArrayWithSum and ListOfArrayWithSum. if my C# had extension methods now, this would have been shorter ;)
public class ArrayWithSum : List<int> { public ArrayWithSum(IEnumerable<int> input) : base(input) { } public ArrayWithSum() { } public int Sum { get { int sum = 0; foreach (int item in this) { sum += item; } return sum; } } public bool IsBlank { get { return (Count == 0); } } public bool IsBiggerThan(ArrayWithSum another) { return (Sum > another.Sum) || ((Sum == another.Sum) && (Count > another.Count)) || (!IsBlank && another.IsBlank); } public new ArrayWithSum GetRange(int index, int count) { return new ArrayWithSum(base.GetRange(index, count)); } public ListOfArrayWithSum GetSubArrays() { ListOfArrayWithSum subArrays = new ListOfArrayWithSum(); for (int index = 0; index < Count; index++) { for (int length = 1; length <= Count - index; length++) { subArrays.Add(GetRange(index, length)); } } return subArrays; } public ArrayWithSum GetMaxSubArray() { return GetSubArrays().GetMaxSubArray(); } }
public class ListOfArrayWithSum : List<ArrayWithSum> { public ListOfArrayWithSum(IEnumerable<ArrayWithSum> input) : base(input) { } public ListOfArrayWithSum() { } public ArrayWithSum GetMaxSubArray() { ArrayWithSum item = new ArrayWithSum(); foreach (ArrayWithSum candidate in this) { if (candidate.IsBiggerThan(item)) { item = candidate; } } return item; } }
and test cases for this "more OOP" solution...
[TestFixture] public class TestNewMaxSubArray { [Test] public void TestCreateEnhancedArray() { ArrayWithSum arr = new ArrayWithSum(GetAllNegativeInput()); Assert.That(arr.Sum, Is.EqualTo(-15)); } [Test] public void TestGetSubsetOfArray() { ArrayWithSum arr = new ArrayWithSum(GetAllNegativeInput()); ArrayWithSum subArr = arr.GetRange(2, 3); Assert.That(subArr.Sum, Is.EqualTo(-12)); } [Test] public void TestGetRangesOfArrayWithSum() { ArrayWithSum arr = new ArrayWithSum(GetAllNegativeInput()); ListOfArrayWithSum subArrays = arr.GetSubArrays(); Assert.That(subArrays.Count, Is.EqualTo(15)); } [Test] public void TestGetMaxSubArray() { ArrayWithSum arr = new ArrayWithSum(GetAllNegativeInput()); ArrayWithSum maxSub = arr.GetSubArrays().GetMaxSubArray(); Assert.That(maxSub.Count, Is.EqualTo(1)); Assert.That(maxSub.Sum, Is.EqualTo(-1)); } [Test] public void TestCase1() { int[] input = new int[0]; ArrayWithSum maxSub = new ArrayWithSum(input).GetMaxSubArray(); Assert.That(maxSub.Count, Is.EqualTo(0)); Assert.That(maxSub.Sum, Is.EqualTo(0)); } [Test] public void TestCase2() { int[] input = {-1, 2, 5, -1, 3, -2, 1}; int[] solution = {2, 5, -1, 3}; ArrayWithSum maxSub = new ArrayWithSum(input).GetMaxSubArray(); Assert.That(maxSub.ToArray(), Is.EquivalentTo(solution)); } [Test] public void TestCase3() { int[] input = {2, 0, -1}; int[] solution = {2, 0}; ArrayWithSum maxSub = new ArrayWithSum(input).GetMaxSubArray(); Assert.That(maxSub.ToArray(), Is.EquivalentTo(solution)); } [Test] public void TestCase4() { int[] input = {0, 2, 0, -1}; int[] solution = {0, 2, 0}; ArrayWithSum maxSub = new ArrayWithSum(input).GetMaxSubArray(); Assert.That(maxSub.ToArray(), Is.EquivalentTo(solution)); } private static int[] GetAllNegativeInput() { int[] allNegative = {-1, -2, -3, -4, -5}; return allNegative; } }
Here is my solution written using the Language of the Gods :D
TestMaxSubArray>>testMaxSubArray "Test cases" self should: [#(1 2 3 4 5) = #(1 2 3 4 5) maxSubArray]. self should: [#(0 2) = #(-1 0 2) maxSubArray]. self should: [#(2 0) = #(2 0 -1) maxSubArray]. self should: [#(2 5 -1 3) = #(-1 2 5 -1 3 -2 1) maxSubArray]. Array>>maxSubArray "Add method to Array. Look for subarray with the highest sum" | result | result := #(0 ). self forEachSubArray: [ :candidate | (candidate isGreaterThan: result) ifTrue: [result := candidate]]. ^ result Array>>forEachSubArray: aBlock "Add method to Array. Execute aBlock for each subarray that can be found" 1 to: self size do: [:start | start to: self size do: [:end | aBlock value: (self copyFrom: start to: end)]]. Array>>isGreaterThan: anArray ^(self sum > anArray sum or: [self sum = anArray sum and: [self size > anArray size]]).
cruizer:we're not worthy!!! grabe nagmukhang basang sisiw yun solution ko kumpara sa yo (in smalltalk) ah!
jop: cruizer:we're not worthy!!! grabe nagmukhang basang sisiw yun solution ko kumpara sa yo (in smalltalk) ah! Yung C# version ko, kaparehas lang nung gawa mo kaya di ko na ipo-post.
Oo nga! bangis nung kay jop! I tried doing this in C# but wasn't able to finish it due to time constraints. Lupet!
cruizer:baka medyo kalawang ka pa keith dahil sa honeymoon, ha ha
Hahah, I'm just not in the zone yet to work ;). Yes jop i think it can be done in SQL..