ruby - Faster prime gap search - arrays

I'm working on creating a method, gap(g, m, n).
All 3 parameters are integers.
gap finds the first 2 consecutive prime numbers, between m and n, that have a difference of g.
For example, gap(2, 1, 10) => [3, 5]
In the range from m to n, 1..10,
the first 2 consecutive prime numbers with a gap of 2 is [3,5].
If instead, it was gap(1, 1, 10) => [2,3]
and if it was gap(6, 1, 10) => nil
https://repl.it/BbGo/1
# method to check if a number is prime
def prime?(num)
(2..Math.sqrt(num).floor).each do |m|
if num % m == 0
return false
end
end
true
end
this method works by iterating through each number from 2, the smallest prime, to the square root of the parameter, checking to see if the parameter is evenly divisible by anything in that range. If it is, the method returns false.
# gap method
def gap(g, m, n)
if g.odd? && g > 1
return nil
end
primes = (m..n).select do |num|
num.odd? && prime?(num)
end
first = primes[0..-2].find_index do |x|
primes[primes.index(x) + 1] - x == g
end
[
primes[first],
primes[first+1]
] unless first.nil?
end
gap(2, 10000000, 11000000)
All prime numbers have a gap of either 2, 4, or a number made up of 2 and 4's added together. The only exception is the gap from 2-3.
So if the gap argument, g, given is a number like 3, which is both odd and greater than 1, the method automatically returns nil because no such prime gap exists.
Problem
My issue is that the method is too slow. By using the replit link above, you get a time of about 20 seconds. Apparently it is possible to get it to about 1 second.
I've tried optimizing by filtering out the even numbers already from m..n, which helped. But I'm just not sure how I can get this to go even faster.
What I'm thinking is before finding out every single prime number in m..n, I should check each iteration if the gap is correct, so once I find it I can just terminate the method without looking at unnecessary primes, but I'm not sure how to implement this.
Thanks for the help, and any general criticism of my code is welcome as well.

Answer using Jordan's advice:
def gap(g, m, n)
if g.odd? && g > 1
return nil
end
recent = 0
current = 0
(m..n).each do |num|
if num.odd? && prime?(num)
current = num
if current - recent == g
break
else
recent = current
end
end
end
[recent, current] unless current - recent != g
end
gap(2, 10000000, 11000000)
#=> [10000139, 10000141]
completes in 3 ms.
https://repl.it/BbGo/3
Thanks!

Related

Find Minimum Score Possible

Problem statement:
We are given three arrays A1,A2,A3 of lengths n1,n2,n3. Each array contains some (or no) natural numbers (i.e > 0). These numbers denote the program execution times.
The task is to choose the first element from any array and then you can execute that program and remove it from that array.
For example:
if A1=[3,2] (n1=2),
A2=[7] (n2=1),
A3=[1] (n3=1)
then we can execute programs in various orders like [1,7,3,2] or [7,1,3,2] or [3,7,1,2] or [3,1,7,2] or [3,2,1,7] etc.
Now if we take S=[1,3,2,7] as the order of execution the waiting time of various programs would be
for S[0] waiting time = 0, since executed immediately,
for S[1] waiting time = 0+1 = 1, taking previous time into account, similarly,
for S[2] waiting time = 0+1+3 = 4
for S[3] waiting time = 0+1+3+2 = 6
Now the score of array is defined as sum of all wait times = 0 + 1 + 4 + 6 = 11, This is the minimum score we can get from any order of execution.
Our task is to find this minimum score.
How can we solve this problem? I tried with approach trying to pick minimum of three elements each time, but it is not correct because it gets stuck when two or three same elements are encountered.
One more example:
if A1=[23,10,18,43], A2=[7], A3=[13,42] minimum score would be 307.
The simplest way to solve this is with dynamic programming (which runs in cubic time).
For each array A: Suppose you take the first element from array A, i.e. A[0], as the next process. Your total cost is the wait-time contribution of A[0] (i.e., A[0] * (total_remaining_elements - 1)), plus the minimal wait time sum from A[1:] and the rest of the arrays.
Take the minimum cost over each possible first array A, and you'll get the minimum score.
Here's a Python implementation of that idea. It works with any number of arrays, not just three.
def dp_solve(arrays: List[List[int]]) -> int:
"""Given list of arrays representing dependent processing times,
return the smallest sum of wait_time_before_start for all job orders"""
arrays = [x for x in arrays if len(x) > 0] # Remove empty
#functools.lru_cache(100000)
def dp(remaining_elements: Tuple[int],
total_remaining: int) -> int:
"""Returns minimum wait time sum when suffixes of each array
have lengths in 'remaining_elements' """
if total_remaining == 0:
return 0
rem_elements_copy = list(remaining_elements)
best = 10 ** 20
for i, x in enumerate(remaining_elements):
if x == 0:
continue
cost_here = arrays[i][-x] * (total_remaining - 1)
if cost_here >= best:
continue
rem_elements_copy[i] -= 1
best = min(best,
dp(tuple(rem_elements_copy), total_remaining - 1)
+ cost_here)
rem_elements_copy[i] += 1
return best
return dp(tuple(map(len, arrays)), sum(map(len, arrays)))
Better solutions
The naive greedy strategy of 'smallest first element' doesn't work, because it can be worth it to do a longer job to get a much shorter job in the same list done, as the example of
A1 = [100, 1, 2, 3], A2 = [38], A3 = [34],
best solution = [100, 1, 2, 3, 34, 38]
by user3386109 in the comments demonstrates.
A more refined greedy strategy does work. Instead of the smallest first element, consider each possible prefix of the array. We want to pick the array with the smallest prefix, where prefixes are compared by average process time, and perform all the processes in that prefix in order.
A1 = [ 100, 1, 2, 3]
Prefix averages = [(100)/1, (100+1)/2, (100+1+2)/3, (100+1+2+3)/4]
= [ 100.0, 50.5, 34.333, 26.5]
A2=[38]
A3=[34]
Smallest prefix average in any array is 26.5, so pick
the prefix [100, 1, 2, 3] to complete first.
Then [34] is the next prefix, and [38] is the final prefix.
And here's a rough Python implementation of the greedy algorithm. This code computes subarray averages in a completely naive/brute-force way, so the algorithm is still quadratic (but an improvement over the dynamic programming method). Also, it computes 'maximum suffixes' instead of 'minimum prefixes' for ease of coding, but the two strategies are equivalent.
def greedy_solve(arrays: List[List[int]]) -> int:
"""Given list of arrays representing dependent processing times,
return the smallest sum of wait_time_before_start for all job orders"""
def max_suffix_avg(arr: List[int]):
"""Given arr, return value and length of max-average suffix"""
if len(arr) == 0:
return (-math.inf, 0)
best_len = 1
best = -math.inf
curr_sum = 0.0
for i, x in enumerate(reversed(arr), 1):
curr_sum += x
new_avg = curr_sum / i
if new_avg >= best:
best = new_avg
best_len = i
return (best, best_len)
arrays = [x for x in arrays if len(x) > 0] # Remove empty
total_time_sum = sum(sum(x) for x in arrays)
my_averages = [max_suffix_avg(arr) for arr in arrays]
total_cost = 0
while True:
largest_avg_idx = max(range(len(arrays)),
key=lambda y: my_averages[y][0])
_, n_to_remove = my_averages[largest_avg_idx]
if n_to_remove == 0:
break
for _ in range(n_to_remove):
total_time_sum -= arrays[largest_avg_idx].pop()
total_cost += total_time_sum
# Recompute the changed array's avg
my_averages[largest_avg_idx] = max_suffix_avg(arrays[largest_avg_idx])
return total_cost

Finding max sum with operation limit

As an input i'm given an array of integers (all positive).
Also as an input i`m given a number of "actions". The goal is to find max possible sum of array elements with given number of actions.
As an "action" i can either:
Add current element to sum
Move to the next element
We are starting at 0 position in array. Each element could be added only once.
Limitation are:
2 < array.Length < 20
0 < number of "actions" < 20
It seems to me that this limitations essentially not important. Its possible to find each combination of "actions", but in this case complexity would be like 2^"actions" and this is bad...))
Examples:
array = [1, 4, 2], 3 actions. Output should be 5. In this case we added zero element, moved to first element, added first element.
array = [7, 8, 9], 2 actions. Output should be 8. In this case we moved to the first element, then added first element.
Could anyone please explain me the algorithm to solve this problem? Or at least the direction in which i shoudl try to solve it.
Thanks in advance
Here is another DP solution using memoization. The idea is to represent the state by a pair of integers (current_index, actions_left) and map it to the maximum sum when starting from the current_index, assuming actions_left is the upper bound on actions we are allowed to take:
from functools import lru_cache
def best_sum(arr, num_actions):
'get best sum from arr given a budget of actions limited to num_actions'
#lru_cache(None)
def dp(idx, num_actions_):
'return best sum starting at idx (inclusive)'
'with number of actions = num_actions_ available'
# return zero if out of list elements or actions
if idx >= len(arr) or num_actions_ <= 0:
return 0
# otherwise, decide if we should include current element or not
return max(
# if we include element at idx
# we spend two actions: one to include the element and one to move
# to the next element
dp(idx + 1, num_actions_ - 2) + arr[idx],
# if we do not include element at idx
# we spend one action to move to the next element
dp(idx + 1, num_actions_ - 1)
)
return dp(0, num_actions)
I am using Python 3.7.12.
array = [1, 1, 1, 1, 100]
actions = 5
In example like above, you just have to keep moving right and finally pickup the 100. At the beginning of the array we never know what values we are going to see further. So, this can't be greedy.
You have two actions and you have to try out both because you don't know which to apply when.
Below is a python code. If not familiar treat as pseudocode or feel free to convert to language of your choice. We recursively try both actions until we run out of actions or we reach the end of the input array.
def getMaxSum(current_index, actions_left, current_sum):
nonlocal max_sum
if actions_left == 0 or current_index == len(array):
max_sum = max(max_sum, current_sum)
return
if actions_left == 1:
#Add current element to sum
getMaxSum(current_index, actions_left - 1, current_sum + array[current_index])
else:
#Add current element to sum and Move to the next element
getMaxSum(current_index + 1, actions_left - 2, current_sum + array[current_index])
#Move to the next element
getMaxSum(current_index + 1, actions_left - 1, current_sum)
array = [7, 8, 9]
actions = 2
max_sum = 0
getMaxSum(0, actions, 0)
print(max_sum)
You will realize that there can be overlapping sub-problems here and we can avoid those repetitive computations by memoizing/caching the results to the sub-problems. I leave that task to you as an exercise. Basically, this is Dynamic Programming problem.
Hope it helped. Post in comments if any doubts.

What is the smallest jump I can make to move through a range, when all jumps are the same length and some positions cannot be landed on?

Working through CodeFights. This is problem 4 on level 5 https://codefights.com/arcade/intro/level-5/XC9Q2DhRRKQrfLhb5 :
You are given an array of integers representing coordinates of
obstacles situated on a straight line.
Assume that you are jumping from the point with coordinate 0 to the
right. You are allowed only to make jumps of the same length
represented by some integer.
Find the minimal length of the jump enough to avoid all the obstacles.
Example
For inputArray = [5, 3, 6, 7, 9], the output should be
avoidObstacles(inputArray) = 4.
Check out the image below for better understanding:
Input/Output
[time limit] 4000ms (rb) [input] array.integer inputArray
Non-empty array of positive integers.
Constraints: 2 ≤ inputArray.length ≤ 10, 1 ≤ inputArray[i] ≤ 40.
[output] integer
The desired length.
The natural method for making this happen seems like it would be step. Here is my code:
def avoidObstacles(arr)
jumps = (arr.min..arr.max+1).to_a - arr
full_map = (0..arr.max+1)
jumps.each do |j|
return j if (full_map.step(j).to_a & arr).empty?
end
end
A clearer way to write this:
def avoidObstacles(arr)
jumps = (arr.min..arr.max+1).reject{|n| arr.include?(n)}
full_map = (0..arr.max+1)
jumps.each do |j|
return j if full_map.step(j).none?{|n|arr.include?(n)}
end
end
I pass all of the visible tests given on the website:
Input: inputArray: [5, 3, 6, 7, 9] Expected Output: 4
Input: inputArray: [2, 3] Expected Output: 4
Input: inputArray: [1, 4, 10, 6, 2] Expected Output: 7
But I get tripped up on one of the hidden tests. I borrowed a solution from another user, which works in all cases:
def avoidObstacles a
obs = a.each_with_object(Hash.new(false)){|v, h| h[v]=true}
m = a.max
(1..m+1).each do |j|
return j if (0...m/j+1).all?{ |i| obs[i*j] == false }
end
m
end
I'm not quite sure where this borrowed solution succeeds and mine fails.
Thanks ahead of time for the help. I'm still quite new to coding and appreciate your time.
avoidObstacles [3,5,7] #=> 4
but it should be 2. You want
def avoid_obstacles(arr)
return nil if arr.empty? or arr.max == 1
jumps = (2..arr.max+1).to_a - arr
full_map = (0..arr.max+1)
jumps.each do |j|
return j if (full_map.step(j).to_a & arr).empty?
end
end
avoid_obstacles [3,5,7]
#=> 2
I changed the name of the method because the Ruby convention is to use snake-case for the names of methods and variables.
The other person's solution works because it starts the search at j = 1. (It could have started at j=2 because j = 1 will always fail.
I tried this in Java using the input constraints given (so did not use input validation)
int avoidObstacles(int[] inputArray) {
Arrays.sort(inputArray);
//start from 2 because jump length 1 will always fail
int minJump=2;
while(validateJumpValue(inputArray, minJump)){
minJump++;
}
return minJump;
}
//check if all the successive jumps can avoid touching obstacles
boolean validateJumpValue(int[] arr, int jump){
int successiveJump=jump;
while(Arrays.binarySearch(arr, successiveJump)<0 && successiveJump <= arr[arr.length-1]) {
successiveJump+=jump;
}
return successiveJump<=arr[arr.length-1];
}
Try this:
def avoidObstacles(inputArray):
obs = sorted(inputArray)
jump_dist = 1
obstacle_hit = True
while(obstacle_hit):
obstacle_hit = False
jump_dist += 1
for i in range(0, len(obs)):
#### 3%4 and 5%4 and 6%4 and 7%4 and 9%4 =! 0
if obs[i] % jump_dist == 0:
obstacle_hit = True
break
return jump_dist

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.

When / How to stop Ruby loop that creates nested arrays within nested arrays

I'm unsure when to end the loop that runs the map statement, the times is simply put in place as an example of where the loop should be and what code should be contained within. I would like to run it until the first value of the created multidimensional array is 0 (because it will consistently be the largest value until it becomes 0 itself and creates the last nested array), but I'm completely stumped on how to do so. Any help would be greatly appreciated!
def wonky_coins(n)
coins = [n]
if n == 0
return 1
end
i = 1
n.times do
coins.map! do |x|
if x != 0
i+= 2
else
next
o = []
o << x/2
o << x/3
o << x/4
x = o
puts x
end
end
end
return i
end
wonky_coins(6)
Problem:
# Catsylvanian money is a strange thing: they have a coin for every
# denomination (including zero!). A wonky change machine in
# Catsylvania takes any coin of value N and returns 3 new coins,
# valued at N/2, N/3 and N/4 (rounding down).
#
# Write a method `wonky_coins(n)` that returns the number of coins you
# are left with if you take all non-zero coins and keep feeding them
# back into the machine until you are left with only zero-value coins.
#
# Difficulty: 3/5
describe "#wonky_coins" do
it "handles a coin of value 1" do
wonky_coins(1).should == 3
end
it "handles a coin of value 5" do
wonky_coins(5).should == 11
# 11
# => [2, 1, 1]
# => [[1, 0, 0], [0, 0, 0], [0, 0, 0]]
# => [[[0, 0, 0], 0, 0], [0, 0, 0], [0, 0, 0]]
end
it "handles a coin of value 6" do
wonky_coins(6).should == 15
end
it "handles being given the zero coin" do
wonky_coins(0).should == 1
end
end
First of all, you should not have nested arrays. You want to flatten the array after each pass, so you just have coins; even better, use flat_map to do it in one step. 0 produces just itself: [0]; don't forget it or your code will lose all of your target coins!
Next, there is no logic to doing it n times that I can see. No fixed number of times will do. You want to do it until all coins are zero. You can set a flag at the top (all_zero = true), and flip it when you find a non-zero coin, that should tell your loop that further iterations are needed.
Also, you don't need to track the number of coins, since the number will be the final size of the array.
And finally, and unrelated to the problem, do get into the habit of using correct indentation. For one thing, it makes it harder for yourself to debug and maintain the code; for another, bad indentation makes many SO people not want to bother reading your question.
Went back a bit later now knowing about how to use .flatten and I got it! Thanks to #Amadan for the helpful tips. Feel free to leave any comments concerning my syntax as I am just starting out and can use all the constructive feedback I can get!
def wonky_coins(n)
coins = [n]
return 1 if n == 0
until coins[0] == 0
coins.map! { |x|
next if x == 0
x = [x/2, x/3, x/4]
}
coins.flatten!
end
return coins.length
end
wonky_coins(6)

Resources