Maximum Sub-Array (#131)

Here’s a solution which iterates over the array just once. Looks like
I came up with a variant of the algorithm presented by Henrik S.-
Møller, though I’m storing the local max sub array rather than just
its delimiting indices. I’m not happy with the calls to slice
(especially as they require calculating the size of the array), but
I’m pleased that I came up with a solution using recursion.

class Array
def max_sub_array
self.max_sub_arrayr[0]
end

def max_sub_arrayr
ary = self.clone
sub_ary = Array.new.push(ary.shift)
sum = sub_ary[0]
max_sub_ary = sub_ary.dup
max_sum = sum
done = false
ary.each_with_index do |n,i|
if sum > 0
if sum + n > 0
sum += n
sub_ary.push(n)
else
sub_ary, sum = ary.slice(i+1…(ary.size-1)).max_sub_arrayr
done = true
end
elsif sum <= n
sub_ary, sum = ary.slice(i…(ary.size-1)).max_sub_arrayr
done = true
end
if sum > max_sum
max_sum = sum
max_sub_ary = sub_ary.dup
break if done
end
end
return max_sub_ary, max_sum
end
end

Michael G.
grzm seespotcode net

On Sunday 15 July 2007 17:17, Michael G. wrote:

Here’s a solution which iterates over the array just once. Looks like
I came up with a variant of the algorithm presented by Henrik S.-
Møller, though I’m storing the local max sub array rather than just
its delimiting indices. I’m not happy with the calls to slice
(especially as they require calculating the size of the array), but
I’m pleased that I came up with a solution using recursion.

Not to be the downer who points out everyone’s problems, but here’s a
bug:

irb(main):008:0> arr = [46, -8, 43, -38, -34, -14, 10, -26, -9, -19,
-36, -6,
-20, -4, -23, -25, 48, -22, 22, 5, -21, -33, 37,
39,
-22, 11, -44, -40, -37, -26]
irb(main):009:0> arr.max_sub_array
=> [37, 39, 10]

That sequence does not appear in arr.

On Jul 15, 2007, at 20:19 , Jesse M. wrote:

Not to be the downer who points out everyone’s problems, but here’s
a bug:

No problem at all! Thanks for taking a look at my work :slight_smile:

irb(main):008:0> arr = [46, -8, 43, -38, -34, -14, 10, -26, -9,
-19, -36, -6,
-20, -4, -23, -25, 48, -22, 22, 5, -21, -33,
37, 39,
-22, 11, -44, -40, -37, -26]
irb(main):009:0> arr.max_sub_array
=> [37, 39, 10]

This should be a bit more pleasing :slight_smile:

irb(main):002:0> arr = [46, -8, 43, -38, -34, -14, 10, -26, -9, -19,
-36, -6,
-20, -4, -23, -25, 48, -22, 22, 5, -21,
-33, 37, 39,
-22, 11, -44, -40, -37, -26]
irb(main):005:0> arr.max_sub_array
=> [46, -8, 43]

Updated code follows below signature:

Michael G.
grzm seespotcode net

class Array
def max_sub_array
return [] if self.empty?
self.max_sub_arrayr[0]
end

def max_sub_arrayr
ary = self.clone
sub_ary = Array.new.push(ary.shift)
sum = sub_ary[0]
max_sub_ary = sub_ary.dup
max_sum = sum
done = false
ary.each_with_index do |n,i|
if sum > 0
if sum + n > 0
sum += n
sub_ary.push(n)
else
sub_ary, sum = ary.dup.slice(i…(ary.size-1)).max_sub_arrayr
done = true
end
elsif sum <= n
sub_ary, sum = ary.dup.slice(i…(ary.size-1)).max_sub_arrayr
done = true
end
if sum > max_sum
max_sum = sum
max_sub_ary = sub_ary.dup
end
break if done
end
return max_sub_ary, max_sum
end
end

Here’s my solution to the maximum sub-array problem. I’m sure my
algorithm is not optimal, but it’s readable and concise:

file: max_sub_array.rb

author: Drew O.

class Array

alias :orig_to_s :to_s

sum the integer values of array contents

def int_sum
self.inject(0){|sum,i| sum+i.to_i}
end

find the maximum sub array in an array

def max_sub_array
(0…self.size).inject([self.first]) do |max_sub,i|
(i…self.size).each do |j|
if max_sub.int_sum < self[i…j].int_sum
max_sub = self[i…j]
end
end
max_sub
end
end

pretty printing for array

def to_s
self.inject("[") do |str,i|
str + "#{i}, "
end[0…-2] + “]”
end
end

test example

if FILE == $0
my_arr = [-1, 2, 5, -1, 3, -2, 1]
puts “array: #{my_arr}”
puts “maximum sub-array: #{my_arr.max_sub_array}”
end

This was a nice diversion on Friday morning at the start of my kids’
championship swim meet. I had to work Saturday and Sunday so the
last two test cases had to wait until now (yes, I should be sleeping!).

-Rob

Rob B. http://agileconsultingllc.com
[email protected]

class Array

Given an Array of numbers, return the contiguous sub-array

having the

maximum sum of its elements. Longer sub-arrays are preferred.

#–

(or members of any Ring having operations ‘+’ (binary,

associative and

commutative) and ‘-’ (unary, giving the inverse with respect to

‘+’))
#++
def sub_max identity=0
return self if size < 2 # nothing to sum!

 ms = Array.new(size) { Array.new(size) {identity} }
 mx, range = self[0], 0..0
 0.upto(size-1) do |e|
   e.downto(0) do |s|
     current = ms[s][e] = if s == e
                            self[s]
                          else
                            ms[s][e-1] + ms[s+1][e] + (- ms[s+1]

[e-1])
end
if current > mx || current == mx && (e - s + 1) > (range.end

  • range.begin + 1)
    mx = current
    range = s…e
    end
    end
    end
    self[range]
    end
    end

if FILE == $0
require ‘test/unit’

class Array
def put2d
print '[ ’
each do |row|
row.put1d
print ",\n "
end
puts ‘]’
end

 def put1d
   print '[ '
   each do |item|
     print("%3d, " % item)
   end
   print ']'
 end

end

class SubMaxTest < Test::Unit::TestCase
def test_quiz_example
input = [-1, 2, 5, -1, 3, -2, 1]
expected = [2, 5, -1, 3]

   assert_equal expected, input.sub_max
 end

 def test_empty
   assert_equal [], [].sub_max
 end
 def test_single
   assert_equal [ 0], [ 0].sub_max
   assert_equal [-1], [-1].sub_max
   assert_equal [ 1], [ 1].sub_max
 end
 def test_all_positive
   input = [ 1, 2, 4, 8 ]
   assert_equal input, input.sub_max
 end
 def test_all_non_negative
   input = [ 1, 2, 0, 4 ]
   assert_equal input, input.sub_max
 end
 def test_all_negative
   input = [ -1, -2, -3, -9 ]
   assert_equal [-1], input.sub_max
   input = [ -2, -1, -3, -9 ]
   assert_equal [-1], input.sub_max, 'need to test diagonal'
 end
 def test_prefer_length_earlier
   input = [ -1, 0, 1, -2, -9 ]
   assert_equal [0, 1], input.sub_max, "if actual is [1], need to

add a length test on range"
end
def test_prefer_length_later
input = [ -1, 1, 0, -2, -9 ]
assert_equal [1, 0], input.sub_max, “if actual is [1], need to
add a length test on range”
end

 def test_prefer_length_multiple_options
   input = [ 1, 2, 3, -6, 6 ]
   # options
   # [6]
   # [1,2,3]
   expected = [ 1, 2, 3, -6, 6 ]
   assert_equal expected, input.sub_max, "if [6] or [1,2,3] you

can do better"
end
end
end
END

A late late entry (I was busy yesterday).
Yes it does an exhaustive search, but for even moderate sized sets,
it’s feasibly fast enough.

class Array
def sum()
s=0
each{|i| s+=i}
s
end
end

array=[-6,-5,-4,-3,-2,-1,0,1,2,3,-5,4,5,6]
maxIndex = array.length-1
sizeByRange = {}
0.upto(maxIndex) do
|start|
start.upto(maxIndex) do
|endI|
sizeByRange.store(array[start…endI].sum,start…endI)
#puts “subarray #{start} to #{endI} sums to
#{array[start…endI].sum}”
end
end

puts “Minimum array is [#{array[sizeByRange.min[1]].join(’,’)}]”
puts “Maximum array is [#{array[sizeByRange.max[1]].join(’,’)}]”

Solution #1: Array is bound to arr

sub_arrs = []
arr.each_index{|i| (i…arr.length).each{|i2| sub_arrs << arr[i…i2]}}
p sub_arrs.sort_by{|arr| arr.inject(0){|s,n|s+n}}.last

Solution #2: Array is bound to a

p
(b=(0…(l=a.size)).to_a).zip([b]*l).map{|(i,s)|s.map{|j|a[i,j]}}.sort_by{|a|a.map!{|a|[a.inject(0){|s,n|s+n},a]}.sort![-1][0]}[-1][-1][-1]

Just a note on my “solution” I sent in earlier… Well, it’s not
entirely correct… I’ve been shown at least one case that fails.
It’s probably not that far off, but unfortunately I don’t have time
this week to correct it… Just so no summaries go using it.

On Jul 17, 2007, at 11:12 AM, Matthew M. wrote:

Just a note on my “solution” I sent in earlier… Well, it’s not
entirely correct… I’ve been shown at least one case that fails.
It’s probably not that far off, but unfortunately I don’t have time
this week to correct it… Just so no summaries go using it.

Next week’s quiz: debug Matthew’s solution. :wink:

James Edward G. II

On 7/13/07, Ruby Q. [email protected] wrote:

Extra Credit:

Given a matrix of integers, find the rectangle with maximum sum.

This is roughly the same matrix solution I posted before.

Sorry for the double post. Two lines in the middle of the program were

too long and got wrapped in the post.

I made the lines shorter and tried again.

Here is my matrix solution.

It does the Matrix extra credit.

If there are multiple rectangles that equal max sum, it prints all of

them.

Since the quiz did not specify how to input,

I just hard coded a sample Matrix at the beginning.

require ‘matrix’
mat=Matrix[[7,-1,2,3,-4],[-7,8,-22,10,11],[3,15,16,17,-18],[4,22,-23,-24,-25]]
s = []
(0…mat.row_size).each do |a|
(0…mat.column_size).each do |b|
(1…mat.row_size).each do |x|
(1…mat.column_size).each do |y|
s << mat.minor(a,x,b,y)
end
end
end
end

tot = s.uniq.map {|x| x.to_a}
bg=tot.max{|x,y|x.flatten.inject(0){|a,b|a+b}<=>y.flatten.inject(0){|c,d|c+d}}
sb=tot.select{|r|r.flatten.inject(0){|a,b|a+b}==bg.flatten.inject(0){|c,d|c+d}}
puts “Original Matrix”
(0…mat.row_size).each do |x|
print mat.row(x).to_a.map{|m|
m.to_s.rjust(tot.flatten.max.to_s.length+2)},“\n”
end
puts
puts “Solutions”
sb.each do |x|
puts
x.each {|y| print y.map{|m|
m.to_s.rjust(tot.flatten.max.to_s.length+2)},“\n”}
end

Harry

this is my first entry
(only found this place last week)

so it’s late,
but I like it.

I deal with left-subarrays, and right-subarrays
(i decided that intuitively a max sub array is a max left of a max
right,
but have not the time to prove this)
I also assume that the empty array [] is always a member.

also, my max_left_of_right and max_right_of_left methods will return
multiple copies of the empty array if that is indeed the smallest.

I did initially uniq the result,
but decided against it in the end
(who says that the empty array is a unique sub array – probably me)

class Array

solution 1

def max_subs
max_found, max_instances = 0, [[]] # the empty sub array will
always return 0 sum
self.left_subs.each do |l_sub|
next if l_sub.last.nil? || l_sub.last < 0 # if
l_sub.right_subs.each do |sub|
next if sub.first.nil? || sub.first < 0
if (sub_sum = sub.sum) > max_found
max_found, max_instances = sub_sum, [sub]
elsif sub_sum == max_found
max_instances << sub
end
end
end
return max_instances
end

i hypothesise that each max sub array is actually;

a max left sub of a max right sub, and the other way round

but i dont have time to prove it

solutions 2 and 3

def max_left_of_right
max_right_subs.inject([]){|rtn, max_r| rtn + max_r.max_left_subs}
end

def max_right_of_left
max_left_subs.inject([]){|rtn, max_l| rtn + max_l.max_right_subs}
end

sub methods

def left_subs
if (l_sub = self.dup) && l_sub.pop
return (l_sub.left_subs << self.dup)
else
return [self]
end
end

def right_subs
if (r_sub = self.dup) && r_sub.shift
return (r_sub.right_subs << self.dup)
else
return [self]
end
end

def sum
self.inject(0){|sum, element| sum+element}
end

def max_left_subs
max_of_subs(:left_subs)
end

def max_right_subs
max_of_subs(:right_subs)
end

def max_of_subs(method)
max_found, max_instances = 0, [[]] # we expect to have an empty sub
self.send(method).each do |sub|
if (sub_sum = sub.sum) > max_found
max_found, max_instances = sub_sum, [sub]
elsif sub_sum == max_found
max_instances << sub
end
end
return max_instances
end
end

On Jul 18, 2007, at 9:17 AM, Matthew M. wrote:

Eh… I got some better quizzes in mind than debugging my lousy
code… I just have to write them up. (This thing called “day job”
keeps getting in the way…)

I always enjoy your quizzes Matthew. Looking forward to it.

James Edward G. II

Eh… I got some better quizzes in mind than debugging my lousy
code… I just have to write them up. (This thing called “day job”
keeps getting in the way…)

Oh, these quizzes. I need to either find more time to play with them
or stop telling myself I don’t have the time. I did this in Perl for
a job interview a while ago and happened to still have it handy, so
all I did was translate it (somewhat) into Ruby.


class Array
def largest_sum_sequence
# initialize with a sequence of the first number
largest = {
:sum => first,
:start => 0,
:end => 0
}

(0 .. length-1).each do |start_i|
  sum = 0
  start_num = self[start_i]

  # don't bother with a sequence that starts with a negative number
  # but what if all the numbers are negative?
  next if largest[:sum] > start_num and start_num < 0

  (start_i .. length-1).each do |end_i|
    end_num = self[end_i]
    sum += end_num

    # if this sequence is the largest so far
    if sum > largest[:sum]
      largest[:sum]   = sum
      largest[:start] = start_i
      largest[:end]   = end_i
    end
  end
end

puts "Largest sum: #{largest[:sum]}"
puts "The sequence starts at element #{largest[:start]} and goes to

element #{largest[:end]}"
puts “The sequence is #{self[largest[:start] …
largest[:end]].join(’ ')}”
end
end

numbers = ARGV.collect { |arg| arg.to_i }

numbers.largest_sum_sequence

Am I missing something, or is this one of the easiest quizzes that’s
been put forward?

Well, you’re missing the fizzfuzz quiz.

On Jul 13, 2007, at 11:10 AM, Kyle S. wrote:

Am I missing something, or is this one of the easiest quizzes that’s
been put forward?

It’s a pretty easy problem. I almost rejected it for that reason.

I was just sure I could do it with a one-liner, but when my own
solution didn’t make it down to that I accepted the problem. I’m
sure someone will get it down there, but I didn’t so it required a
touch more thought than I expected.

We’ve had some pretty easy problems. FizzBuzz was pretty close to
this level.

I’m fine with that thought. Ruby Q. is for all levels.

James Edward G. II

On Fri, 13 Jul 2007 19:47:11 +0200, anansi wrote:

but why is [2, 5, -1, 3] sum= 9 the sub-array with the maximum sum?
wouldn’t be [2,5,3,1] sum=11 the right solution?

Because the subarrays in the quiz problem must be contiguous.

The answer to your proposed version is
ARRAY=[-1, 2, 5, -1, 3, -2, 1]
ARRAY.select{|x| x>=0}

–Ken

Kyle S. wrote, On 7/13/2007 11:10 AM:

Am I missing something, or is this one of the easiest quizzes that’s
been put forward?

–Kyle

Well, you could put a maximum time complexity constraint on it and the
solutions aren’t as obvious (unless you’ve seen it before)

could someone explain this please? I really don’t understand this quiz?

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]

I know what an array is :slight_smile: I know what integers are :slight_smile: I know what a sum
is :slight_smile:

but why is [2, 5, -1, 3] sum= 9 the sub-array with the maximum sum?
wouldn’t be [2,5,3,1] sum=11 the right solution?


greets

                 one must still have chaos in oneself to be able to

give birth to a dancing star

On Jul 13, 2007, at 2:29 PM, Ari B. wrote:

Being a nub at life, liberty, and ruby, what is the best way to
search it? Would it be to go and sum up the elements in every
possible array (ie, [abc] => [a]. [b]. [c]. [ab]. [bc])? Because
that seems like it would get very CPU consuming with larger arrays.

An exhaustive search would have execution time on the order of N2,
where N is the length of the array. That’s not great but its not
horrible either – that is, not nearly so bad as a search that takes
exponential (e
N) time.

Regards, Morton