Printing the process of a recursive backtracking problem - ruby

I have been given this assignment for school:
You have been given a puzzle consisting of a row of squares each containing an integer, like this:
6, 4, 1, 3, 3, 1, 4, 1, 1, 0
The bold number on the initial square is a marker that can move to other squares along the row.
At each step in the puzzle, you may move the marker the number of squares indicated by the integer in the square it currently occupies.
The marker may move either left or right along the row but may not move past either end.
The goal of the puzzle is to move the marker to the 0 at the far end of the row.
The program checks the index you are currently on, and moves either left or right a number of squares based on the integer at that index in the array. It decides this based on the bounds of the array, if it can't move the required number of squares to the left it will move to the right, and vice versa.
In this program, the first move has to be 6 to the right, since it cannot move 6 spaces left without going out of bounds. Then, it has to move 4 left since it cant move 4 right, and it goes on like that.
I have got this working, and printing the process is worth extra credit. I have it printing the process, but it is out of order.
here is my code:
def self.solvable(start, board)
return false if start>= board.length || start<0
return false if ##infinite[start] == -1
return true if board[start] == 0
##infinite[start] = -1
if solvable(board[start] + start, board)
puts "Shifted right " + board[start].to_s + " spaces, from index " + start.to_s + " to index " + (board[start] + start).to_s
return true
else
puts "Shifted left " + board[start].to_s + " spaces, from index " + start.to_s + " to index " + (start - board[start]).to_s
end
return solvable(start - board[start], board)
end
print "Enter an array of numbers: "
input = gets.chomp!
board = input.each_char.map(&:to_i)
##infinite = input.each_char.map(&:to_i)
puts solvable(0, board)
I do not understand how to make the code output in a more logical order, printing 6 spaces right, 4 spaces left, etc... instead of the current output, which is:
Shifted left 4 spaces, from index 6 to index 2
Shifted left 3 spaces, from index 3 to index 0
Shifted left 1 spaces, from index 2 to index 1
Shifted left 1 spaces, from index 5 to index 4
Shifted right 1 spaces, from index 8 to index 9
Shifted right 1 spaces, from index 7 to index 8
Shifted right 3 spaces, from index 4 to index 7
Shifted right 4 spaces, from index 1 to index 5
Shifted right 6 spaces, from index 0 to index 6

Assumption
I assume the game begins at position 0. Each move increases or decreases the position by an integal amount. The objective is to get back to position 0 after the first move has been made.
We are given an array, arr, of integers and a mapping from positions to indices of the array. For position p the index of arr is given by p % arr.size.
If we are at position p we obtain the value may move to position p + n or p - n, where
n = arr[p % arr.size]
For the example given:
arr = [6, 4, 1, 3, 3, 1, 4, 1, 1, 0]
(arr.size #=> 10) and p initially zero,
n = arr[0 % 10]
#=> arr[0] => 6
so we may move to position +6 or -6. If we move to +6, we calculate
n = arr[6 % 10]
#=> 4
so we may move to position 6+4 #=> 10 or 6-4 #=> 2. If we move to -6, we calculate
n = arr[-6 % 10]
#=> 3
so we may move to position -6-3 #=> -9 or -6+3 #=> -3.
Note that arr[9] #=> 0 can be regarding as an absorbing state.
Code
The method I've chosen to use is recursive.
def onward_to_zero(arr, pos=0)
n = arr[pos % arr.size]
return [] if n.zero?
return [-n] if (pos-n).zero?
return [n] if (pos+n).zero?
if rand < 0.5
rv = onward_to_zero(arr, pos-n)
return [-n] + rv unless rv.empty?
rv = onward_to_zero(arr, pos+n)
return [n] + rv unless rv.empty?
else
rv = onward_to_zero(arr, pos+n)
return [n] + rv unless rv.empty?
rv = onward_to_zero(arr, pos-n)
return [-n] + rv unless rv.empty?
end
[]
end
I believe it can be proven that there is always a path back to zero, but I have not given thought to a proof.
Examples
arr = [6, 4, 1, 3, 3, 1, 4, 1, 1, 0]
onward_to_zero(arr)
#=> [-6, 3, 1, -1, 1, -1, -1, 4]
# pos % 10 0 4 7 8 7 8 7 6
# pos-> 0 -6 -3 -2 -3 -2 -3 -4 0
arr = [3, 2, 4, 1, 3, 6, 2]
onward_to_zero(arr)
#=> [3, -1, 4, 2, 2, 1, -3, 2, -1, -4, -6, -2, 3]
# pos-> 3 2 6 8 10 11 8 10 9 5 -1 -3 0
arr = [3, 3]
onward_to_zero(arr)
#=> [-3, 3]
# pos-> -3 0
arr = [7, 26, 33, 18, 7, 13]
onward_to_zero(arr)
#=> [-7, -13, 7, 13]
# pos-> -7 -20 -13 0
Discussion
Notice that if rand < 0.5 causes me to consider a reduction in the position before an increase in the position roughly half the time. If I were always to consider a reduction before an increase, or vice-versa, I could easily get a stack level too deep error.
Even with that probability mechanism, however, the method gives quite varied results and could still result in a stack level too deep error. Here are the results I obtained by running the first example 10 times.
[6, -4, 1, -3]
[-6, 3, 1, -1, -1, 4]
[6, 4, 6, 4, 6, 4, 6, 4, 6,..., -1, -1, 4] (824 elements)
[6, 4, -6, -3, 4, 1, -4, -1,..., -4, 1, -3] (386 elements)
[-6, 3, 1, -1, -1, 4]
[-6, -3, 4, 1, 4]
[-6, 3, 1, -1, 1, -1, -1, 4]
[-6, -3, -4, -1, -4, 1, -3, 6, 4, 6, 4]
[-6, -3, -4, 1, -1, -1, -4, -1, 4, 1, 4, 6, 4]
[-6, 3, -1, 4]

Related

Why does my algorithm solution fail validation?

Here is a task i got:
Cards are laid out on the table in a row, each card has a natural number written on it. In one move, it is allowed to take a card either from the left or from the right end of the row. In total, you can make k moves. The final score is equal to the sum of the numbers on the selected cards. Determine what is the maximum score you can get at the end of the game.
Here`s my code:
def card_counter(arr, k):
if len(arr) == k:
return sum(arr)
rang = len(arr) // 2
left = arr[:rang]
right = list(reversed(arr[rang:]))
c = 0
for _ in range(k):
min_arr = left if sum(left) >= sum(
right) and len(left) > 0 else right
c += min_arr.pop(0)
return c
if __name__ == '__main__':
assert card_counter([1, 2, 3, 4, 5], 5) == 15
assert card_counter([0, 0, 0], 1) == 0
assert card_counter([150], 1) == 150
This code works on all variants that I have come up with, including extreme cases. But the system does not accept this option, automatic tests do not pass. Where can there be a mistake?
I cannot comment on the algorithm you implemented as you did not state it in non-algorithm terms and I am not familiar with the language you are using (which I am guessing is Python). I will present a simple solution written in Ruby, hoping that the description I give will make it understood by readers who do not know Ruby.
Suppose
deck = [1, 2, 5, 7, 1, 4, 6, 3]
nbr_moves = 4
ds = deck.size
#=> 8
After removing nbr_moves from the ends,
m = ds - nbr_moves
#=> 4
consecutive cards will remain. In Ruby we could write
best = (0..ds-1).each_cons(m).max_by { |arr| deck.values_at(*arr).sum }
#=> [3, 4, 5, 6]
to obtain the indices of deck, for which the sum of the associated values is maximum:
deck.values_at(*best).sum
#=> 18
where
deck.values_at(*best)
#=> [7, 1, 4, 6]
In view of the value of best ([3, 4, 5, 6]), we need to remove left = best.first #=> 3 elements from the left and nbr_moves - left #=> 1 element from the right. The order of the removals is not relevant.
Note that
enum = (0..ds-1).each_cons(m)
#=> #<Enumerator: 0..7:each_cons(4)>
returns an enumerator. We can convert this enumerator to an array to see the values it will generate.
enum.to_a
#=> [[0, 1, 2, 3], [1, 2, 3, 4], [2, 3, 4, 5], [3, 4, 5, 6], [4, 5, 6, 7]]
When, for example,
arr = [2, 3, 4, 5]
then
a = deck.values_at(*arr)
#=> [5, 7, 1, 4]
a.sum
#=> 17
Note that, having computed, for example,
t = [deck[i], deck[i+1],..., deck[j]].sum
the sum of
[deck[i+1], deck[i+2],..., deck[j+1]]
is seen to equal
t - deck[i] + deck[j+1]
This suggests a more efficient way to perform the calculations when m is large.

Find all combinations that include a specific value where all values are next to each other in array

I have an array of variable length containing all unique values and I need to find all combinations of values whose indices are next to each other and always include a specified value. The order of values in each resulting combination doesn't matter (However I kept them in order in my example to better illustrate).
As an example: [5, 4, 2, 0, 1, 3]
If the specific value chosen is 0, we would end up with the following 12 combinations:
0
0, 1
2, 0
0, 1, 3
2, 0, 1
4, 2, 0
2, 0, 1, 3
4, 2, 0, 1
5, 4, 2, 0
4, 2, 0, 1, 3
5, 4, 2, 0, 1
5, 4, 2, 0, 1, 3
If the specific value chosen is 3, we would end up with the following 6 combinations:
3
1, 3
0, 1, 3
2, 0, 1, 3
4, 2, 0, 1, 3
5, 4, 2, 0, 1, 3
Answers in any programming language will work.
EDIT: I believe this can be brute forced by finding all combinations of all numbers and then narrowing that list to make sure each combination meets the requirements...its not ideal but should work.
This problem could be solved in O(n^3) time-complexity using the following algorithm:
Step-1: Find the index of the target element.
Step-2: Iterate through an index of the target to the rightmost index. Let's call this iterator as idx.
Step-3: Then iterate from the target index to the leftmost index. Let's call this index as i.
Step-4: Print all the elements between the indices idx and i.
Following the above steps will print all the combinations.
The code for the above algorithm is implemented using python below.
def solution(array,target):
index = -1
for idx,element in enumerate(array):
if(element == target):
index = idx
n = len(array)
for idx in range(n-1,index-1,-1):
for i in range(index,-1,-1):
for j in range(i,idx+1):
print(array[j],end = ",")
print()
arr = [5, 4, 2, 0, 1, 3]
target = 0
solution(arr,target)

Find all combinations in a 3x3 matrix following some rules

Given a 3x3 matrix:
|1 2 3|
|4 5 6|
|7 8 9|
I'd like to calculate all the combinations by connecting the numbers in this matrix following these rules:
the combinations width are between 3 and 9
use one number only once
you can only connect adjacent numbers
Some examples: 123, 258, 2589, 123654, etc.
For example 1238 is not a good combination because 3 and 8 are not adjacent. The 123 and the 321 combination is not the same.
I hope my description is clear.
If anyone has any ideas please let me know. Actually I don't know how to start :D. Thanks
This is a search problem. You can just use straightforward depth-first-search with recursive programming to quickly solve the problem. Something like the following:
func search(matrix[N][M], x, y, digitsUsed[10], combination[L]) {
if length(combination) between 3 and 9 {
add this combination into your solution
}
// four adjacent directions to be attempted
dx = {1,0,0,-1}
dy = {0,1,-1,0}
for i = 0; i < 4; i++ {
next_x = x + dx[i]
next_y = y + dy[i]
if in_matrix(next_x, next_y) and not digitsUsed[matrix[next_x][next_y]] {
digitsUsed[matrix[next_x][next_y]] = true
combination += matrix[next_x][next_y]
search(matrix, next_x, next_y, digitsUsed, combination)
// At this time, sub-search starts with (next_x, next_y) has been completed.
digitsUsed[matrix[next_x][next_y]] = false
}
}
}
So you could run search function for every single grid in the matrix, and every combinations in your solution are different from each other because they start from different grids.
In addition, we don't need to record the status which indicates one grid in the matrix has or has not been traversed because every digit can be used only once, so grids which have been traversed will never be traversed again since their digits have been already contained in the combination.
Here is a possible implementation in Python 3 as a a recursive depth-first exploration:
def find_combinations(data, min_length, max_length):
# Matrix of booleans indicating what values have been used
visited = [[False for _ in row] for row in data]
# Current combination
comb = []
# Start recursive algorithm at every possible position
for i in range(len(data)):
for j in range(len(data[i])):
# Add initial combination element and mark as visited
comb.append(data[i][j])
visited[i][j] = True
# Start recursive algorithm
yield from find_combinations_rec(data, min_length, max_length, visited, comb, i, j)
# After all combinations with current element have been produced remove it
visited[i][j] = False
comb.pop()
def find_combinations_rec(data, min_length, max_length, visited, comb, i, j):
# Yield the current combination if it has the right size
if min_length <= len(comb) <= max_length:
yield comb.copy()
# Stop the recursion after reaching maximum length
if len(comb) >= max_length:
return
# For each neighbor of the last added element
for i2, j2 in ((i - 1, j), (i, j - 1), (i, j + 1), (i + 1, j)):
# Check the neighbor is valid and not visited
if i2 < 0 or i2 >= len(data) or j2 < 0 or j2 >= len(data[i2]) or visited[i2][j2]:
continue
# Add neighbor and mark as visited
comb.append(data[i2][j2])
visited[i2][j2] = True
# Produce combinations for current starting sequence
yield from find_combinations_rec(data, min_length, max_length, visited, comb, i2, j2)
# Remove last added combination element
visited[i2][j2] = False
comb.pop()
# Try it
data = [[1, 2, 3],
[4, 5, 6],
[7, 8, 9]]
min_length = 3
max_length = 9
for comb in find_combinations(data, min_length, max_length):
print(c)
Output:
[1, 2, 3]
[1, 2, 3, 6]
[1, 2, 3, 6, 5]
[1, 2, 3, 6, 5, 4]
[1, 2, 3, 6, 5, 4, 7]
[1, 2, 3, 6, 5, 4, 7, 8]
[1, 2, 3, 6, 5, 4, 7, 8, 9]
[1, 2, 3, 6, 5, 8]
[1, 2, 3, 6, 5, 8, 7]
[1, 2, 3, 6, 5, 8, 7, 4]
[1, 2, 3, 6, 5, 8, 9]
[1, 2, 3, 6, 9]
[1, 2, 3, 6, 9, 8]
[1, 2, 3, 6, 9, 8, 5]
[1, 2, 3, 6, 9, 8, 5, 4]
[1, 2, 3, 6, 9, 8, 5, 4, 7]
...
Look at all the combinations and take the connected ones:
import itertools
def coords(n):
"""Coordinates of number n in the matrix."""
return (n - 1) // 3, (n - 1) % 3
def adjacent(a, b):
"""Check if a and b are adjacent in the matrix."""
ai, aj = coords(a)
bi, bj = coords(b)
return abs(ai - bi) + abs(aj - bj) == 1
def connected(comb):
"""Check if combination is connected."""
return all(adjacent(a, b) for a, b in zip(comb, comb[1:]))
for width in range(3, 10):
for comb in itertools.permutations(range(1, 10), width):
if connected(comb):
print(comb)

Algorithm for generating sequence denoting the sequence of removal of numbers

Given a finite sequence of numbers, in each round, any number whose left neighbour is smaller than itself will be removed. This removal action will continue until nothing can be removed. Each number removed will be labelled with the round it was removed, or 0 if it is never removed.
For example, consider the sequence
0 9 8 7 9 8 7 5
After first round, it becomes,
0 8 7 8 7 5
and after consecutive 4 rounds,
0 7 7 5
0 7 5
0 5
0
Thus the corresponding labels for the numbers are
0 1 2 3 1 2 4 5
How may I generate the sequence of labels in O(N) time by using a stack or queue, when N is the length of the sequence offered? Or may I know the maximum rounds of removing in O(N) time?
Yes it can be done using a single stack, I will describe the solution, and briefly explain why it works later.
Assume the array is A = [0, 9, 8, 7, 9, 8, 7, 5], Ans = [] be the array of corresponding answer. We also maintain an initially empty stack S. The stack will store pairs {A[i], Ans[i]} and we will try to preserve the stack so that it is always strictly increasing on A[i] as follows:
Push {A[0], 0} to S
Loop through array A. For an instance of iteration, let A[i] be current number (Ans[i] not known yet):
2a. Initialize a variable round_to_wait = 0, keep popping the stack S until the top element is smaller than A[i], set rount_to_wait to maximum Ans[x] meanwhile
2b. If the S is empty, then set Ans[i] = 0, else set Ans[i] = round_to_wait + 1
2c. Push {A[i], Ans[i]} to S
Let's do an demo based on your example:
A = [0, 9, 8, 7, 9, 8, 7, 5], Ans = [0, -1, -1, -1, -1, -1, -1, -1], S = [{0,0}]
A[1] = 9 and S.top() already smaller than 9, no element is popped. Ans[1] = round_to_wait + 1 = 0 + 1 = 1, push {9, 1} into S
A = [0, 9, 8, 7, 9, 8, 7, 5], Ans = [0, 1, -1, -1, -1, -1, -1, -1], S = [{0,0}, {9,1}]
A[2] = 8 and we popped element until {0,0} is left. rount_to_wait = 1 as it is the maximum in whole popping process. Ans[2] = round_to_wait + 1 = 1 + 1 = 2, push {8, 2} into S
A = [0, 9, 8, 7, 9, 8, 7, 5], Ans = [0, 1, 2, -1, -1, -1, -1, -1], S = [{0,0}, {8,2}]
Similarly, S = [{0,0}, {7,3}]
Similarly, S = [{0,0}, {7,3}, {9,1}]
Similarly, S = [{0,0}, {7,3}, {8,2}]
Similarly, S = [{0,0}, {7,4}]
Similarly, S = [{0,0}, {5,5}]
And that's it, you have the answer in one loop. As each element at most be pushed and popped one time, the complexity is still O(N)
Why it works is because, Let A[i] be the first element which does not form strictly increasing sequence with S, what does that mean?
That means that, at some point, the top element in S S_top will "block" us from removing A[i]. We have chances (not sufficient condition though) to remove A[i] only if S_top is removed, which means it is at least as soon as Ans[S_top] + 1, and we take the maximum among all such elements.
The special case is that, there is no element smaller than A[i] at all, which means S will eventually be empty, in such case Ans[i] = 0
(PS: I thought I have some memory on this problem on some online judge a few years ago, if that's the source, may you post it out so that I can go and submit to verify the solution?)

Given integers how do I find asc and desc sequences of three?

I have integers i.e. 9, 5, 4, 3, 1, 6, 7, 8. I want to return the index where a sequence of three descending or ascending integers exists. In the example above I would get indices 1 and 5. What is the ruby code for this?
def seq
array = [9,5,4,3,1,6,7,8]
array.each_with_index |val, index|
if (val < (array[index + 1]).val < (array[index + 1]).val)
puts "#{index}"
# Skip two indexes
end
end
I think the logic behind your solution is almost correct, but your syntax is pretty far off from valid Ruby.
Here are a pair of pretty verbose solutions that will (hopefully) be fairly obvious:
numbers = [9, 6, 5, 4, 3, 1, 6, 7, 8]
# Find non-overlapping sets
i = 0
until i > numbers.length - 2
a, b, c = numbers[i..i + 2]
if (a - b == b - c) && (a - b).abs == 1
puts "#{i} (#{a},#{b},#{c})"
# Skip next two indexes
i += 3
else
i += 1
end
end
# Find overlapping sets (same solution, but don't skip indexes)
(0...numbers.length - 2).each do |i|
a, b, c = numbers[i..i + 2]
if (a - b == b - c) && (a - b).abs == 1
puts "#{i} (#{a},#{b},#{c})"
end
end
Since the question is not clear enough. I will assume the question is about finding 3 ascending or descending continuous numbers. If the length of the satisfied sequence it longer than 3, e.g [2, 3, 4, 5], it returns 0 and 1.
Here is the algorithm, do list[index] - list[index - 1] for all elements, and repeat it for another time, the answer will be the index of 0 elements after the calculation.
Intuitively,
original 9, 5, 4, 3, 1, 6, 7, 8
first pass -4, -1, -1, -2, 5, 1, 1
2nd pass 3, 0, -1, 7, 4, 0 -> the answer will be the indexes of 0's, which is 1, 5
Algorithm:
lst = [9, 5, 4, 3, 1, 6, 7, 8]
lst1 = lst.each_cons(2).map{ |a, b| b-a }
lst2 = lst1.each_cons(2).map{ |a, b| b-a }
result = lst2.each_index.select{|i| lst2[i] == 0}
result = [1, 5]
Here’s a solution using each_cons(3).with_index:
[9,5,4,3,1,6,7,8].each_cons(3).with_index.select { |s, i| s[0] < s[1] && s[1] < s[2] }.map(&:last)

Resources