Extracting values without looping in Ruby - ruby

Lets say we need to extract values from range of (0..999_999_999). We need the sum of all numbers that returns true for our conditional statement. For example, the ones that have the sequence of "123" digits at the end of their numbers.
What would be the fastest way to get this sum without looping?
Edit: The conditional statement could be any, such as n.to_s.chars.map{|d| d = d.to_i}.inject(:+) % 12345 == 0 where n is the number within the range.
Edit2: Here is the code that I have trouble with:
def find_all(n, k)
arr = []
lower_limit = ("1" + "0" * (k - 1)).to_i
upper_limit = ("9" * k).to_i
while lower_limit <= upper_limit
if lower_limit.to_s.chars == lower_limit.to_s.chars.sort && lower_limit.to_s.chars.map{|v| v = v.to_i}.inject(:+) == n
arr << lower_limit
end
lower_limit += 1
end
arr.empty? ? [] : [arr.size, arr.min, arr.max]
end
where n is the sum of all digits and k is the # of digits in number.
My code should run on the server in less than 12000 ms with very huge numbers of k several times with different (n,k). Even though my code works, its algorithm is too slow, looping will not result in success.

The range is
r = 0..10**9-1
r.size
#=> 1_000_000_000 (10**9)
For any given last three digits, this range contains 10**6 numbers ending in those three digits. Suppose those last three digits were 000. Then the sum of those numbers would be the sum of the numbers in the range 0..10**6-1 multiplied by 10**3:
s = (10**3) * (0 + 10**6-1)*(10**6)/2
#=> 499999500000000
If the last three digits of each of the 10**6 numbers in this sum were 123, rather than 000, 123 would be added to each of those numbers. Therefore, the sum of the numbers ending 123 is
s + 123 * (10**6)
#=> 499999746000000

Related

Given integers X and Y, how do you find the largest permutation of X that is less than or equal to Y?

Given two positive integers X and Y, find the largest permutation of X
that is less than or equal to Y. Return the largest permutation that is
less than or equal to Y as an integer. If there is no permutation of X
that is less than or equal to Y, return -1.
Example 1:
Input: X = 123, Y = 321
Output: 321
Example 2:
Input: X = 1733, Y = 3311
Output: 3173
Example 3:
Input: X = 999, Y = 111
Output: -1
Got this problem for an online assessment earlier yesterday, couldn't find an efficient solution for it and have been thinking about it but still can't think of the right approach. I first tried greedy, in which I would iterate Y from left to right and I create a permutation of X by appending the largest digit in X that is less than or equal to the digit in Y. But for X = 1733 and Y = 3311, my implementation would return -1 because the greedy algorithm rearranged X to 3317. So I turned to recursion, but as you'd expect this very quickly reached stack limit.
I've read this thread that seems to discuss a similar problem, but I believe the top solution fails for example 2. How do you approach this problem?
A recursive solution.
Sort the digits of X decreasingly. Then, as long as you find no solution
take in turn every digit in X that is not larger than the leading digit of Y;
if those digits are equal, recurse on X less this digit and the tail of Y;
if the digit of X is smaller (or X is empty), you are done;
if there is no such digit, you reached a dead-end.
This works because you are trying the permutations of X by decreasing value.
321 vs. 321
3 21 vs. 3 21
21 vs. 21
1 vs. 1
Done
7331 vs. 3311
3 731 vs. 3 311
3 71 vs. 3 11
1 7 vs. 1 1
Dead end
1 73 vs. 3 11
Done
999 vs. 111
Dead end
A non-recursive efficient solution, hinted by #Stef.
The permutations of X can be ordered increasingly by sorting the digits then picking every first digit and recursing on the remaining ones. This established a bijection between the permutations and the integers in [0, d!) for d digits.
For an integer m, you can retrieve the corresponding permutation using a conversion from the factorial basis (take the quotient by (d-1)! and proceed recursively with the remainder). This takes d operations, and you can compare the permutation to Y in O(d) operations.
Now just implement a dichotomic search on the d! permutations, which takes O(d.log(d!)) = O(d².log(d))) operations.
Update: the second solution only works for distinct digits otherwise the permutations do not yield increasing numbers. I hope that there is a workaround.
If X has more digits then there is no solution. If Y has more digits then a descending sort of the digits of X is the solution. Assuming X and Y have the same number of digits:
Put the digits of X in a counting hash.
For each digit of Y going in descending order (left-to-right), take the max digit of X that isn't greater than it and use that in your permutation.
If you ever place a digit lower than its counterpart in Y, place all remaining digits in descending order.
If there ever isn't a non-greater digit available then do the following: repeatedly unwind your prior move until you get to a digit where a lower digit was available. Select the max such lower digit. Then, all remaining digits can be placed in descending order from the map. If there is no such digit (where a lower digit could have been chosen) then there is no solution.
If you get through all the digits then you've produced the max solution.
This is linear in the number of digits if this is limited to base 10. If your base can vary, this is O(num_digits * base)
Here's Ruby code for this.
def get_perm(x, y)
# hist keeps a count of each of the digits of x
hist = Hash.new 0; x.digits.each { |d| hist[d] += 1 }
# output_digits is the answer we're building
output_digits = []
y_digits = y.digits
x_digits = x.digits
# If x has fewer digits then all permutations are good so pick the largest
if x.digits.length < y.digits.length
9.downto(0) do |digit|
output_digits += [digit] * hist[digit]
end
return output_digits
end
# If y has fewer digits then no permutation is good, return -1
if y.digits.length < x.digits.length
return -1
end
# parse the digits of y
(y_digits.length - 1).downto(0) do |i|
cur_y_digit = y_digits[i]
# use the current digit of y if possible
if hist[cur_y_digit] > 0
hist[cur_y_digit] -= 1
output_digits.append(cur_y_digit)
return output_digits if i == 0
# otherwise, use the largest smaller digit available if possible
else
(cur_y_digit - 1).downto(0) do |smaller_digit|
if hist[smaller_digit] > 0
# place the smaller digit, then all remaining digits in descending order
hist[smaller_digit] -= 1
output_digits.append(smaller_digit)
9.downto(0) do |digit|
output_digits += [digit] * hist[digit]
end
return output_digits
end
end
# If we make it here then no digit was available; we need to unwind moves until we
# can replace a digit of our solution with a smaller digit
smallest_digit = hist.keys.min
while i < (y.digits.length - 1) do
i += 1
cur_y_digit = y_digits[i]
cur_unwound_digit = output_digits.pop
hist[cur_unwound_digit] += 1
smallest_digit = [smallest_digit, cur_unwound_digit].min
if cur_y_digit > smallest_digit
(cur_y_digit - 1).downto(smallest_digit) do |d|
if hist[d] >= 1
output_digits.append(d)
hist[d] -= 1
9.downto(0) do |digit|
output_digits += [digit] * hist[digit]
end
return output_digits
end
end
end
end
return -1
end
end
end
Outputs for OP sample cases:
> get_perm(123, 321)
=> [3, 2, 1]
> get_perm(1733, 3311)
=> [3, 1, 7, 3]
> get_perm(999, 111)
=> -1
If Z is the answer, and the numbers have n digits, you can show that there is an index i such that Z[:i] = Y[:i], Z[i]<Y[i], and Z[i+1:] is as large as possible given digits of X \ Z[:i+1] (I use python array slice notation, and the last expression means "the set of digits of X minus those already chosen in Z up to i+1").
Given this, you can easily loop over each candidate i, and efficiently check if it's feasible to chose such i as in above. The solution is with the largest possible i.
The solution should be O(n*log(n)).
I'll leave the proof and implementation details, as I understand it's a homework :)

Making this ruby algorithm faster

Given a positive integer n I wish to find the largest integer m comprised of the digits contained in n that is less than n.
The code is to return m unless one of the following results obtain, in which case -1 it should be returned.
there is no possible variation;
if the number of digits isn't equal to the input;
if the first digit of the output == 0;
My code works, but it takes too long when "n" is a huge number! I believe it's because of the method #Permutation but I'm not sure. Can anyone shed a light on this?
Here's my code
def next_smaller (n)
new = n.to_s.split("").permutation.to_a.map { |n| n.join.to_i }
res = new.sort.reverse.select { |x| x < n }.first
res_arr = res.to_s.split("")
res.nil? || res_arr.count != n.to_s.split("").count || res_arr[0] == 0 ? -1 : res
end
Thank you
UPD: The code below works incorrectly with some input.
It is better to skip the generation of all permutations. Array#permutation can take a block of code:
def fast_next_smaller(number)
number.digits.reverse.permutation do |array|
next if array.first == 0
target_number = array.join.to_i
next if target_number == number
return target_number if target_number < number
end
-1
end
fast_next_smaller(907) #=> 790
fast_next_smaller(513) #=> 153
fast_next_smaller(153) #=> 135
fast_next_smaller(135) #=> -1
Here is the benchmark:
require 'benchmark'
n = 1000
Benchmark.bm do |x|
x.report('next_smaller') { n.times { next_smaller(rand(1_000_000..9_000_000)) } }
x.report('fast_next_smaller') { n.times { fast_next_smaller(rand(1_000_000..9_000_000)) } }
end
user system total real
next_smaller 4.433144 0.000000 4.433144 ( 4.433113)
fast_next_smaller 0.041333 0.000003 0.041336 ( 0.041313)
# With a very big number
puts Benchmark.measure { fast_next_smaller(5312495046546651005896) }
0.000000 0.000184 0.000184 ( 0.000176)
This should generally be pretty quick.
Code
def largest(n)
arr = n.to_s.chars.map(&:to_i)
nbr_chars = arr.size
case nbr_chars
when 1
-1
when 2
m = arr.reverse.join.to_i
m < 10 || m >= n ? -1 : m
else
(2..nbr_chars).each do |m|
fix_digits = arr[0,nbr_chars-m]
var_digits = arr[-m..-1]
if var_digits == var_digits.sort
return -1 if m == nbr_chars
else
a = solve_for_last_m_digits(var_digits)
if a.nil?
next if m < nbr_chars
return -1
else
x = (fix_digits + a).join.to_i
return x >= 10**(nbr_chars-1) ? x : -1
end
end
end
-1
end
end
def solve_for_last_m_digits(a)
nbr_chars = a.size
a_as_int = a.join.to_i
x = a.permutation(nbr_chars).max_by do |b|
m = b.join.to_i
m < a_as_int ? m : 0
end
x.join.to_i < a_as_int ? x : nil
end
Examples
largest 907 #=> 790
largest 531 #=> 513
largest 2638 #=> 2386
largest 78436 #=> 78364
largest 1783435893 #=> 1783435839
largest 385395038954829678 #=> 385395038954828976
largest 135 #=> -1
largest 106 #=> -1
All of the calculations were effectively instantaneous.
Explanation
See Array#permutation and Enumerable#max_by.
It's easiest to explain the algorithm with an example. Suppose the given integer were:
n = 385395038954829678
Had the last two digits been 87, rather than 78, we could simply reverse them and we'd be finished. As it is 78, however, we conclude that there is no integer less n that can be obtained by permuting the last two digits of n.
Next we consider the last three digits, 678. After examine the six permutations of these 3 digits we find that none are smaller than 678, so we conclude that there is no integer less n that can be obtained by permuting the last three digits.
Actually I don't examine the 6 permutations of the digits of 678. Rather I infer that the digits of that number cannot be permuted to produce a number smaller than 678 because they are non-decreasing (6 <= 7 <= 8). That is the purpose of the fragment
if var_digits == var_digits.sort
return -1 if m == nbr_chars
If the digits of the entire string are non-decreasing (m == nbr_chars is true), we return -1; else m is incremented by one.
We therefore move on to examining the last 4 digits of the number, 9678. As the digits comprising 9678 are not non-decreasing we know that they can be permuted to produce a number smaller than 9678 (simply swap two consecutive digits that are decreasing). After examining the 24 permutations of those four digits we find the largest number less than 9678 is 8976. Clearly, there is no permutation of digits that would produce a number less than n but larger than n with the last 4 digits replaced by 8976. The integer of interest is therefore obtained by replacing the last four digits of 385395038954829678 with 8976, which is 385395038954828976.
As soon as the last n-digits of m are not non-decreasing we know they can be rearranged to produce one more more numbers smaller than m, the largest of which will be the replacement for the last n digits of m.
The last step is to execute:
return x >= 10**(nbr_chars-1) ? x : -1
Suppose the number were 106. The largest number less than 106 that can be obtained by permuting its digits is x = 61 (061). As 61 has one or more (here one) leading zeroes, we return -1. We know there is at least one leading zéro because nbr_chars #=> 3, 10**(nbr_chars -1) #=> 100and61 < 100`.

Can someone explain the mathematics behind this solution

A question asks:
Take a sequence of numbers from 1 to n (where n > 0).
Within that sequence, there are two numbers, a and b.
The product of a and b should equal the sum of all numbers in the sequence excluding a and b.
Given a number n, could you tell me the numbers excluded from the sequence?
My plan was to get the sum of the range, then create an array using the combination enumerator to get all of the possible pairs of the range, then check if the product of the pair equals the sum of the range minus the sum of the pair. This solution worked, but took way too long:
def removNb(n)
arr = [*1..n]
sum = arr.inject(:+)
ab = []
[*(n/2)..n].combination(2).to_a.each do |pair|
if pair.inject(:*) == sum - pair.inject(:+)
ab << pair
ab << [pair[1],pair[0]]
end
end
ab
end
Here is a solution that I found:
def removNb(n)
res = []
total = (n*n + n) / 2
range = (1..n)
(1..n).each do |a|
b = ((total - a) / (a * 1.0 + 1.0))
if b == b.to_i && b <= n
res.push([a,b.to_i])
end
end
return res
end
but can't understand how it works. I understand the equation behind the total.
You could form a equation
a * b = (sum of sequence from 1 to n) - (a + b)
from this statement
the product of a and b should be equal to the sum of all numbers in
the sequence, excluding a and b
sum of sequence from 1 to n (denote as total) = n(n+1)/2 = (n*n + n) / 2
Reorder above equation, you get
b = (total - a) / (a + 1)
The remaining work is to test if there exist integer a and b matching this equation
The code returns an array of all pairs of numbers in the sequence that have the desired property. Let's step through it.
Initialize the array to be returned.
res = []
Compute the sum of the elements in the sequence. The sum of the elements of any arithmetic sequence equals the first element plus the last element, multiplied by the number of elements in the sequence, the product divided by 2. Here that is total = n*(1+n)/2, which can be expressed as
total = (n*n + n) / 2
range = (1..n) is unnecessary as range is not subsequently referenced.
Loop through the elements of the sequence
(1..n).each do |a|
For each value of a we seek another element of the sequence b such that
a*b = total - a - b
Solving for b:
b = (total - a)/ (a * 1.0 + 1.0)
If b is in the range, save the pair [a, b]
if b == b.to_i && b <= n
res.push([a,b.to_i])
end
Return the array res
res
This method contains two errors:
If [a,b] is added to res, it will be added twice
[a,a] could be added to res (such as n=5, a=b=3)
I would write this as follows.
def remove_numbers(n)
total = n*(n+1)/2
(1..n-1).each_with_object([]) do |a,res|
next unless (total-a) % (a+1) == 0
b = (total-a)/(a+1)
res << [a,b] if (a+1..n).cover?(b)
end
end
For example,
remove_numbers 10
#=> [[6, 7]]
remove_numbers 1000
#=> []
Out of cursiosity:
(2..10_000).map { |x| [x, remove_numbers(x).size] }.max_by(&:last)
#=> [3482, 4]
remove_numbers 3482
#=> [[1770, 3423], [2023, 2995], [2353, 2575], [2460, 2463]]

Sort Integer Array Ruby

Have the function PermutationStep (num) take the num parameter being passed and return the next number greater than num using the same digits. For example: if num is 123 return 132, if it's 12453 return 12534. If a number has no greater permutations, return -1 (ie. 999)
Here's my code. I'd like to sort an array of large integers in numerical order. Using the regular sort method doesn't give the right order for some numbers. Is there a sort_by structure that I can replace 'sort' with in my code below?
def PermutationStep(num)
num = num.to_s.split('').map {|i| i.to_i}
permutations = num.permutation.to_a.sort #<= I want to sort by numerical value here
permutations.each_with_index do |n, idx|
if n == num
if n == permutations[-1]
return -1
else
return permutations[idx+1].join.to_i
end
end
end
end
For example, 11121. When I run the code it gives me 11121.I want the next highest permutation, which should be 12111.
Also, when I try { |a,b| b <=> a }, I also get errors.
You can pass a block to sort.
num.permutation.to_a.sort { |x, y| x.to_i <=> y.to_i }
This SO thread may be of some assistance: How does Array#sort work when a block is passed?
num.permutation.to_a is an array of arrays, not an array of integers, which causes the result not what you expected.
Actually you don't need to sort since you only need the minimum integer that is bigger than the input.
def PermutationStep(num)
nums = num.to_s.split('')
permutations = nums.permutation.map{|a| a.join.to_i}
permutations.keep_if{|n| n > num}.min || -1
end
puts PermutationStep(11121) # 11211
puts PermutationStep(999) # -1
Call to_i before your sort the permutations. Once that is done, sort the array an pick the first element greater than your number:
def PermutationStep(num)
numbers = num.to_s.split('')
permutations = numbers.permutation.map { |p| p.join.to_i }.sort
permutations.detect { |p| p > num } || -1
end
You don't need to consider permutations of digits to obtain the next higher number.
Consider the number 126531.
Going from right to left, we look for the first decrease in the digits. That would be 2 < 6. Clearly we cannot obtain a higher number by permuting only the digits after the 2, but we can obtain a higher number merely by swapping 2 and 6. This will not be the next higher number, however.
We therefore look for the smallest digit to the right of 2 that is greater than 2, which would be 3. Clearly, the next higher number will begin 13 and will have the remaining digits ordered smallest to largest. Therefore, the next higher number will be 131256.
You can easily see that the next higher number for 123 is 132, and for 12453 is 12534.
The proof that procedure is correct is easily established by induction, first showing that it is correct for numbers with two digits, then assuming it is correct for numbers with n>=2 digits, showing it is correct for numbers with n+1 digits.
It can be easily implemented in code:
def next_highest(n)
a = n.to_s.reverse.split('').map(&:to_i)
last = -Float::INFINITY
x,ndx = a.each_with_index.find { |d,i| res = d<last; last=d; res }
return nil unless x
swap_val = a[ndx]
swap_ndx = (0...ndx).select { |i| a[i] > swap_val }.min_by{ |i| a[i] }
a[ndx], a[swap_ndx] = a[swap_ndx], swap_val
a[0...ndx] = a[0...ndx].sort.reverse
a.join.reverse
end
next_highest(126531) #=> "131256"
next_highest(109876543210) #=> "110023456789"

Simple recursion problem

I'm taking my first steps into recursion and dynamic programming and have a question about forming subproblems to model the recursion.
Problem:
How many different ways are there to
flip a fair coin 5 times and not have
three or more heads in a row?
If some could put up some heavily commented code (Ruby preferred but not essential) to help me get there. I am not a student if that matters, this is a modification of a Project Euler problem to make it very simple for me to grasp. I just need to get the hang of writing recursion formulas.
If you would like to abstract the problem into how many different ways are there to flip a fair coin Y times and not have Z or more heads in a row, that may be beneficial as well. Thanks again, this website rocks.
You can simply create a formula for that:
The number of ways to flip a coin 5 times without having 3 heads in a row is equal to the number of combinations of 5 coin flips minus the combinations with at least three heads in a row. In this case:
HHH-- (4 combinations)
THHH- (2 combinations)
TTHHH (1 combination)
The total number of combinations = 2^5 = 32. And 32 - 7 = 25.
If we flip a coin N times without Q heads in a row, the total amount is 2^N and the amount with at least Q heads is 2^(N-Q+1)-1. So the general answer is:
Flip(N,Q) = 2^N - 2^(N-Q+1) +1
Of course you can use recursion to simulate the total amount:
flipme: N x N -> N
flipme(flipsleft, maxhead) = flip(flipsleft, maxhead, 0)
flip: N x N x N -> N
flip(flipsleft, maxhead, headcount) ==
if flipsleft <= 0 then 0
else if maxhead<=headcount then 0
else
flip(flipsleft - 1, maxhead, headcount+1) + // head
flip(flipsleft - 1, maxhead, maxhead) // tail
Here's my solution in Ruby
def combination(length=5)
return [[]] if length == 0
combination(length-1).collect {|c| [:h] + c if c[0..1]!= [:h,:h]}.compact +
combination(length-1).collect {|c| [:t] + c }
end
puts "There are #{combination.length} ways"
All recursive methods start with an early out for the end case.
return [[]] if length == 0
This returns an array of combinations, where the only combination of zero length is []
The next bit (simplified) is...
combination(length-1).collect {|c| [:h] + c } +
combination(length-1).collect {|c| [:t] + c }
So.. this says.. I want all combinations that are one shorter than the desired length with a :head added to each of them... plus all the combinations that are one shorter with a tail added to them.
The way to think about recursion is.. "assuming I had a method to do the n-1 case.. what would I have to add to make it cover the n case". To me it feels like proof by induction.
This code would generate all combinations of heads and tails up to the given length.
We don't want ones that have :h :h :h. That can only happen where we have :h :h and we are adding a :h. So... I put an if c[0..1] != [:h,:h] on the adding of the :h so it will return nil instead of an array when it was about to make an invalid combination.
I then had to compact the result to ignore all results that are just nil
Isn't this a matter of taking all possible 5 bit sequences and removing the cases where there are three sequential 1 bits (assuming 1 = heads, 0 = tails)?
Here's one way to do it in Python:
#This will hold all possible combinations of flipping the coins.
flips = [[]]
for i in range(5):
#Loop through the existing permutations, and add either 'h' or 't'
#to the end.
for j in range(len(flips)):
f = flips[j]
tails = list(f)
tails.append('t')
flips.append(tails)
f.append('h')
#Now count how many of the permutations match our criteria.
fewEnoughHeadsCount = 0
for flip in flips:
hCount = 0
hasTooManyHeads = False
for c in flip:
if c == 'h': hCount += 1
else: hCount = 0
if hCount >= 3: hasTooManyHeads = True
if not hasTooManyHeads: fewEnoughHeadsCount += 1
print 'There are %s ways.' % fewEnoughHeadsCount
This breaks down to:
How many ways are there to flip a fair coin four times when the first flip was heads + when the first flip was tails:
So in python:
HEADS = "1"
TAILS = "0"
def threeOrMoreHeadsInARow(bits):
return "111" in bits
def flip(n = 5, flips = ""):
if threeOrMoreHeadsInARow(flips):
return 0
if n == 0:
return 1
return flip(n - 1, flips + HEADS) + flip(n - 1, flips + TAILS)
Here's a recursive combination function using Ruby yield statements:
def combinations(values, n)
if n.zero?
yield []
else
combinations(values, n - 1) do |combo_tail|
values.each do |value|
yield [value] + combo_tail
end
end
end
end
And you could use regular expressions to parse out three heads in a row:
def three_heads_in_a_row(s)
([/hhh../, /.hhh./, /..hhh/].collect {|pat| pat.match(s)}).any?
end
Finally, you would get the answer using something like this:
total_count = 0
filter_count = 0
combinations(["h", "t"], 5) do |combo|
count += 1
unless three_heads_in_a_row(combo.join)
filter_count += 1
end
end
puts "TOTAL: #{ total_count }"
puts "FILTERED: #{ filter_count }"
So that's how I would do it :)

Resources