Using memoization for storing values in ruby array - arrays

For a short array the following function works well. It's supposed to return the first array pair that whe sum is equal to a given integer. However, if the array has a length upwards of 10 million elements, the request times out, because (I think) is storing thousands of values in the variable I create in the first line. I know I have to use memoization (||=) but have no idea how to use it.
array1 = [1,2,3,4,5,6,7]
number = 3
array2 = [1,2,3.....n] # millions of elements
combos = array1.combination(2).to_a
(combos.select { |x,y| x + y == number }).sort.first
I need to gather all possible pairs to sort them, I'm using select to go through the entire list and not stop at the first pair that returns true.

This is one of the possible solutions.
def sum_pairs(ints, s)
seen = {}
for i in ints do
return [s-i, i] if seen[s-i]
seen[i] = true
end
nil
end

def find_smallest(arr, nbr)
first, *rest = arr.sort
until rest.empty?
matching = rest.bsearch { |n| n == nbr - first }
return [first, matching] unless matching.nil?
first, *rest = rest
end
nil
end
arr = [12, 7, 4, 5, 14, 9]
find_smallest(arr, 19) #=> [5, 14]
find_smallest(arr, 20) #=> nil
I've used the method Array#bsearch (rather than Enumerable#find to speed up the search for an element equal to nbr - first (O(log rest.size) vs. O(rest.size)).

Related

do I make this method work. The goal is to create a new set or array in which each element in the original set is multiplied by 2

def new_array(nums)
i = 0
double = []
while i <= nums.length
new_array = double[i] * 2
double << new_array
i += 1
end
return double
end
puts new_array[2, 4, 6, 8]
You can use map to create a new array with each value multiplied by 2.
def array_times_two(original_array)
original_array.map { |n| n * 2 }
end
array_times_two([2, 4, 6, 8])
=> [4, 8, 12, 16]
new_array = double[i] * 2
You are mixing up your variables. new_array is not an array, it is the name of your method. double is an empty array, so double[i] does not exist. Also, it loops once too many (the loop starts to count at zero). Try this:
while i < nums.length # not <=
double << nums[i] * 2
i += 1
end
Futhermore, call your method like this: puts new_array([2, 4, 6, 8]) , so with brackets (or put a space between the method and the argument), otherwise ruby itself will be mixed up.
Doubling the elements of an array is better done using map or map! (depending on what exactly you want to achieve), but I guess you try to implement the algorithm in this hard way (without using the Ruby core library) as a programming exercise, so I focus on your question why your approach does not work:
In your code, when you execute
new_array = double[i] * 2
double[i] is always nil, because you initialize double to the empty array and update it later, after the multiplication. You obviously can't multiply nil and will get an error message from this line.

Getting nil error when adding elements in Array - Ruby

I am trying to get a number from the user, store this number in the array and then add everything in the array together to display a total.
names = Array.new(20)
sum = 0
x = 0
for index in 0..5
puts "Enter a number: "
data = gets.to_i
names.push data
x = x + names[index]
end
puts x
But I am getting the error rb:10:in `+': nil can't be coerced into Integer (TypeError)
I guess its not allowing me to add whatever is in the array together. Anybody know a workaround for this?
There are some issues with your code.
Array.new(20) – you probably think this creates an array of the given size. Well, it does, but it also fills the array with a default object of nil:
Array.new(3)
#=> [nil, nil, nil]
In Ruby, you just create an empty array. It will grow and shrink automatically. And instead of Array.new you can use an array literal:
names = []
for index in 0..5 – for loops are very unidiomatic. You should use one of these:
(0..5).each do |index|
# ...
end
0.upto(5) do |index|
# ...
end
6.times do |index|
# ...
end
And finally:
names.push data
x = x + names[index]
You are pushing an element to the end of the array and then fetch it from the array using an absolute index. This only works if index and array size correlate exactly.
It's more robust to either use an explicit index for both, storing and fetching:
names[index] = data
x = x + names[index]
or to fetch the last element: (note that index isn't needed)
names.push data
x = x + names.last
Ruby also provides negative indices that are relative to the array's end:
names.push data
x = x + names[-1]
Needless to say, you could just omit the fetching:
names.push data
x = x + data
It might be useful to separate the data gathering from the calculation:
numbers = []
6.times do
puts 'Enter a number: '
numbers << gets.to_i
end
and then:
numbers = [1, 2, 3, 4, 5, 6] # <- example input
numbers.sum
#=> 21
# -- or --
numbers.inject(:+)
#=> 21
# -- or --
sum = 0
numbers.each { |n| sum += n }
sum
#=> 21
You are initializing the array with 20 nil names. Then you push the first entered number to the array (position 21) and try to concat the position [0] (names[0]) that is nil.
Try to change the first line:
names = Array.new

How to square an array of numbers in ruby with while without `each` `map` or `collect` methods?

I'm new to coding in RUBY. I'm trying to write a method that squares each element in an array of numbers and returns a new array of these numbers squared. Trying to use while loop and NOT use each, collect, or map. Having trouble understanding how to index/loop each individual element of array and square is (**).
This is what makes sense to me but I know its wrong.
def square_array(numbers)
count = 0
while count < numbers.length do
numbers.index ** 2
end
square_array(numbers)
end
Will anyone please help me? Thanks!
The easy way to do it is map, of course:
def square_array(numbers)
numbers.map { |e| e ** 2 }
end
But here's what you have to do to do the same with a while loop (which is good practice).
Create an array to contain the transformed data.
Create a counter (you've done that).
Set up your while loop (as you have it, except you don't need the do at the end).
Write a statement that squares the array element whose index is the same as your counter, and pushes that result into the array you created in step 1.
Increment your counter by 1 (you forgot to do that, so you'll be getting an endless loop since count will always equal zero).
Return the array you created in step 1.
That will do it for you! See if you can put that together, rather than me just giving you the code.
def square_array(numbers)
# Allocate an array with the same size as `numbers`
# so that the runtime does not have to resize it from time to time
result = Array.new(numbers.size)
# The index
i = 0
while i < numbers.size
# Fill the result array
result[i] = numbers[i] ** 2
# and don't forget to increase the index,
# otherwise the loop will run forever.
i += 1
end
# Return the result array
result
end
The more functional approach would be to use recursion.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
And for any mapper (e. g. square root,) use it like:
fun.([1,2,3,4,5], ->(e) { e ** 2 }, fun)
#⇒ [1, 4, 9, 16, 25]
Please note! This approach mutates the initial array, so it should be explicitly array.dup’ed before passing to the function. To eliminate the necessity to pass the function itself through and leave the initial array intact, we would need a wrapper.
fun =
->(acc = [], arr, map, fun) {
arr.empty? ? acc : fun.(acc << map.(arr.shift), arr, map, fun)
}
#⇒ #<Proc:0x000055ab64333fa0#(pry):12 (lambda)>
mapper = ->(arr, map) { fun.([], arr.dup, map, fun) }
And use it like:
arr = [1,2,3,4,5]
mapper.(arr, ->(e) { e ** 2 })
#⇒ [1, 4, 9, 16, 25]
arr
#⇒ [1, 2, 3, 4, 5]
def sq(arr)
enum = arr.each
a = []
loop do
n = enum.next
a << n*n
end
a
end
sq [1, 2, 3, 4]
#=> [1, 4, 9, 16]
See Array#each, Kernel#loop and Enumerator#next. One could use Kernel#to_enum (documented in Object) in place of Array#each.
Using a for loop?
ary = [1,2,3]
res = []
for n in ary do
res << n ** 2
end
res
#=> [1, 4, 9]
But better you stick with map.
Here is my solution:
def square_array(numbers)
new_array = []
counter = 0
while counter < numbers.length()
new_array.push(numbers[counter] * numbers[counter])
counter += 1
end
return new_array
end
Without using each, map, or collect.
def square_array(array)
new_array = []
array.length.times do |index|
new_array.push(array[index] ** 2)
end
new_array
end

Ruby - Efficient method of checking if sum of two numbers in array equal a value

Here's my problem: I have a list of 28,123 numbers I need to iterate through and an array of 6965 other numbers checking if the sum of two numbers (can be the same number) have equal value to each of the 28,123 numbers. I want to put them in a new array or mark them as true / false. Any solutions I've come up with so far are extremely inefficient.
So a dumbed-down version of what I want is if I have the following: array = [1, 2, 5] and the numbers 1 to 5 would return result = [2, 3, 4] or the array of result = [false, true, true, true, false]
I read this SE question: Check if the sum of two different numbers in an array equal a variable number? but I need something more efficient in my case it seems, or maybe a different approach to the problem. It also doesn't seem to work for two of the same number being added together.
Any help is much appreciated!
non_abundant(n) is a function that returns the first n non_abundant numbers. It executes almost instantaneously.
My Code:
def contains_pair?(array, n)
!!array.combination(2).detect { |a, b| a + b == n }
end
result = []
array = non_abundant(6965)
(1..28123).each do |n|
if array.index(n) == nil
index = array.length - 1
else
index = array.index(n)
end
puts n
if contains_pair?( array.take(index), n)
result << n
end
end
numbers = [1, 2, 5]
results = (1..10).to_a
numbers_set = numbers.each_with_object({}){ |i, h| h[i] = true }
results.select do |item|
numbers.detect do |num|
numbers_set[item - num]
end
end
#=> [2, 3, 4, 6, 7, 10]
You can add some optimizations by sorting your numbers and checking if num is bigger then item/2.
The complexity is O(n*m) where n and m are lengths of two lists.
Another optimization is if numbers list length is less then results list (n << m) you can achieve O(n*n) complexity by calculating all possible sums in numbers list first.
The most inefficient part of your algorithm is the fact that you are re-calculating many possible sums of combinations, 28123 times. You only need to do this once.
Here is a very simple improvement to your code:
array = non_abundant(6965)
combination_sums = array.combination(2).map {|comb| comb.inject(:+)}.uniq
result = (1..28123).select do |n|
combination_sums.include? n
end
The rest of your algorithm seems to be an attempt to compensate for that original performance mistake of re-calculating the sums - which is no longer needed.
There are further optimisations you could potentially make, such as using a binary search. But I'm guessing this improvement will already be sufficient for your needs.

How to find duplicates in array without using `uniq` method

I am doing a challenge to make a method that finds duplicate values in an array, and prints out a new array without the duplicates. Ruby has a built in uniq method; however, I am not allowed to use it.
In my mind, this should work:
def uniques(array)
tempPos = 0
arrayPos = 0
duplicate = true
result = [] # array the result will be "pushed" too
for arrayPos in 0..array.length
for tempPos in 0..array.length
# If the values at the indexes are the same. But the indexes are not the same.
# we have a duplicate
if array[arrayPos] == array[tempPos] && arrayPos != tempPos
duplicate = true
else
duplicate = false
end
if duplicate == false
result[arrayPos] = array[arrayPos]
end
end
puts duplicate
end
puts result.inspect
end
Output:
uniq *this is the short hand user input to run the method*
false
false
false
false
false
false
[1, 2, 1, 4, 5, nil]
I must be doing something wrong.
Are you allowed to use a Set?
require 'set'
array = [1, 2, 3, 3, 3, 4]
Set.new(array).to_a
#=> [1, 2, 3, 4]
An other way is to iterate over every pair in the array:
array.each_cons(2).with_object([array.first]) do |pair, result|
result << pair.last unless pair.first == pair.last
end
#=> [1, 2, 3, 4]
There are many ways to do that. Here's another. Suppose:
arr = [3,5,1,3,4,1,1]
Construct:
h = arr.group_by(&:itself)
#=> {3=>[3, 3], 5=>[5], 1=>[1, 1, 1], 4=>[4]}
The duplicates are given by:
h.select { |_,v| v.size > 1 }.keys
#=> [3, 1]
and an array without the duplicates is given by:
h.keys
#=> [3, 5, 1, 4]
Your logic works fine altough as mentioned above a set would work better. You could also sort the elements, and then find adjacent pairs that are the same value which wouldn't work as well as a set, but would have slightly better run-time than your current solution:
To polish what you currently have:
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = false
for tempPos in 0...result.length
# if the values at the indexes are the same... but the indexes are not the same...
# we have a duplicate
duplicate ||= (array[arrayPos] == result[tempPos])
end
if !duplicate
result << array[arrayPos]
end
end
puts result
end
an slightly better approach (altought still poor performance):
def uniques(array)
result = [] # array the result will be "pushed" too
for arrayPos in 0...array.length
duplicate = result.include?(array[arrayPos])
if !duplicate
result << array[arrayPos]
end
end
puts result
end
Although this solution is OK for a learning assignment, you should note that the complexity of this is O(n^2) (n-squared). What that means is that for an array of size n (for example n=10), you are doing n-squared (100) iterations.
It gets exponentially worse. If you have an array of length 1,000,000, you are doing 1,000,000,000,000 iterations. This is why using a set is so important, it's average run-time will be much lower.
A fairly simple way to so this is to leverage array.include?
new = []
arr.each { |x| new << x unless new.include?(x)}
puts new
That will give you an array (new) that only includes unique elements from the original array (arr)
Duplicate array easy way
arr1 = [1,3,4,5,6,6,6,1]
arry = Array.new(arr1)
puts arry
Find uniq array easy way using OR operator
arr1 = [1,3,4,5,6,6,6,1]
arr2 = Array.new # creating new array
arry = arr1 | arr2 # compare two array using OR operator
puts arry

Resources