Reduce array of numbers (sequence) - ruby

Imagine having an array of numbers defined as: a = [18, 20, 21, 22, 23]. I want to modify it so that it would look like this: a = [18, (20..23)]. What is the most elegant solution for that one?

Here is a code minimally modified from doc: Enumerable#slice_before.
i = a[0]
a.slice_before do |e|
i, j = e, i
j + 1 != e
end
.flat_map{|es| es.length < 3 ? es : es.first..es.last}
# => [18, 20..23]

a = [18, 20, 21, 22, 23,13,14].sort!
prev = a[0]
p a.slice_before { |e|
prev, prev2 = e, prev
prev2 + 1 != e
}.flat_map{|i| next Range.new(i.first,i.last) if i.size !=1 ; i}
#=> [13..14, 18, 20..23]

Related

Why "studentRequired < m" returns true in minimum allocation problem

In Book allocation problem in isPossible() function why even if studentsRequired are less than m (students we need to allocate books for ) results the curr_min to be a viable sollution and we return true ? Does't it mean that books are not allocated to all the students ?
for example if the array is [5, 82, 52, 66, 16, 37, 38, 44, 1, 97, 71, 28, 37, 58, 77, 97, 94, 4, 9]
with m = 16. Maximum value studentsRequired gets is 13. does't it mean that only 13 students got the books but 16 should have ?
here is the code in JS, for other languages ( cpp, java, python ) please get to this GFG page
function isPossible(arr, n, m, curr_min) {
let studentsRequired = 1;
let curr_sum = 0;
for (let i = 0; i < n; i++) {
if (arr[i] > curr_min) return false;
if (curr_sum + arr[i] > curr_min) {
studentsRequired++;
curr_sum = arr[i];
if (studentsRequired > m) return false;
} else {
curr_sum += arr[i];
}
}
return true;
}
function findPages(arr, n, m) {
let sum = 0;
if (n < m) return -1;
for (let i = 0; i < n; i++) sum += arr[i];
let start = 0,
end = sum;
let result = Number.MAX_VALUE;
while (start <= end) {
let mid = Math.floor((start + end) / 2);
if (isPossible(arr, n, m, mid)) {
result = Math.min(result, mid);
end = mid - 1;
} else start = mid + 1;
}
return result;
}
const ans = findPages(
[5, 82, 52, 66, 16, 37, 38, 44, 1, 97, 71, 28, 37, 58, 77, 97, 94, 4, 9],
19,
16
);
console.log("Ans : ", ans);
What i think is happening =>
When i dry run the code for test case arr = [10,20,30,40] with m = 4, the result is S1 = [10,20], S2 = [30], S3 = [40] and we did not allocated a book to S4.
Are we assuming that students with more books (like S1 had [10,20]) can transfer there last book, 20 in this case to there right student ? And then S2 will have [20,30] so 30 will be then transferred to S3 and in the end S3 will tranfer its last book to S4 making the condition "Every student must have a book" true as now S1 = [10], S = [20], S3 = [30], S4 = [40]? Or its something else ?

Decompose string to form a valid expression

I am given a string S (of integers) and a number N. I want to insert arbitrary number of '+' in S so that the sum becomes equal to N.
Ex:<br>
S = 15112 and N = 28<br>
Ans is : 15+11+2<br>
S = 120012 and N = 33<br>
Ans is : 1+20+012<br>
S = 123 and N = 123<br>
Ans is : 123
given : |S| <= 120 and N <= 10^6
It is guarenteed that S and N are given such that it is always possible to form valid expression. Is there any algorithm which can solve this? I tried to think on it but couldn't come up with solution.
There may be more efficient ways to do this, but since you have nothing so far…
You can simply find all combinations of a boolean array that indicates whether a plus should exist between the numbers or not.
For example: with an input of 112134, 1 + 12 + 13 + 4 can be represented with the boolean array [true, false, true, false, true] indicating that there is a plus after the 1st, 3rd, and 5th numbers. The problem then reduces to finding which combinations add to your number. There are lot of ways to find combinations. Recursive backtracking is a classic.
In javascript/node this might look like this:
function splitOnIndexes(arr, a) {
// split the array into numbers based on the booleans
let current = "" + arr[0]
let output = []
for (let i = 0; i < a.length; i++) {
if (!a[i]) {
current += arr[i + 1]
} else {
output.push(current)
current = "" + arr[i + 1]
}
}
output.push(current)
return output
}
function findSum(input, total) {
function backtrack(n, k = 0, a = []) {
const sum = (arr) => arr.reduce((a, c) => a + parseInt(c), 0)
if (k === n) {
let ans = splitOnIndexes(input, a)
if (sum(ans) === total) {
console.log(ans.join(' + '))
}
} else {
k = k + 1
let c = [true, false]
for (let i = 0; i < 2; i++) {
a[k - 1] = c[i]
backtrack(n, k, a)
}
}
}
backtrack(input.length - 1)
}
findSum('15112', 28)
findSum('120012', 33)
findSum('123', 123)
As you can see, more than one answer is possible. Your first example is solved with both 15+1+12 and 15+11+2. If you only need one, you can of course stop early.
The idea is to use dynamic programming, you only care about sums between 0 and 10^6 and only have 120 possible indexes. if dp[i][j] = x, it means that from index x of the string, we went to index i (so we added a + before i) and we got a sum of j. This leads to a O(|S| * N) solution:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
string s;
long n;
long dp[123][1000001];
void solve (int index, long sum) {//index = what index of s still remains to scan. sum = the sum we have accumulated till now
if (sum >= n or index >= s.length()) return;
if (dp[index][sum] != -1) return;
if (index == n and sum == n) return;
long num = 0;
for (int i = 0; i < 7 && index + i < s.length(); i++) { //N has 6 digits at most
num = stoi(s.substr(index, i + 1));
solve(index + i + 1, sum + num);
if (sum + num <= n) {
dp[index + i + 1][sum + num] = index;
}
}
}
int main () {
cin >> s;
cin >> n;
for (int i = 0; i < 121; i++) {
for (int j = 0; j < 1000001; j++) {
dp[i][j] = -1;
}
}
solve(0, 0);
int sum = n;
int idx = s.length();
vector<string> nums;
//reconstruct solution
while (idx != 0) {
nums.push_back(s.substr(dp[idx][sum], idx - dp[idx][sum]));
idx = dp[idx][sum];
sum -= stoi(nums[nums.size() - 1]);
}
for (int i = nums.size() -1; i >= 0; i--) {
cout << nums[i];
if (i != 0) cout << "+";
}
}
This is a Ruby version with step by step explanation of the algorithm, so you can easily code in C++ (or I'll try later).
# Let's consider that we extracted the values from text, so we already have the string of int and the result as integer:
string_of_int = "15112"
result = 28
# The basic idea is to find a map (array) that tells how to group digits, for example
sum_map = [2, 1, 2]
# This means that string_of_int is mapped into the following numbers
# 15, 1, 12
# then sum the numbers, in this case 15+1+12 = 28
# For finding a the solution we need to map
# all the possible combinations of addition given the n digits of the string_of_int then check if the sum is equal to the result
# We call k the number of digits of string_of_int
# in ruby we can build an array called sum_maps
# containing all the possible permutations like this:
k = string_of_int.length # => 5
sum_maps = []
k.times do |length|
(1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k}
end
sum_maps
# => [[1, 5], [2, 4], [3, 3], [4, 2], [5, 1], [1, 1, 4], [1, 2, 3], [1, 3, 2], [1, 4, 1], [2, 1, 3], [2, 2, 2], [2, 3, 1], [3, 1, 2], [3, 2, 1], [4, 1, 1]]
# Now must check which of of the sum_map is giving us the required result.
#
# First, to keep the code short and DRY,
# better to define a couple of useful methods for the String class to use then:
class String
def group_digits_by(sum_map)
string_of_int_splitted = self.split("")
grouped_digits = []
sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
grouped_digits.reject { |element| element == 0 }
end
def sum_grouped_of_digits_by(sum_map)
group_digits_by(sum_map).inject(:+)
end
end
# So we can call the methods directly on the string
# for example, in ruby:
string_of_int.group_digits_by sum_map #=> [15, 1, 12]
string_of_int.sum_grouped_of_digits_by sum_map #=> 28
# Now that we have this metods, we just iterate through the sum_maps array
# and apply it for printing out the sm_map if the sum of grouped digits is equal to the result
# coded in ruby it is:
combinations = []
sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
p combinations.uniq
# => [[15, 1, 12], [15, 11, 2]]
In short, written as a Ruby module it becomes:
module GuessAddition
class ::String
def group_digits_by(sum_map)
string_of_int_splitted = self.split("")
grouped_digits = []
sum_map.each { |n| grouped_digits << string_of_int_splitted.shift(n).join.to_i}
grouped_digits.reject { |element| element == 0 }
end
def sum_grouped_of_digits_by(sum_map)
group_digits_by(sum_map).inject(:+)
end
end
def self.guess_this(string_of_int, result)
k = string_of_int.length
sum_maps = []
k.times { |length| (1..k).to_a.repeated_permutation(length).each {|e| sum_maps << e if e.inject(:+) == k} }
combinations = []
sum_maps.each { |sum_map| combinations << string_of_int.group_digits_by(sum_map) if string_of_int.sum_grouped_of_digits_by(sum_map) == result }
combinations.uniq
end
end
p GuessAddition::guess_this("15112", 28) # => [[15, 1, 12], [15, 11, 2]]

Generating Ascending Sequence 2^p*3^q

I was interested in implementing a specific Shellsort method I read about that had the same time complexity as a bitonic sort. However, it requires the gap sequence to be the sequence of numbers [1, N-1] that satisfy the expression 2^p*3^q for any integers p and q. In layman's terms, all the numbers in that range that are only divisible by 2 and 3 an integer amount of times. Is there a relatively efficient method for generating this sequence?
Numbers of that form are called 3-smooth. Dijkstra studied the closely related problem of generating 5-smooth or regular numbers, proposing an algorithm that generates the sequence S of 5-smooth numbers by starting S with 1 and then doing a sorted merge of the sequences 2S, 3S, and 5S. Here's a rendering of this idea in Python for 3-smooth numbers, as an infinite generator.
def threesmooth():
S = [1]
i2 = 0 # current index in 2S
i3 = 0 # current index in 3S
while True:
yield S[-1]
n2 = 2 * S[i2]
n3 = 3 * S[i3]
S.append(min(n2, n3))
i2 += n2 <= n3
i3 += n2 >= n3
Simplest I can think of is to run a nested loop over p and q and then sort the result. In Python:
N=100
products_of_powers_of_2and3 = []
power_of_2 = 1
while power_of_2 < N:
product_of_powers_of_2and3 = power_of_2
while product_of_powers_of_2and3 < N:
products_of_powers_of_2and3.append(product_of_powers_of_2and3)
product_of_powers_of_2and3 *= 3
power_of_2 *= 2
products_of_powers_of_2and3.sort()
print products_of_powers_of_2and3
result
[1, 2, 3, 4, 6, 8, 9, 12, 16, 18, 24, 27, 32, 36, 48, 54, 64, 72, 81, 96]
(before sorting the products_of_powers_of_2and3 is
[1, 3, 9, 27, 81, 2, 6, 18, 54, 4, 12, 36, 8, 24, 72, 16, 48, 32, 96, 64]
)
Given the size of products_of_powers_of_2and3 is of the order of log2N*log3N the list doesn't grow very fast and sorting it doesn't seem particularly inefficient. E.g. even for N = 1 million, the list is very short, 142 items, so you don't need to worry.
You can do it very easy in JavaScript
arr = [];
n = 20;
function generateSeries() {
for (let i = 0; i < n; i++) {
for (let j = 0; j < n; j++) {
arr.push(Math.pow(2, i) * Math.pow(3, j))
}
}
sort();
}
function sort() {
arr.sort((a, b) => {
if (a < b) {return -1;}
if (a > b) {return 1;}
return 0;
});
}
function solution(N) {
arr = [];
if (N >= 0 && N <= 200 ) {
generateSeries();
console.log("arr >>>>>", arr);
console.log("result >>>>>", arr[N]);
return arr[N];
}
}
N = 200
res =[]
a,b = 2,3
for i in range(N):
for j in range(N):
temp1=a**i
temp2=b**j
temp=temp1*temp2
if temp<=200:
res.append(temp)
res = sorted(res)
print(res)

ruby: index of all elements in array lesser than given value

I have an array
a=[10,20,30,10,3,2,200]
I want to find indexes of the element having value less than , say 21. I can surely do this in loop, but i was wondering if there is better one liner approach of it. Like we have in R.
Do as below using Array#each_index:
a = [10,20,30,10,3,2,200]
a.each_index.select { |i| a[i] < 21 }
# => [0, 1, 3, 4, 5]
I am using ruby 1.8.5
Then do
a = [10,20,30,10,3,2,200]
a.size.times.select { |i| a[i] < 21 }
# => [0, 1, 3, 4, 5]
Here are some more choices for those occasions when a straigtforward solution just won't do:
a = [10,20,30,10,3,2,200]
a.size.times.with_object([]) { |i,b| b << i if a[i] < 21 } # or a.each_index.with...
a.size.times.map { |i| a[i] < 21 ? i : nil }.compact # or a.each_index.with...
a.zip( Array(0...a.size) ).select { |e,_| e < 21 }.map(&:last)
a.reduce([[],0]) { |(b,i),e| [e < 21 ? b << i : b, i + 1] }.first
a.each_with_object([]) {|e,a| a << (e.to_i < 21 ? a.size : nil) }.compact
a.each_with_object([[],[]]) {|e,(g,b)|
e < 21 ? g << (g.size + b.size) : b << nil }.first
(a.each_with_object([-1]) { |_,b| i = a[b.last+1..-1].index {
|f| f < 21 }; b << b.last + 1 + i if i })[1..-1]
b = a.dup
a.each_with_object([]) { |e,c| i = b.index(e); c << i if e < 21; b[i] = nil }
b = (a + [nil])
a.each.with_object([]) { |_,c|
c << (a.size - b.index(nil)) if b.first.to_i < 21; b.rotate! }
a.join(',').gsub(/(\d+)/) { |e| (e.to_i < 21) ? $`.count(',') : nil } \
.gsub(',,',',').split(',').map(&:to_i)
# All => [0, 1, 3, 4, 5]
This should do the trick and is linear:
a.each_with_index.inject([]) {|indexes, pair| indexes << pair.last if pair.first < 21; indexes}
Ruby usually do not need to find indexes, why are you trying to do it?
This should work fine in 1.8:
[10,20,30,10,3,2,200].each_with_index.select{|x,_|x<21}.map{|_,i|i}

How can I convert this array so that each element represents the cumulative value of the previous elements? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Cumulative array sum in Ruby
I have an array of integers like this
[20, 25, 40, 60]
How can I turn it into an array with each element representing the cumulative value of the elements before it, including itself?
[20, 45, 85, 145]
I'm using Rails 3.2.0 & ruby 1.9.3
s = 0
[20, 25, 40, 60].map{|e| s += e}
[20, 25, 40, 60].reduce([]) do |arr, v|
arr << (arr.last || 0) + v
end
Or an ugly one liner.
[20, 25, 40, 60].reduce([0]){ |a, v| a << a[-1] + v }[1..-1]
array = [20, 25, 40, 60]
(array.size - 1).times { |i| array[i + 1] += array[i] }
puts array
# => [20, 45, 85, 145]
arr = [20, 25, 40, 60]
first = []
sum = 0
arr.each do |e|
sum += e
first << sum
end
puts first
arr.each_with_index.map{|x, i| x + (i==0 ? 0 : arr[0..i-1].inject(:+))}
=> [20, 45, 85, 145]
Matlab:
B = cumsum(A)
Ruby:
class Array
def ruby_cumsum!
(1..size-1).each {|i| self[i] += self[i-1] }
self
end
end

Resources