How to optimize the program with two for loops - ruby-on-rails

I have a following programm
def calc_res(a)
n = a.length
result = 0
for i in 0 .. (n - 1)
for j in i .. (n - 1)
if (a[i] != a[j] && j - i > result) then
result = j - i
end
end
end
return result
end
which return following output
irb(main):013:0> calc_res([4, 6, 2, 2, 6, 6, 4])
=> 5
but it is taking time if array size is too large e.g. [0,1,2,3,.....70000]
can any one suggest me how can I optimize it.
Thanks

If I have understood the problem you are trying to solve (from code)
def calc_res(a)
last_index = a.length - 1
index = 0
while a[index] == a.last do
index = index + 1
break if index == last_index
end
last_index - index
end
It checks items from start if they are equal to items from end, end it moves the index toward the last element. As I understood you search for max length between different elements.
For you problem with [4, 6, 2, 2, 6, 6, 4] it will have one iteration and return 5, for the problem with [1...70000] it will have zero iterations and will return the difference in positions for those two (size of the array - 1)

My understanding is that the problem is to find two unique elements in the array whose distance apart (difference in indices) is maximum, and to return the distance they are apart. I return nil if all elements are the same.
My solution attempts to minimize the numbers of pairs of elements that must be examined before an optimal solution is identified. For the example given in the question only two pairs of elements need be considered.
def calc_res(a)
sz = a.size-1
sz.downto(2).find { |n| (0..sz-n).any? { |i| a[i] != a[i+n] } }
end
a = [4,6,2,2,6,6,4]
calc_res a
#=> 5
If sz = a.size-1, sz is the greatest possible distance two elements can be apart. If, for example, a = [1,2,3,4], sz = 3, which is the number of positions 1 and 4 are apart.
For a, sz = a.size-1 #=> 6. I first determine if any pair of elements that are n = sz positions apart are unique. [a[0], a[6]] #=> [4,4] is the only pair of elements 6 positions apart. Since they are not unique I reduce n by one (to 5) and examine all pairs of elements n positions apart, looking for one whose elements are unique. There are two pairs 5 positions apart: [a[0], a[5]] #=> [4,6] and [a[1], a[6]] #=> [6,4]. Both of these meet the test, so we are finished, and return n #=> 5. In fact we are finished after testing the first of these two pairs. Had neither these pairs contained unique values n would have been reduced by 1 to 4 and the three pairs [a[0], a[4]] #=> [4,6], [a[1], a[5]] #=> [6,6] and [a[2], a[6]] #=> [2,6] would have been searched for one with unique values, and so on.
See Integer#downto, Enumerable#find and Enumerable#any?.

A more rubyesque versions include:
def calc_res(a)
last = a.last
idx = a.find_index {|e| e != last }&.+(1) || a.size
a.size - idx
end
def calc_res(a)
last = a.last
a.size - a.each.with_index(1).detect(->{[a.size]}) {|e,_| e != last }.last
end
def calc_res(a)
last = a.last
a.reduce(a.size) do |memo, e|
return memo unless e == last
memo -= 1
end
end
def calc_res(a)
return 0 if b = a.uniq and b.size == 1
a.size - a.index(b[-1]).+(1)
end

Related

Random sum of elements in an array equals to y - ruby [duplicate]

This question already has answers here:
Finding all possible combinations of numbers to reach a given sum
(32 answers)
Closed 6 years ago.
Need to create an array whose sum should be equal to expected value.
inp = [1,2,3,4,5,6,7,8,9,10]
sum = 200
output:
out = [10,10,9,1,3,3,3,7,.....] whose sum should be 200
or
out = [10,7,3,....] Repeated values can be used
or
out = [2,3,4,9,2,....]
I tried as,
arr = [5,10,15,20,30]
ee = []
max = 200
while (ee.sum < max) do
ee << arr.sample(1).first
end
ee.pop(2)
val = max - ee.sum
pair = arr.uniq.combination(2).detect { |a, b| a + b == val }
ee << pair
ee.flatten
Is there any effective way to do it.
inp = [1,2,3,4,5,6,7,8,9,10]
sum = 20
inp.length.downto(1).flat_map do |i|
inp.combination(i).to_a # take all subarrays of length `i`
end.select do |a|
a.inject(:+) == sum # select only those summing to `sum`
end
One might take a random element of resulting array.
result = inp.length.downto(1).flat_map do |i|
inp.combination(i).to_a # take all subarrays of length `i`
end.select do |a|
a.inject(:+) == sum # select only those summing to `sum`
end
puts result.length
#⇒ 31
puts result.sample
#⇒ [2, 4, 5, 9]
puts result.sample
#⇒ [1, 2, 3, 6, 8]
...
Please note, that this approach is not efficient for long-length inputs. As well, if any original array’s member might be taken many times, combination above should be changed to permutation, but this solution is too ineffective to be used with permutation.
I found an answer of this question in the following link:
Finding all possible combinations of numbers to reach a given sum
def subset_sum(numbers, target, partial=[])
s = partial.inject 0, :+
#check if the partial sum is equals to target
puts "sum(#{partial})=#{target}" if s == target
return if s >= target #if we reach the number why bother to continue
(0..(numbers.length - 1)).each do |i|
n = numbers[i]
remaining = numbers.drop(i+1)
subset_sum(remaining, target, partial + [n])
end
end
subset_sum([1,2,3,4,5,6,7,8,9,10],20)

ArithGeo(arr) CoderByte Ruby: Why doesn't this solution work for certain test case scenarios

Below I have posted the instructions for this problem along with my solution. A few test case scenarios have failed, but seem to be working for most. Can anybody help out at which point I've gone wrong? Any help is much appreciated!!
Using the Ruby language, have the function ArithGeo(arr) take the array of numbers stored in arr and return the string "Arithmetic" if the sequence follows an arithmetic pattern or return "Geometric" if it follows a geometric pattern.
If the sequence doesn't follow either pattern return -1.
An arithmetic sequence is one where the difference between each of the numbers is consistent
Arithmetic example: [2, 4, 6, 8]
In a geometric sequence, each term after the first is multiplied by some constant or common ratio.
Geometric example: [2, 6, 18, 54]
Negative numbers may be entered as parameters, 0 will not be entered, and no array will contain all the same elements.
Code:
def arithGeo(num)
idx = 0
while idx < num.length
if ((num[idx] - num[idx + 1]) == (num[idx + 1] - num[idx + 2]))
return "Arithmetic"
elsif ((num[idx + 1] / num[idx]) == (num[idx + 2] / num[idx + 1]))
return "Geometric"
else
return "-1"
end
idx += 1
end
end
#Test Cases that Failed
p arithGeo([1, 2, 3, 4, 5, 10, 20])
p arithGeo([1, 2, 3, 4, 5, 6, 7, 88, 2])
p arithGeo([10, 110, 210, 310, 410, 511])
OK, lets do a much more "ruby like" way:
def arith?(arr)
check_arr = []
arr.reverse.inject {|memo, num| check_arr << (memo - num); num}
#loop through from highest to lowest, subtracting each from the next and store in check_arr
check_arr.all? {|num| num == check_arr[-1]}
#check that all results are the same in the arr i.e. [2,2,2,2,2]
end
This returns true if all of the operations return the same result, thus a linear progression.
def geo?(arr)
check_arr = []
arr.reverse.inject {|memo, num| check_arr << (memo / num); num}
#loop through from highest to lowest, dividing each by the next and store in check_arr
check_arr.all? {|x| x == check_arr[-1]}
#check that all products are the same in the arr i.e. [3,3,3,3,3]
end
This returns true if all of the operations return the same result, thus a geometric progression.
Now use those methods in your other method
def arith_geo?(arr)
if arith?(arr)
'Arithmetic'
elsif geo?(arr)
'Geometric'
else
-1
end
end
You did use a while but you do not loop over the data, because you write return you will only ever look at the first three numbers and then immediately return the result. You will have to keep the previous result, and make sure the result stays the same to return either geometric or arithmetic.
This should help you to complete the exercise :)
I was able to do the solution in JavaScript and this is what I came up with:
function algoGeo(arr){
var algo = true;
var geo = true;
//first check algo
for(var k = 1; k < arr.length; k++){
if( (arr[0] + (arr[1] - arr[0]) * k) !== arr[k] && algo ){
algo = false;
}
if( arr[0] * Math.pow(arr[1] / arr[0], k) !== arr[k] && geo){
geo = false;
}
}
return algo ? "Arithmetic" : geo ? "Geometric" : -1;
}
var arr = [5,12,19,26];
console.log(algoGeo(arr));
def ArithGeo(arr)
diff1 = []
diff2 = []
arr.each_index do |x|
if(x + 1 < arr.length)
diff1 << arr[x + 1] - arr[x]
diff2 << arr[x + 1] / arr[x]
end
end
diff1.uniq.size == 1 ? "Arithmetic" : diff2.uniq.size == 1 ? "Geometric" : -1
end
A little late but this is what i came up with when trying to solve this same question.

This seems to just be filling up an array with the greatest number, why?

def third_greatest(nums)
idx = 0
arr = []
i = 1
largest = 0
while idx < nums.length
while i < nums.length
if nums[idx] > nums [i]
largest = nums[idx]
else
largest = nums[idx]
end
i += 1
end
arr.push(largest)
idx += 1
i += idx
end
return arr[2]
end
puts(third_greatest([4, 3, 2, 1]) == 2)
#should equal true
I'm trying to get the third largest number out of the array but I keep getting four for any value of the array that returns data.
Any help would be great!
Here is an easier solution for finding the third greatest number in an array:
def third_greatest(nums)
nums.sort!
nums[-3]
end
third_greatest([4, 3, 2, 1])
=> 2
puts(third_greatest([4, 3, 2, 1]) == 2)
=> true
at the end of the first time to the loop, i will be nums.length.
afterwards you increase i with idx so it is now bigger than nums.length.
In the next loops you will never enter the inner loop again, so the largest is never updated anymore.
That's why you always get largest in the result.
to fix it do something like :
end
arr.push(largest)
idx += 1
i = idx + 1
end
so that i is reset to one higher than idx.
But the real solution is to leverage the rich standard library as Alex suggests.
Recent versions of Enumerable#max have allowed a parameter:
(0..9).to_a.max(3).last #=> 7
max(n) returns the three largest values, in decreasing magnitude. This could be expected to be more efficient than sort (unless n == arr.size, of course). Related Enumerable methods (max_by, min, min_by) also have this functionality.

Codility: Passing cars in Lua

I'm currently practicing programming problems and out of interest, I'm trying a few Codility exercises in Lua. I've been stuck on the Passing Cars problem for a while.
Problem:
A non-empty zero-indexed array A consisting of N integers is given. The consecutive elements of array A represent consecutive cars on a road.
Array A contains only 0s and/or 1s:
0 represents a car traveling east,
1 represents a car traveling west.
The goal is to count passing cars. We say that a pair of cars (P, Q), where 0 ≤ P < Q < N, is passing when P is traveling to the east and Q is traveling to the west.
For example, consider array A such that:
A[0] = 0
A[1] = 1
A[2] = 0
A[3] = 1
A[4] = 1
We have five pairs of passing cars: (0, 1), (0, 3), (0, 4), (2, 3), (2, 4).
Write a function:
function solution(A)
that, given a non-empty zero-indexed array A of N integers, returns the number of pairs of passing cars.
The function should return −1 if the number of pairs of passing cars exceeds 1,000,000,000.
For example, given:
A[0] = 0
A[1] = 1
A[2] = 0
A[3] = 1
A[4] = 1
the function should return 5, as explained above.
Assume that:
N is an integer within the range [1..100,000];
each element of array A is an integer that can have one of the following values: 0, 1.
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(1), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
My attempt in Lua keeps failing but I can't seem to find the issue.
local function solution(A)
local zeroes = 0
local pairs = 0
for i = 1, #A do
if A[i] == 0 then
zeroes = zeroes + 1
else
pairs = pairs + zeroes
if pairs > 1e9 then
return -1
end
end
end
return pairs
end
In terms of time-space complexity constraints, I think it should pass so I can't seem to find the issue. What am I doing wrong? Any advice or tips to make my code more efficient would be appreciated.
FYI: I keep getting a result of 2 when the desired example result is 5.
The problem statement says A is 0-based so if we ignore the first and start at 1, the output would be 2 instead of 5. 0-based tables should be avoided in Lua, they go against convention and will lead to a lot of off-by one errors: for i=1,#A do will not do what you want.
function solution1based(A)
local zeroes = 0
local pairs = 0
for i = 1, #A do
if A[i] == 0 then
zeroes = zeroes + 1
else
pairs = pairs + zeroes
if pairs > 1e9 then
return -1
end
end
end
return pairs
end
print(solution1based{0, 1, 0, 1, 1}) -- prints 5 as you wanted
function solution0based(A)
local zeroes = 0
local pairs = 0
for i = 0, #A do
if A[i] == 0 then
zeroes = zeroes + 1
else
pairs = pairs + zeroes
if pairs > 1e9 then
return -1
end
end
end
return pairs
end
print(solution0based{[0]=0, [1]=1, [2]=0, [3]=1, [4]=1}) -- prints 5

How to create a nested loop with Ruby the "Right Way!"?

I'm in the process of learning Ruby, taking a Berkeley's MOOC, and, in some of these MOOC's homework we have an exercise that says:
Define a method sum_to_n? which takes an array of integers and an
additional integer, n, as arguments and returns true if any two
elements in the array of integers sum to n. An empty array should sum
to zero by definition.
I already created two methods that can do the job, but I'm not comfortable with any of them because I think they are not written in the Ruby Way. I hope some of you can help me to learn which would be the right way!
The first method I made uses the each method for both iterations, but what I don't like about this method is that every number is summed with every other number, even with the same number, doing something like this:
arr[1, 2, 3, 4] => 1+1, 1+2, 1+3, 1+4, 2+1, 2+2, 2+3, 2+4, 3+1, 3+2... 4+3, 4+4
As you can see, there's a lot of repeated sums, and I don't want that.
This is the code:
def sum_to_n?(arr, n)
arr.each {|x| arr.each {|y| return true if x + y == n && x != y}}
return true if n == 0 && arr.length == 0
return false
end
With the other method I got what I wanted, just a few sums without repeating any of them or even summing the same numbers, but it looks HORRIBLE, and I'm pretty sure someone would love to kill me for doing it this way, but the method does a great job as you can see:
arr[1, 2, 3, 4] => 1+2, 1+3, 1+4, 2+3, 2+4, 3+4
This is the code:
def sum_to_n?(arr, n)
for i in 0..arr.length - 1
k = i + 1
for k in k..arr.length - 1
sum = arr[i] + arr[k]
if sum == n
return true
end
end
end
return true if n == 0 && arr.length == 0
return false
end
Well, I hope you guys have fun doing a better and prettier method as I did trying.
Thank you for your help.
I'd write it like this:
def sum_to_n?(arr, n)
return true if arr.empty? && n.zero?
arr.combination(2).any? {|a, b| a + b == n }
end
That seems to be a pretty Rubyish solution.
I came across this on CodeWars. The accepted answer sure does look very Rubyish, but that is at the cost of performance. Calling arr.combination(2) results in a lot of combinations, it'd be simpler to go over the array element by element and search whether the 'complement' sum - element exists. Here's how that'd look like -
def sum_to_n?(arr, n)
(arr.empty? and n.zero?) or arr.any? { |x| arr.include?(n - x) }
end
Beside #jorg-w-mittag's answer. I found another solution using 'permutation'.
https://stackoverflow.com/a/19351660/66493
def sum_to_n?(arr, n)
(arr.empty? && n.zero?) || arr.permutation(2).any? { |a, b| a + b == n }
end
I didn't know about permutation before.
Still like #jorg-w-mittag answer because its more readable.
This one will do it in O(n.log(n)) rather than O(n²):
a = 1, 2, 3, 4
class Array
def sum_to? n
unless empty?
false.tap {
i, j, sorted = 0, size - 1, sort
loop do
break if i == j
a, b = sorted[i], sorted[j]
sum = a + b
return a, b if sum == n
sum < n ? i += 1 : j -= 1
end
}
end
end
end
a.sum_to? 7 #=> [3, 4]
I had a thought that the beginning of any answer to this question should probably start with pruning the array for superfluous data:
Can't use this:
arr.select! { |e| e <= n } # may be negative values
But this might help:
arr.sort!
while arr[0] + arr[-1] > n # while smallest and largest value > n
arr.delete_at(-1) # delete largest vaue
end
i wonder why no answers here using hash ?
def sum_to_n?(arr, n)
return true if arr.empty? && n.zero?
h = {}
arr.any? { |x| complement = h[n-x]; h[x] = true; complement }
end
puts sum_to_n?([1,2,3,4,5,7], 6) # true
puts sum_to_n?([6,2,3,5,7,9], 6) # false
puts sum_to_n?([3,4,5,3], 6) # true
puts sum_to_n?([3,4,5,7], 6) # false
puts sum_to_n?([], 6) # false
puts sum_to_n?([], 0) # true
I like rohitpaulk's answer but it fails when n doubles x. We should remove x from the array before sending include? n - x.
def sum_to_n?(arr, n)
return true if arr.empty? && n.zero?
arr.any? { |x| arr.tap { arr.delete_at arr.index x }.include? n - x }
end
Lam Phan's answer using a hash is the best

Resources