Can someone explain the mathematics behind this solution - ruby

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]]

Related

How to Find the List with the Highest Sum of Factorial of its Numbers While Returning Results to Mod M

Given different lists of numbers, say;
list1 --> [2,4,2]
list2 --> [3,5]
list3 --> [4,4,4,4,4,4],
list4 --> [5,5]
list5 --> [6]
Find the sum of the factorial of the numbers in each list.
Return the maximum of the sums. Take the sum to Mod M since they can be very large.
For example, in the above lists, the sums for each list, taken M=10^9 + 7, are
list1 --> (2!+4!+2!)%M = (2+24+2)%M = 26
list2 --> (3!+5!)%M = (6+120)%M = 126
list3 --> (4!+4!+4!+4!+4!+4!)%M = (24+24+24+24+24+24)%M = 144
list4 --> (5!+5!)%M = (120+120)%M = 240
list5 --> (6!)%M = 720
As can be seen from the above calculation, list5 is the required list.
My problem is that when items in each list become large, factorial becomes too large pretty fast that the Mod starts taking effects. After Mod takes effect, I would not be able to find the max number since, for example, 1000000008 % M = 1, but 10 % M = 10.
How do I efficiently find the list that actually gets the max sum?
Constraints
--> Size of each list is between 1(inclusive) and N(exclusive)
--> Each list contain numbers between 2(inclusive) and N(exclusive)
--> N is between 2(inclusive) and 10^6 (exclusive)
Find the max element, and among these the one with the max count, across all the arrays. Say this is p, repeated r times.
So this array's sum is at least r * p!.
Now, say some other array has a max element of q, and length m. So it's max possible factorial sum is m * q!. What does it take for this other array to possibly have a greater sum? I.e. for it to be worth checking?
If q = p then all we need is m > r for this to be worth checking.
If q = p-1 then we need m * (p-1)! > r * p * (p-1)! so m > r * p.
If q = p-2 then we need m * (p-2)! > r * p * (p-1) * (p-2)! so m > r * p * (p-1)
etc... if q = p-k, then we need m > r * p! / (p-k)!
Whether this is helpful depends on the values in the arrays. E.g. if a single array has a max value of N repeated once, and the next highest value in any array is N-1 or lower then the array with N has to be (or be tied with) the max sum array (because it would take N (N-1)'s to equal N!).
This helps trim down the arrays to check. Unless the max element across all arrays is very small relative to N, this likely narrows you down to a very small set of (max value, max value repetition) values across the survivors. E.g. if p > sqrt(N) then survivors can have a max value no smaller than p-1.
E.g., if N is 100 and the largest value across all lists is 11. Say this is in a list of just one element: [11]. Well, 11! = 39,916,800. Since 100 * 9! is just 36,288,000, we can discard anything with a max element < 10.
We can keep using this idea recurively to find the single highest-value list. Say we're down to two max values: p and p-1. For all the lists which have a max value of p, change their count of ps to zero and for each p gotten rid of, give then p (p-1)s. Next, find the surviving list with the lowest count of (p-1s), and remove the count from all surviving lists. Then repeat the process.
For very small values relative to N we'll be getting more than 2 possible max values. You could either extend this to 3+, or probably just calculate the factorial sums of the survivors directly since all values will be small.
---- Here's an O(num_lists * N) version ----
Say there are L lists.
1. Sort each list in O(N) using bucket sort, total is O(L * N).
2. Find the max element across lists, use repetition to break ties.
3. Say this is p, repeated r times.
4. For each list (including this one): call remove(p, r, list)
5. Repeat for the new max element.
def remove(p, r, list):
while r > 0 and list.has(p):
list.remove(p)
r -= 1
if r > 0:
list.remove((p-1) * r * p)
The remove method can stop early if a list gets to a situation where no solution is possible (as in the top section of this answer).
This is O(L * N) in total. Each time we call remove, we either remove one of at most N elements, or reduce what we're removing by 1 of at most N elements, so it can be called at most O(2N) times per list before exhausting the list.
---- Ruby code ----
def find_max_fsum(lists, n)
sorted_lists = Hash.new { |h, k| h[k] = [0] * (n+1) }
lists.each_with_index do |list, i|
list.each do |elt|
sorted_lists[i][elt] += 1
end
end
max_val = n + 1
while max_val > 1 do
max_val -= 1
max_val_reps = 0
sorted_lists.values.each do |sorted_list|
max_val_reps = [max_val_reps, sorted_list[max_val]].max
end
if max_val_reps > 0
sorted_lists.each do |i, sorted_list|
success = remove(max_val, max_val_reps, sorted_list)
if !success
sorted_lists.delete(i)
end
end
end
end
sorted_lists.keys.each do |i|
print "max_fsum list: #{lists[i].to_s}\n"
end
end
def remove(max_val, max_val_reps, sorted_list)
if max_val_reps <= sorted_list[max_val]
sorted_list[max_val] -= max_val_reps
return true
end
if (max_val_reps > sorted_list[max_val]) && max_val > 1
max_val_reps -= sorted_list[max_val]
sorted_list[max_val] = 0
return remove(max_val - 1, max_val_reps * max_val, sorted_list)
end
return false
end
find_max_fsum([[2,4,2], [3,5], [6,3,3,3,3,3], [4,4,4,4,4,4], [5,5], [6, 4]], 6)
max_fsum list: [6, 3, 3, 3, 3, 3]
find_max_fsum([[2,4,2], [3,5], [6,3,3,3], [4,4,4,4,4,4], [5,5], [6, 4]], 6)
max_fsum list: [6, 4]
Every positive integer has a unique "base factorial" representation. That representation looks like: a_1 * 1! + a_2 + 2! + a_3 * 3! + ... with every a_n <= n. This is like a base 10 factorial, but the base increases as you move down the list.
And if you have a factorial representation that isn't in canonical form, you can fix that by starting with 1, 2, 3 and so on, for each one taking a_n mod (n+1) as the remainder, and adjusting the next term up. This is no different than taking something like 25 * 1 + 3 * 10 + 40 * 100 and figuring out that it is 5 * 1 + 5 * 10 + 4 * 1000 so in base 10 it is 4055.
From your lists you can easily produce a factorial representation, then canonicalize it. The one with the biggest canonical representation is your answer.
Here is your example to show how it works:
list1 --> [2,4,2]
2 * 2! + 1 * 4!
list2 --> [3,5]
1 * 3! + 1 * 5!
list3 --> [4,4,4,4,4,4]
6 * 4! = 1 * 4! + 1 * 5! (This is the first one we had to canonicalize.)
list4 --> [5,5]
2 * 5!
list5 --> [6]
6!
Now you just sort the canonical representations and find that list5 is largest, then list4, then list3, then list2 then list1.

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`.

special pythagorean triplets

def pythagorean(n):
aAndB = []
for a in range(150, n-1):
for b in range(150, n):
for c in range(150,n+1):
if (c * c) == a *a + b*b and a + b + c == 1000:
aAndB.append(a)
return aAndB
print(pythagorean(500))
So I made this function to find pythagorean triplets that meets criteria a+b+c=1000. When I run this, I get [200,375]. Question is why do I receive two numbers in my list aAndB when I specifically asked to append an item for a?
If I try it with aAndB.append(c), the result shows [425, 425]. How do I fix it only to show exactly one element in the list?
Thank you for your help!
That's because there are 2 values, that satisfy your condition:
if (c * c) == a *a + b*b and a + b + c == 1000:
You can debug the code or just add more information in array, like that:
def pythagorean(n):
aAndB = []
for a in range(150, n-1):
for b in range(150, n):
for c in range(150,n+1):
if (c * c) == a * a + b * b and a + b + c == 1000:
aAndB.append({'a': a, 'b': b, 'c': c})
return aAndB
result = pythagorean(500)
for v in result:
print(v)
So if you want just one element - choose which one from 'result' array.
For example, if you want only first:
first_element = None
if len(result) > 0:
first_element = result[0]
print('First element:', first_element)
You can use euclid’s proof of pythagoreas triplets. You can choose any arbitrary numbers greater than zero say m,n. According to euclid the triplet will be a(m∗m−n∗n),b(2∗m∗n),c(m∗m+n∗n). Now apply this formula to find out the triplets , say our one value of triplet is 6 then, other two.
a(m∗m−n∗n),b(2∗m∗n),c(m∗m+n∗n)
It is sure that b(2∗m∗n) is obviously even. So now
(2∗m∗n)=6 =>(m∗n)=3 =>m∗n=3∗1 =>m=3,n=1
You can take any other value rather than 3 and 1 but those two values should hold the product of two numbers is 3 (m∗n=3).
Now , when m equals 3 and n equals 1 then
a(m∗m−n∗n)=(3∗3−1∗1)=8 , c(m∗m−n∗n)=(3∗3+1∗1)=10
6,8,10 is our triplet for value this our visualization of how generating triplets .
If given number is odd like (9) then slightly modified here, because b(2∗m∗n) never be odd. So, here we have to take
a(m∗m−n∗n)=7 , (m+n)∗(m−n)=7∗1 , So,(m+n)=7,(m−n)=1
Now find m and n from here then find other two values.
Do code according this , it will generate distinct triplets and efficiently

Extracting values without looping in 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

Algorithm to print all valid combations of n pairs of parenthesis

I'm working on the problem stated in the question statement. I know my solution is correct (ran the program) but I'm curious as to whether or not I'm analyzing my code (below) correctly.
def parens(num)
return ["()"] if num == 1
paren_arr = []
parens(num-1).each do |paren|
paren_arr << paren + "()" unless "()#{paren}" == "#{paren}()"
paren_arr << "()#{paren}"
paren_arr << "(#{paren})"
end
paren_arr
end
parens(3), as an example, will output the following:
["()()()", "(()())", "(())()", "()(())", "((()))"]
Here's my analysis:
Every f(n) value is roughly 3 times as many elements as f(n-1). So:
f(n) = 3 * f(n-1) = 3 * 3 * f(n-2) ~ (3^n) time cost.
By a similar analysis, the stack will be occupied by f(1)..f(n) and so the space complexity should be 3^n.
I'm not sure if this analysis for either time or space is correct. On the one hand, the stack only holds n function calls, but each of these calls returns an array ~3 times as big as the call before it. Does this factor into space cost? And is my time analysis correct?
As others have mentioned, your solution is not correct.
My favourite solution to this problem generates all the valid combinations by repeatedly incrementing the current string to the lexically next valid combination.
"Lexically next" breaks down into a few rules that make it pretty easy:
The first difference in the string changes a '(' to a ')'. Otherwise the next string would be lexically before the current one.
The first difference is as far to the right as possible. Otherwise there would be smaller increments.
The part after the first difference is lexically minimal, again because otherwise there would be smaller increments. In this case that means that all the '('s come before all the ')'.
So all you have to do is find the rightmost '(' that can be changed to a ')', flip it, and then append the correct number of '('s and ')'s.
I don't know Ruby, but in Python it looks like this:
current="(((())))"
while True:
print(current)
opens=0
closes=0
pos=0
for i in range(len(current)-1,-1,-1):
if current[i]==')':
closes+=1
else:
opens+=1
if closes > opens:
pos=i
break
if pos<1:
break
current = current[:pos]+ ")" + "("*opens + ")"*(closes-1)
Output:
(((())))
((()()))
((())())
((()))()
(()(()))
(()()())
(()())()
(())(())
(())()()
()((()))
()(()())
()(())()
()()(())
()()()()
Solutions like this turn out to be easy and fast for many types of "generate all the combinations" problems.
Recursive reasoning makes a simple solution. If the number of left parens remaining to emit is positive, emit one and recur. If the number of right parens remaining to emit is greater than the number of left, emit and recur. The base case is when all parens, both left and right, have been emitted. Print.
def parens(l, r = l, s = "")
if l > 0 then parens(l - 1, r, s + "(") end
if r > l then parens(l, r - 1, s + ")") end
if l + r == 0 then print "#{s}\n" end
end
As others have said, the Catalan numbers give the number of strings that will be printed.
While this Ruby implementation doesn't achieve it, a lower level language (like C) would make it easy to use a single string buffer: O(n) space. Due to substring copies, this one is O(n^2). But since the run time and output length are O(n!), O(n) space inefficiency doesn't mean much.
I found Tom Davis' article, "Catalan Numbers," very helpful in explaining one recursive method for defining the Catalan Numbers. I'll try to explain it myself (in part, to see how much of it I've understood) as it may be applied to finding the set of all unique arrangements of N matched parentheses (e.g., 1 (); 2 ()(), (()); etc. ).
For N > 1 let (A)B represent one arrangement of N matched parentheses, where A and B each have only balanced sets of parentheses. Then we know that if A contains k matched sets, B must have the other N - k - 1, where 0 <= k <= N - 1.
In the following example, a dot means the group has zero sets of parentheses:
C_0 => .
C_1 => (.)
To enumerate C_2, we arrange C_1 as AB in all ways and place the second parentheses around A:
. () = AB = C_0C_1 => (.)()
() . = AB = C_1C_0 => (()) .
Now for C_3, we have three partitions for N - 1, each with its own combinations: C_0C_2, C_1C_1, C_2C_0
C_0C_2 = AB = . ()() and . (()) => ()()(), ()(())
C_1C_1 = AB = ()() => (())()
C_2C_0 = AB = ()() . and (()) . => (()()), ((()))
We can code this method by keeping a set for each N and iterating over the combinations for each partition. We'll keep the individual arrangements as bits: 0 for left and 1 for right (this appears backwards when cast as a binary string).
def catalan
Enumerator.new do |y|
# the zero here represents none rather than left
s = [[0],[2]]
y << [0]
y << [2]
i = 2
while true
s[i] = []
(0..i - 1).each do |k|
as = s[k]
bs = s[i - k - 1]
as.each do |a|
bs.each do |b|
if a != 0
s[i] << ((b << (2*k + 2)) | (1 << (2*k + 1)) | (a << 1))
else
s[i] << (2 | (b << 2))
end
end # bs
end # as
end # k
y.yield(s[i])
i = i + 1
end # i
end # enumerator
end
catalan.take(4)
# => [[0], [2], [10, 12], [42, 50, 44, 52, 56]]
The yielder is lazy: although the list is infinite, we can generate as little as we like (using .take for example):
catalan.take(4).last.map{|x| x.to_s(2)}
# => ["101010", "110010", "101100", "110100", "111000"]
The former generation obliges us to keep all previous sets in order to issue the next. Alternatively, we can build a requested set through a more organic type, meandering recursion. This next version yields each arrangement to the block, so we can type:
catalan(4){
|x| (0..7).reduce(""){
|y,i| if x[i] == 0 then y + "(" else y + ")" end
}
}.take(14)
# => ["(((())))", "((()()))", "((())())", "((()))()", "(()(()))", "(()()())",
# "(()())()", "(())(())", "(())()()", "()((()))", "()(()())", "()(())()",
# "()()(())", "()()()()"]
Direct generation:
def catalan(n)
Enumerator.new do |y|
s = [[0,0,0]]
until s.empty?
left,right,result = s.pop
if left + right == 2 * n
y << yield(result)
end
if right < left
s << [left, right + 1, result | 1 << (left + right)]
end
if left < n
s << [left + 1, right, result]
end
end
end
end

Resources