Speeding up solution to algorithm - ruby

Working on the following algorithm:
Given an array of non-negative integers, you are initially positioned
at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Below is my solution. It tries every single potential step, and then memoizes accordingly. So if the first element is three, the code takes three steps, two steps, and one step, and launches three separate functions from there. I then memoized with a hash. My issue is that the code works perfectly fine, but it's timing out for very large inputs. Memoizing helped, but only a little bit. Am I memoizing correctly or is backtracking the wrong approach here?
def can_jump(nums)
#memo = {}
avail?(nums, 0)
end
def avail?(nums, index)
return true if nums.nil? || nums.empty? || nums.length == 1 || index >= nums.length - 1
current = nums[index]
true_count = 0
until current == 0 #try every jump from the val to 1
#memo[index + current] ||= avail?(nums, index + current)
true_count +=1 if #memo[index + current] == true
current -= 1
end
true_count > 0
end

Here's a 𝑂(𝑛) algorithm:
Initialize 𝑚𝑎𝑥 to 0.
For each number 𝑛𝑖 in 𝑁:
If 𝑖 is greater than 𝑚𝑎𝑥, neither 𝑛𝑖 nor any subsequent number can be reached, so
return false.
If 𝑛𝑖+𝑖 is greater than 𝑚𝑎𝑥, set 𝑚𝑎𝑥 to 𝑛𝑖+𝑖.
If 𝑚𝑎𝑥 is greater than or equal to the last index in 𝑁
return true.
Otherwise return false.
Here's a Ruby implementation:
def can_jump(nums)
max_reach = 0
nums.each_with_index do |num, idx|
return false if idx > max_reach
max_reach = [idx+num, max_reach].max
end
max_reach >= nums.size - 1
end
p can_jump([2,3,1,1,4]) # => true
p can_jump([3,2,1,0,4]) # => false
See it on repl.it: https://repl.it/FvlV/1

Your code is O(n^2), but you can produce the result in O(n) time and O(1) space. The idea is to work backwards through the array keeping the minimum index found so far from which you can reach index n-1.
Something like this:
def can_jump(nums)
min_index = nums.length - 1
for i in (nums.length - 2).downto(0)
if nums[i] + i >= min_index
min_index = i
end
end
min_index == 0
end
print can_jump([2, 3, 1, 1, 4]), "\n"
print can_jump([3, 2, 1, 0, 4]), "\n"

Related

How to compare 1 number to an entire array?

Create a function that takes an array of hurdle heights and a jumper's jump height, and determine whether or not the hurdler can clear all the hurdles. A hurdler can clear a hurdle if their jump height is greater than or equal to the hurdle height.
My code:
def hj (arr, h)
i = 0
while i < arr.length
j = 0
while j < arr.length
if arr[i] > h
return false
end
j += 1
end
return true
i += 1
end
end
puts hj([2, 3, 6, 1, 3, 1, 8], 7)
Desired output: true if h is >= to any number in the array; false if h is < any number in the array (I want true or false to display once)
Where I'm questioning my own code:
Not sure if I need two while statements
the current array being passed should output false
loop seems to only be comparing the first set of numbers, so 7 and 2. Not sure why the loop is stopping.
Not sure if I'm utilizing true and false correctly
Feel like I should be using a block for this, but not sure where to implement it.
Thank you in advance for any feedback.
Some solutions:
Using loop
def hj(arr, h)
for elem in arr
return false if elem > h
end
true
end
See? Only one loop. Actually this is the most unruby implementation.
Using Enumerable#all?
def hj(arr, h)
arr.all?{|elem| elem <= h}
end
This is the most intuitive and most Ruby implementation.
Using Enumerable#max
If one can jump over the highest hurdle, he can jump over all hurdles.
def hj(arr, h)
arr.max <= h
end
Not sure if I need two while statements
You don't. You only need to traverse the list once. You're traversing, not sorting / reordering.
loop seems to only be comparing the first set of numbers, so 7 and 2. Not sure why the loop is stopping.
This is because you are using return true as the second last statement of your outer loop. Return interrupts function execution and returns immediately to the calling function - in this case, the last line of your program.
Feel like I should be using a block for this, but not sure where to implement it.
A block is the idiomatic ruby way to solve this. You are, essentially, wanting to check that your second parameter is larger than any value in the list which is your first parameter.
A solution in idiomatic ruby would be
def hj (arr, h)
# return true if h >= all elements in arr, false otherwise
# given arr = [1, 2, 3] and h = 2,
# returns [ true, true, false ] which all? then returns as false
# (since all? returns the boolean AND of the results of the block evaluation
arr.all? { |elem| elem <= h }
end

Python Codility Frog River One time complexity

So this is another approach to probably well-known codility platform, task about frog crossing the river. And sorry if this question is asked in bad manner, this is my first post here.
The goal is to find the earliest time when the frog can jump to the other side of the river.
For example, given X = 5 and array A such that:
A[0] = 1
A[1] = 3
A[2] = 1
A[3] = 4
A[4] = 2
A[5] = 3
A[6] = 5
A[7] = 4
the function should return 6.
Example test: (5, [1, 3, 1, 4, 2, 3, 5, 4])
Full task content:
https://app.codility.com/programmers/lessons/4-counting_elements/frog_river_one/
So that was my first obvious approach:
def solution(X, A):
lista = list(range(1, X + 1))
if X < 1 or len(A) < 1:
return -1
found = -1
for element in lista:
if element in A:
if A.index(element) > found:
found = A.index(element)
else: return -1
return found
X = 5
A = [1,2,4,5,3]
solution(X,A)
This solution is 100% correct and gets 0% in performance tests.
So I thought less lines + list comprehension will get better score:
def solution(X, A):
if X < 1 or len(A) < 1:
return -1
try:
found = max([ A.index(element) for element in range(1, X + 1) ])
except ValueError:
return -1
return found
X = 5
A = [1,2,4,5,3]
solution(X,A)
This one also works and has 0% performance but it's faster anyway.
I also found solution by deanalvero (https://github.com/deanalvero/codility/blob/master/python/lesson02/FrogRiverOne.py):
def solution(X, A):
# write your code in Python 2.6
frog, leaves = 0, [False] * (X)
for minute, leaf in enumerate(A):
if leaf <= X:
leaves[leaf - 1] = True
while leaves[frog]:
frog += 1
if frog == X: return minute
return -1
This solution gets 100% in correctness and performance tests.
My question arises probably because I don't quite understand this time complexity thing. Please tell me how the last solution is better from my second solution? It has a while loop inside for loop! It should be slow but it's not.
Here is a solution in which you would get 100% in both correctness and performance.
def solution(X, A):
i = 0
dict_temp = {}
while i < len(A):
dict_temp[A[i]] = i
if len(dict_temp) == X:
return i
i += 1
return -1
The answer already been told, but I'll add an optional solution that i think might help you understand:
def save_frog(x, arr):
# creating the steps the frog should make
steps = set([i for i in range(1, x + 1)])
# creating the steps the frog already did
froggy_steps = set()
for index, leaf in enumerate(arr):
froggy_steps.add(leaf)
if froggy_steps == steps:
return index
return -1
I think I got the best performance using set()
take a look at the performance test runtime seconds and compare them with yours
def solution(X, A):
positions = set()
seconds = 0
for i in range(0, len(A)):
if A[i] not in positions and A[i] <= X:
positions.add(A[i])
seconds = i
if len(positions) == X:
return seconds
return -1
The amount of nested loops doesn't directly tell you anything about the time complexity. Let n be the length of the input array. The inside of the while-loop needs in average O(1) time, although its worst case time complexity is O(n). The fast solution uses a boolean array leaves where at every index it has the value true if there is a leaf and false otherwise. The inside of the while-loop during the entire algotihm is excetuded no more than n times. The outer for-loop is also executed only n times. This means the time complexity of the algorithm is O(n).
The key is that both of your initial solutions are quadratic. They involve O(n) inner scans for each of the parent elements (resulting in O(n**2)).
The fast solution initially appears to suffer the same fate as it's obvious it contains a loop within a loop. But the inner while loop does not get fully scanned for each 'leaf'. Take a look at where 'frog' gets initialized and you'll note that the while loop effectively picks up where it left off for each leaf.
Here is my 100% solution that considers the sum of numeric progression.
def solution(X, A):
covered = [False] * (X+1)
n = len(A)
Sx = ((1+X)*X)/2 # sum of the numeric progression
for i in range(n):
if(not covered[A[i]]):
Sx -= A[i]
covered[A[i]] = True
if (Sx==0):
return i
return -1
Optimized solution from #sphoenix, no need to compare two sets, it's not really good.
def solution(X, A):
found = set()
for pos, i in enumerate(A, 0):
if i <= X:
found.add(i)
if len(found) == X:
return pos
return -1
And one more optimized solution for binary array
def solution(X, A):
steps, leaves = X, [False] * X
for minute, leaf in enumerate(A, 0):
if not leaves[leaf - 1]:
leaves[leaf - 1] = True
steps -= 1
if 0 == steps:
return minute
return -1
The last one is better, less resources. set consumes more resources compared to binary list (memory and CPU).
def solution(X, A):
# if there are not enough items in the list
if X > len(A):
return -1
# else check all items
else:
d = {}
for i, leaf in enumerate(A):
d[leaf] = i
if len(d) == X:
return i
# if all else fails
return -1
I tried to use as much simple instruction as possible.
def solution(X, A):
if (X > len(A)): # check for no answer simple
return -1
elif(X == 1): # check for single element
return 0
else:
std_set = {i for i in range(1,X+1)} # list of standard order
this_set = set(A) # set of unique element in list
if(sum(std_set) > sum(this_set)): # check for no answer complex
return -1
else:
for i in range(0, len(A) - 1):
if std_set:
if(A[i] in std_set):
std_set.remove(A[i]) # remove each element in standard set
if not std_set: # if all removed, return last filled position
return(i)
I guess this code might not fulfill runtime but it the simplest I could think of
I am using OrderedDict from collections and sum of first n numbers to check the frog will be able to cross or not.
def solution(X, A):
from collections import OrderedDict as od
if sum(set(A))!=(X*(X+1))//2:
return -1
k=list(od.fromkeys(A).keys())[-1]
for x,y in enumerate(A):
if y==k:
return x
This code gives 100% for correctness and performance, runs in O(N)
def solution(x, a):
# write your code in Python 3.6
# initialize all positions to zero
# i.e. if x = 2; x + 1 = 3
# x_positions = [0,1,2]
x_positions = [0] * (x + 1)
min_time = -1
for k in range(len(a)):
# since we are looking for min time, ensure that you only
# count the positions that matter
if a[k] <= x and x_positions[a[k]] == 0:
x_positions[a[k]] += 1
min_time = k
# ensure that all positions are available for the frog to jump
if sum(x_positions) == x:
return min_time
return -1
100% performance using sets
def solution(X, A):
positions = set()
for i in range(len(A)):
if A[i] not in positions:
positions.add(A[i])
if len(positions) == X:
return i
return -1

Quick Sort Algo

My algorithm is not working as intended. When I use a data set that has a starting value greater than the last element, the method sorts the numbers in descending order rather than ascending. I am not exactly sure changing the numbers at input[0] and input.length - 1 can alter the output from ascending to reverse order. I would appreciate any insight on how to fix this. Thanks!
def quickSort(input)
divide = lambda do |first, last|
if first >= last
return
end
mid = first
i = 0
while i < last do
if input[i] < input[last]
input[i], input[mid] = input[mid], input[i]
mid += 1
end
i += 1
end
input[mid], input[last] = input[last], input[mid]
divide.call(first, mid - 1)
divide.call(mid + 1, last)
end
divide.call(0, input.length - 1 )
return input
end
quickSort([24, 6, 8, 2, 35]) // causes a descending sort
quickSort([3,9,1,4,7]) // works as intended
I don't think that is quicksort (at least not the way I learned), and if you try adding more values to the first array you are sorting it will crash the program.
Take a look at this following implementation (my ruby is a bit rusty so bear with me)
def quickSort(input)
return input if input.length <= 1
i = input.length - 1
pivot = input[rand(i)]
input.delete(pivot)
lesser = []
greater = []
input.map do |n|
lesser.push(n) if n < pivot
greater.push(n) if n >= pivot
end
sorted = []
sorted.concat(quickSort(lesser))
sorted.push(pivot)
sorted.concat(quickSort(greater))
return sorted
end
print quickSort([24, 6, 8, 2, 35, 12])
puts ""
print quickSort([3,9,1,4,7,8,10,15,2])
puts ""
Usually when doing quicksort you will pick a random pivot in the array and split the array into parts lesser and greater than the pivot. Then you recursively call quicksort on the lesser and greater arrays before rejoining them into a sorted array. Hope that helps!

How to find the nearest larger element to another element in an array?

I am doing a little preparation for a course I want to take. One of the questions they give as practice is the following:
Write a function, nearest_larger(arr, i) which takes an array and an
index. The function should return another index, j: this should
satisfy:
(a) arr[i] < arr[j], AND
(b) there is no j2 closer to i than j where arr[i] < arr[j].
In case of ties (see example below), choose the earliest (left-most)
of the two indices. If no number in arr is larger than arr[i],
return nil.
So the code I wrote was:
def function(arr,i)
k = arr.length
puts "nil" if arr.all? {|number| arr[i] >= number}
(0..(arr.length-1)).to_a.each do |j|
if arr[j] > arr[i]
k = j if((i-j).abs < k) ##
end
end
puts k
end
This function([1,2,6,5,10],3) returns 4 when it should be return 2.
I am having trouble addressing the scenario when there is a tie. I thought I designed my code to do this(I put ## where I think this is addressed.) Because it should be assigning the index to k if the distance is strictly less than so I don't know why it would return the right side. I hope that makes sense. Any help is appreciated.
From your current code:
def function(arr,i)
k = i - arr.length # the first result of (i-k).abs should be array length.
# returns -1 for testability purposes for `puts`
# this should be changed to return `nil`
return -1 if arr.all? {|number| arr[i] >= number}
(0...arr.length).to_a.each do |j|
if arr[j] > arr[i]
# since k is an index you should compare distance between i and j
# against the distance between i and k
k = j if((i-j).abs < (i-k).abs)
end
end
k
end
puts function([1,2,6,5,10], 0)
puts function([1,2,6,5,10], 1)
puts function([1,2,6,5,10], 2)
puts function([1,2,6,5,10], 3)
puts function([1,2,6,5,10], 4)
Output:
1
2
4
2
-1
k = arr.length
You have defaulted k to the length of the array but that's your return value. Best to make it something like nil so it doesn't confuse matters. I think that's where your return value 4 is coming from. If you add a new value onto the array it will the print 5, the new length of the array.
puts "nil" if arr.all? {|number| arr[i] >= number}
When you do the 'is there any value greater than my index value' test with all?, your not returning so the method would continue on and also print the length of the array (see above).
(0..(arr.length-1)).to_a.each do |j|
Enumerable (hence Array) has an each_with_index method so you don't need to specify/calculate the index's out the front of loops. You don't need to convert a range (0..x) to_a to loop around it either.
k = j if((i-j).abs < k) ##
You're comparing the distance of j to k itself, rather than to the distance of k.
You could cover both tests in a single loop of the array. With the < test, the left most answer in a tie takes cares of itself by the order you go over the array.
def find_closest_larger_index values, test_index, found = nil
values.each_with_index do |value,j|
found = j if value > values[test_index] and ( found.nil? or (test_index - j).abs < (test_index - found).abs )
end
found
end
Gives you
> find_closest_larger_index [1,2,6,5,10], 0
=> 1
> find_closest_larger_index [1,2,6,5,10], 1
=> 2
> find_closest_larger_index [1,2,6,5,10], 2
=> 4
> find_closest_larger_index [1,2,6,5,10], 3
=> 2
> find_closest_larger_index [1,2,6,5,10], 4
=> nil

Why is my code returning the wrong index number?

Need help with this homework problem. How do I write a function, nearest_larger(arr, i) which takes an array and an index. The function should return another index. The conditions are below. Thanks.
This should satisfy:
(a) `arr[i] < arr[j]`, AND
(b) there is no `j2` closer to `i` than `j` where `arr[i] < arr[j]`.
In case of ties (see example beow), choose the earliest (left-most) of the two indices. If no number in arr is largr than arr[i], return nil.
example:
nearest_larger([2,3,4,8], 2).should == 3
end
My code is:
def nearest_larger(arr, idx)
greater_nums = []
arr.each {|element| greater_nums << element if element>idx}
sorted_greater_nums= greater_nums.sort
nearest_larger = sorted_greater_nums[0]
arr.index(nearest_larger)
end
THANKS a lot guys. See post below for solution
I see at least two mistakes here.
First, your code seems to assume the array is sorted. (Otherwise why would taking the least of greater_nums give you the closest index?) But from your requirements (choose the left-most index in case of a tie), that is clearly not guaranteed.
More importantly, in your each loop you're comparing element to idx (the index passed in) rather than arr[idx].
I think what you really want to do is something like this:
def nearest_larger(arr, idx)
value = arr[idx]
# Ensure idx is actually valid.
return nil if idx < 0 || idx >= arr.length
left, right = [idx - 1, idx + 1]
while (left >= 0 || right < arr.length)
# Always check left first, per the requirement.
return left if left >= 0 && arr[left] > value
return right if right < arr.length && arr[right] > value
# Incrementally move farther and farther left/right from the specified index
# looking for a larger value.
left, right = [left - 1, right + 1]
end
# This will return nil if no values were larger than arr[idx].
end

Resources