Home > OS >  Finding subsets from array
Finding subsets from array

Time:05-10

Scenario : I want to count no. of subsets having negative numbers between two zeros. Here no. of subsets with negative numbers can be consider as travelling down the hill from sea level

Consider a array

a=[0,1,0,-1,-2,-l,0]

0 here represent sea level. Numbers between zeros are steps going up/down and is represent by positive/negative consecutive steps. You can move any steps up/down but it should come to sea level before moving up/down

If you consider 0,1,0 then it means that you moved from sea level to uphill and then again to sea level

0, 1, 0

similarly

From sea level to moving down one step, second step down, one step up and then again to sea level

0, -1, -2, -1, 0

Now considering an example.

 a=[0,1,0,-1,-2,-1,0]

 subset #1 = [0,1]
 subset #2 = [0,-1,-2,-1,0]

Output: 1 (One time travelling below sea level)

Consider another example

b = [0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0]

subset #1 = [0,1,2,1,0] (moving up)
subset #2 = [0,-1,-2,-1,0] (moving down)
subset #3 = [0,1,2,1,0] (moving up)
subset #4 = [0,-1,0] (moving down)

Output: 2 (Two times travelling below sea level)

CodePudding user response:

I'm wondering if there's a reason that you couldn't just count the times that you have a zero followed by a negative number; something like:

def count_below(arr)
    count = 0
    arr.each_index do |i|
        count  = 1 if arr[i] == 0 && (arr[i   1] || 0) < 0
    end
    count
end
irb(main):039:0> count_below([0,1,0,-1,-2,-1,0])
=> 1
irb(main):040:0> count_below([0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0])
=> 2

CodePudding user response:

Option 1: Produce Desired "Output" (Number of times below "Sea Level")

Assumptions:

  1. We start at or above Sea Level.

Proposed Solution:

b = [0,1,2,1,0,-1,-2,-1,0,1,2,1,0,-1,0]
b.each_cons(2).count {|a,b| !a.negative? && b.negative? }

Steps:

  • Create an Enumerator of consecutive 2 elements (b.each_cons(2))
  • Count each time the first element is not negative and the second is negative (count {|a,b| !a.negative? && b.negative? })

Option 2: To produce the slices in your question the following should work

Assumptions:

  1. We always start at Sea Level (0)
  2. One must touch Sea Level when ascending or descending (e.g. [0,1,-1] is considered invalid input)
  3. The first example is incorrect and should be [[0,1,0],[0,-1,-2,-1,0]]

Proposed Solution:

b.each_cons(2).with_object([]) do |(a,b),obj|
  obj << [a] if a.zero?
  obj.last << b
end
#=> [[0, 1, 2, 1, 0], [0, -1, -2, -1, 0], [0, 1, 2, 1, 0], [0, -1, 0]]

Steps:

  • Create an Enumerator of consecutive 2 elements (b.each_cons(2))
  • Iterate with a Accumulator Array (with_object([]))
  • Every time the first element is 0 insert a new sub Array (obj << [a] if a.zero?)
  • Insert the second element into the current (last) sub Array (obj.last << b)
  • with_object will return the accumulator Array as the result

You could chain methods to get the same result as Option 1 (such as count {|a| a.any?(&:negative)})

  • Related