Algorithm Backtracking: How to do recursion without storing state - ruby

Normally in backtracking, we take a helper function which takes in an initial state and each recursive call takes care of its own computation and pass the result to the next recursion call. Theoretically, we denote this through unseen and seen variable.
For example, in permutation for a string we will use this program:
def permute(str)
return str if str.length < 2
permute_helper(str, "")
end
def permute_helper(unseen, seen)
#base case
if unseen.length <= 0
p seen
return
else
(0..unseen.length-1).each do |i|
buffer = unseen
buffer = buffer.split('')
buffer.delete_at(i)
buffer = buffer.join('')
permute_helper(buffer, seen+unseen[i])
end
end
end
permute('abc')
Thie will print out the required results.
I was asked to do this without using two variables in a recent interview. without storing state in the seen variable. I couldn't think through the whole at that time but I would like to ask how to do backtracking without storing states ?

The permutations of the string "cd" is ["cd", "dc"]. If we now wish to obtain the permutations of the string "bcd" we simply replace each element of this array with three strings, each having "b" at a different position. "cd" becomes "bcd", "cbd" and "cdb" and "dc" becomes "bdc", "dbc" and "dba". The permutations of "bcd" are therefore
["bcd", "cbd", "cdb", "bdc", "dbc", "dba"]
If we now wish to obtain the permutations of "abcd", we replace each element of the above six-element array with four strings, each with "a" in a different position. For example, "bcd" becomes "abcd", "bacd", "bcad" and "bcda". The structure of the recursion should now be obvious.
def permute(str)
case str.length
when 0, 1
str
when 2
[str, str.reverse]
else
first = str[0]
sz = str.size-1
permute(str[1..-1]).flat_map { |s| (0..sz).map { |i| s.dup.insert(i,first) } }
end
end
permute('')
#=> ""
permute('a')
#=> "a"
permute('ab')
#=> ["ab", "ba"]
permute('abc')
#=> ["abc", "bac", "bca", "acb", "cab", "cba"]
permute('abcd')
#=> ["abcd", "bacd", "bcad", "bcda", "acbd", "cabd", "cbad", "cbda",
# "acdb", "cadb", "cdab", "cdba", "abdc", "badc", "bdac", "bdca",
# "adbc", "dabc", "dbac", "dbca", "adcb", "dacb", "dcab", "dcba"]
str is of course the "unseen" variable.

#CarySwoveland's answer an explanation is awesome, per usual. For those looking to permute an array, consider this functional approach. While this uses an auxiliary lambda all_pos, no extra state parameter is used to accumulate the result.
def permute ((x, *xs))
all_pos = lambda do |(y,*ys)|
if y.nil?
[[ x ]]
else
[[ x, y, *ys ]] + (all_pos.call ys) .map { |rest| [ y, *rest ] }
end
end
if x.nil? or xs.empty?
[[x]]
else
(permute xs) .flat_map &all_pos
end
end
permute [1,2,3,4]
# [ [1, 2, 3, 4]
# , [2, 1, 3, 4]
# , [2, 3, 1, 4]
# , [2, 3, 4, 1]
# , [1, 3, 2, 4]
# , [3, 1, 2, 4]
# , [3, 2, 1, 4]
# , [3, 2, 4, 1]
# , [1, 3, 4, 2]
# , [3, 1, 4, 2]
# , [3, 4, 1, 2]
# , [3, 4, 2, 1]
# , [1, 2, 4, 3]
# , [2, 1, 4, 3]
# , [2, 4, 1, 3]
# , [2, 4, 3, 1]
# , [1, 4, 2, 3]
# , [4, 1, 2, 3]
# , [4, 2, 1, 3]
# , [4, 2, 3, 1]
# , [1, 4, 3, 2]
# , [4, 1, 3, 2]
# , [4, 3, 1, 2]
# , [4, 3, 2, 1]
# ]

Related

Ruby inject daisy chaining?

I'm not sure what sugar syntax this is, but let me just show you the problem.
def factors num
(1..num).select {|n| num % n == 0}
end
def mutual_factors(*nums)
nums
.map { |n| factors(n) }
.inject(:&)
end
p mutual_factors(50, 30) # [1, 2, 5, 10]
p mutual_factors(50, 30, 45, 105) # [1, 5]
p mutual_factors(8, 4) # [1, 2, 4]
p mutual_factors(8, 4, 10) # [1, 2]
p mutual_factors(12, 24) # [1, 2, 3, 4, 6, 12]
p mutual_factors(12, 24, 64) # [1, 2, 4]
p mutual_factors(22, 44) # [1, 2, 11, 22]
p mutual_factors(22, 44, 11) # [1, 11]
p mutual_factors(7) # [1, 7]
p mutual_factors(7, 9) # [1]
with this being the portion in questioning:
nums
.map { |n| factors(n) }
.inject(:&)
okay, so this is my mental trace: first, map uses the helper method to get the factors, and outputs the factors into another array, and then that array gets injected?
I think the
.inject(:&)
is what is throwing me off. I ran a quick google on it, but I haven't used inject for many things other than summing arrays, and basic stuff like that. I've also done things like
test = "hello".split("").map(&:upcase)
p test.join
but .inject(:&)? I know & is a proc, but I've only used them in arguments. I don't know the fundamentals under the hood. Please, take my current level into mind when trying to explain this to me =), I know how the basic inject works, and the splat operator also.
Partial quote form the documentation of Enumerable#inject.
inject(symbol) → object
[...]
Returns an object formed from operands via either:
A method named by symbol.
[...]
With method-name argument symbol, combines operands using the method:
# Sum, without initial_operand.
(1..4).inject(:+) # => 10
That means in the context of inject the (:&) is not a proc but simply the symbol :& that tells inject what operation to perform to combine the elements in the array.
Let's look at this example:
mutual_factors(8, 4, 10)
#=> [1, 2]
and let's look what happens at each step:
nums
.map { |n| factors(n) } #=> [[1, 2, 4, 8], [1, 2, 4], [1, 2, 5, 10]]
.inject(:&) #=> [1, 2, 4, 8] & [1, 2, 4] & [1, 2, 5, 10]
And Array#& is a method that returns a new array containing each element found in both arrays (duplicates are omitted).

Extract items between 2 numbers in Ruby

Problem:
Given an array of numbers in Ruby, return the groups of numbers that appear between 1 and 2.
The numbers 1 and 2 do not appear in between other 1's and 2's (there are no subsets of subsets).
Example 1
input: [1, 3, 2, 1, 4, 2]
output: [[1, 3, 2], [1, 4, 2]]
Example 2
input: [0, 1, 3, 2, 10, 1, 5, 6, 7, 8, 7, 5, 2, 3, 1, -400, 2, 12, 16]
output: [ [1, 3, 2], [1, 5, 6, 7, 8, 7, 5, 2], [1, -400, 2] ]
My hunch is to use a combination of #chunk and #drop_while or a generator.
Thanks in advance.
This is an option using [Enumerable#slice_when][1]:
ary1 = [1, 3, 2, 1, 4, 2]
ary2 = [0, 1, 3, 2, 10, 1, 5, 6, 7, 8, 7, 5, 2, 3, 1, -400, 2, 12, 16]
For example:
stop = [1, 2]
ary2.slice_when{ |e| stop.include? e }
.each_slice(2).map { |a, b| b.unshift(a.last) if b }
.reject { |e| e.nil? || (e.intersection stop).empty? }
#=> [[1, 3, 2], [1, 5, 6, 7, 8, 7, 5, 2], [1, -400, 2]]
Other option
More verbose but clearer, given the input:
input = %w(b a b c a b c a c b c a c a)
start = 'a'
stop = 'b'
Using Enumerable#each_with_object, why not use the good old if then else?:
tmp = []
pickup = false
input.each_with_object([]) do |e, res|
if e == start
pickup = true
tmp << e
elsif pickup && e == stop
tmp << e
res << tmp
tmp = []
pickup = false
elsif pickup
tmp << e
end
end
#=> [["a", "b"], ["a", "b"], ["a", "c", "b"]]
[1]: https://ruby-doc.org/core-2.7.0/Enumerable.html#method-i-slice_when
Sounds like an interview question. I'll explain the simplest algorithm I can think of:
You loop through the array once and build the output as you go. When you encounter 1, you store it and the subsequent numbers into another temporary array. When you encounter 2, you put the array in the output array. The edge cases are:
another 1 after you start building the temporary array
a 2 when you don't have a temporary array
First case is easy, always build a new temp array when you encounter a 1. For the second one, you have to check whether you have any items in your temporary array and only append the temp array to your output if it's not empty.
That should get you started.
You could use chunk and Ruby's flip-flop operator:
input = [0, 1, 3, 2, 10, 1, 5, 6, 7, 8, 7, 5, 2, 3, 1, -400, 2, 12, 16]
input.chunk { |i| true if i==1..i==2 }.each { |_, ary| p ary }
Output:
[1, 3, 2]
[1, 5, 6, 7, 8, 7, 5, 2]
[1, -400, 2]
For all people wanting to take a walk on the beach but for obvious reasons can't:
class Flipflop
def initialize(flip, flop) #flip and flop being boolean-returning lambdas
#state = false
#flip = flip
#flop = flop
end
def flipflop(x) #logic taken from The Ruby Programming Language page 111
if !#state
result = #flip[x]
if result
#state = !#flop[x]
end
result
else
#state = !#flop[x]
true
end
end
end
ff = Flipflop.new( ->(x){x == 1}, ->(x){x == 2} )
input = [0, 1, 3, 2, 10, 1, 5, 6, 7, 8, 7, 5, 2, 3, 1, -400, 2, 12, 16]
res = input.select{|el| ff.flipflop(el) }.slice_before(1) #an Enumerator
p res.to_a
# =>[[1, 3, 2], [1, 5, 6, 7, 8, 7, 5, 2], [1, -400, 2]]
For strings, ff = Flipflop.new( ->(x){x.chomp == "BEGIN"}, ->(x){x.chomp == "END"} ) or something like that should work.
Since you commented and added that you are actually reading a file, I deleted my old answer (which was faulty anyways, as #Stefan pointed out) and cam up with this. You can paste this in a file and run it, the DATA IO contains everything that appears after __END__. In your application you would replace it with your File.
class Chunker
BEGIN_INDICATOR = "BEGIN"
END_INDICATOR = "END"
def initialize(io)
#io = io
end
def each
return enum_for(:each) if !block_given?
chunk = nil
while !io.eof? do
line = io.readline.chomp
if line == BEGIN_INDICATOR
chunk = []
chunk << line
elsif line == END_INDICATOR
chunk << line
yield chunk.freeze
chunk = nil
elsif chunk
chunk << line
end
end
end
private
attr_reader :io
end
chunker = Chunker.new(DATA)
chunker.each do |chunk|
p chunk
end
# or, thanks to the `return enum_for(:each) if !block_given?` line:
chunker.each.with_index do |chunk, index|
p "at #{index} is #{chunk}"
end
__END__
ignore
BEGIN
some
thing
END
BEGIN
some
other
thing
END
maybe ignore as well
´´´
You could enhance it to throw EOF when `each` is called multiple times or whatever suits your needs.

Find combinations in Ruby that are less than a certain number

Say I have an array [1,2,3] and I want every combination of these numbers that don't exceed 4. So I would have [1,2,3].someMethod(4) and it would give me:
[1,1,1,1]
[1,1,2]
[1,3]
[2,2]
So far I have:
(1..4).flat_map{|size| [1,2,3].repeated_combination(size).to_a }
but this gives me every possible combinations, including the ones that exceed my given limit. Is there an good way to either only get combinations that add up to my limit?
arr = [1,2,3]
(arr+[0]).repeated_combination(4).select { |a| a.reduce(:+) == 4 }.map { |a| a - [0] }
#=> [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Change == to <= if desired.
This answer, like the others, assumes arr contains natural numbers, including 1.
results = (1..4).each.with_object([]) do |size, results|
[1,2,3].repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
p results
--output:--
[[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
Parameterizing the algorithm:
def do_stuff(values, target_total)
(1..target_total).each.with_object([]) do |size, results|
values.repeated_combination(size) do |combo|
results << combo if combo.reduce(:+) == 4
end
end
end
p do_stuff([1, 2, 3], 4)
You can filter out the arrays you don't want by using the select method. Just select all the arrays that have a sum == 4 (the sum is calculated by the inject method).
all_arrs = (1..4).flat_map do |size|
[1,2,3].repeated_combination(size).to_a
end
valid_arrs = all_arrs.select do |arr|
arr.inject { |a, b| a + b } == 4
end
print valid_arrs
# Output:
# [[1, 3], [2, 2], [1, 1, 2], [1, 1, 1, 1]]
A recursive approach.
def some_method(a, n)
return [[]] if n == 0
a.select { |e| e <= n }.\
flat_map { |e| some_method(a,n-e).map { |es| ([e] + es).sort } }.\
sort.\
uniq
end
p some_method([1,2,3], 4)
# => [[1, 1, 1, 1], [1, 1, 2], [1, 3], [2, 2]]
EDIT: Here is another recursive version without filtering duplicates but with opposite order. I added comments to make it clearer.
def some_method(a, n)
return [[]] if n == 0 # bottom (solution) found
return [] if a.empty? || n < 0 # no solution
max = a.max
# search all solutions with biggest value
l = some_method(a, n-max).map { |e| [max] + e }
# search all solutions without biggest value
r = some_method(a-[max],n)
l + r
end
p some_method([1,2,3], 4)
# => [[3, 1], [2, 2], [2, 1, 1], [1, 1, 1, 1]]

Each loop not working as expected

I have an array called #results that is made up only of arrays. I want to iterate through #results and permanently delete any of the inner arrays that are smaller than a given size:
My code:
def check_results limit
#results.each_with_index do |result, index|
#results.delete_at(index) if result.size < limit
end
end
Unfortunately, this only deletes the first item where the array length is less than limit. For example if limit = 4 and #results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]] then check_results returns [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]
I can't figure out why this is happening. Am I using the wrong loop?
You should do this, as delete_at modifies the array, and you will get unexpected behavior if you are deleting elements while iterating it
#results.reject { |i| i.size < limit }
Above code will exclude all array elements whose size is smaller than limit
It's not a good idea to modify the #results array in place as that will conflict with the outer iteration.
What you should do instead is use select to build a new array.
def check_results(limit)
#result.select { |result| result.size > limit }
end
As per the documentation, #delete_at returns the element at that index.
a = ["ant", "bat", "cat", "dog"]
a.delete_at(2) #=> "cat"
a #=> ["ant", "bat", "dog"]
a.delete_at(99) #=> nil
I added some debug statements to show you what is happening at each step, assuming limit is 4:
#results = [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
#results.each_with_index do |r, i|
puts "RESULT: #{r.to_s}"
puts "INDEX: #{i}"
#results.delete_at(i) if r.size < 4
puts "ARRAY: #{#results.to_s}"
end
RESULT: [1, 1, 1, 1]
INDEX: 0
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
RESULT: [1, 1, 1, 1]
INDEX: 1
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1], [1, 1]]
RESULT: [1, 1, 1]
INDEX: 2
ARRAY: [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]
# #results == [[1, 1, 1, 1], [1, 1, 1, 1], [1, 1]]
As you can see, the element originally at index 2 has been removed. Because you are modifying #results while you are iterating through it, an index of 3 no longer exists, and an index of 2 has already been analyzed. This is why you should not modify an object while iterating through it.
Ideally, you want to use #delete_if. Similar to methods ending in !, #delete_if will modify the array (not return a copy of the result), based on conditions from a block (as an argument). The following would be how you would implement the method:
def check_results(limit)
#results.delete_if { |arr| arr.length < limit }
end
#results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]
check_results(2)
# => #results == [ ['foo', 'bar'], ['bizz', 'bazz'] ]
If you do not want to modify #results, then I suggest a similar method, #reject. Again, #results will not be modified, and instead a copy of the results will be returned.
def check_results(limit)
#results.reject { |arr| arr.length < limit }
end
#results = [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]
check_results(2)
# => [ ['foo', 'bar'], ['bizz', 'bazz'] ]
# => #results == [ ['foo', 'bar'], ['bizz', 'bazz'], ['kaboom'] ]

Generating combinations from an array which == a specified amount?

I need to get all the possible number combinations from denom_arr which equal the amt.
denom_arr = [4,3,1]
amt = 10
This case would produce:
[4, 4, 1, 1]
[3, 3, 3, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[4, 3, 1, 1, 1]
[4, 3, 3]
. . . (other cases...)
Problem is the code I wrote is breaking after 1-3 and I'm not sure how to make it loop over the same index to get case 4-6+
set, sets = [], []
i = 0
loop do
i = 0 if denom_arr[i].nil?
loop do
set << denom_arr[i]
break if set.inject(:+) > amt
end
set.pop if set.inject(:+) > amt
if set.inject(:+) == amt
sets << set
set = []
denom_arr.shift
end
i += 1
sets
break if denom_arr.empty?
end
UPDATE
I know this can be done with recursion with memoization/dynamic programming techniques, but I am trying to do this strictly in a loop for the sake of testing a theory.
I would do this recursively
def possible_sums(arr, amt)
return [[]] if amt == 0
return [] if amt < 0
arr.reduce([]) do |sums, e|
sums.concat(
possible_sums(arr, amt-e)
.map { |sum| sum.unshift(e).sort }
)
end.uniq
end
p possible_sums([4,3,1], 10)
# => [
# [1, 1, 4, 4], [3, 3, 4], [1, 1, 1, 3, 4], [1, 1, 1, 1, 1, 1, 4],
# [1, 3, 3, 3], [1, 1, 1, 1, 3, 3], [1, 1, 1, 1, 1, 1, 1, 3],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# ]
Although this is potentially inefficient in that it repeats work, this can be alleviated by using dynamic programming (essentially, memoizing the results of the recursive function).
UPDATE Here is an iterative solution:
def possible_sums_it(arr, amt)
sums = Array.new(amt+1) { [] }
sums[0] << []
(1..amt).each do |i|
arr.each do |e|
if i-e >= 0
sums[i].concat(
sums[i-e].map { |s| [e, *s].sort }
)
end
end
sums[i].uniq!
end
sums[amt]
end
This is in fact the dynamic programming algorithm for the problem.
So if you squint at it just right, you'll see that essentially what it is doing, is calculating all the possible sums for 0 up to amt into the sums array, using what is basically the recursive algorithm, but instead of the recursive call, we lookup a value in sums that we have calculated beforehand.
This works because we know that we won't need sums[i] before sums[j] for j < i.

Resources