Prime Digit Sums - ruby

So I'm doing one of those programming challenges on HackerRank to help build my skills. (No this is NOT for an interview! The problem I am on is the Prime Digit Sum. (Full description: https://www.hackerrank.com/challenges/prime-digit-sums/problem) Basically given a value n, I am to find all numbers that are n digits long that meet the following three criteria:
Every 3 consecutive digits sums to a prime number
Every 4 consecutive digits sums to a prime number
Every 5 consecutive digits sums to a prime number
See the link for a detailed breakdown...
I've got a basic function that works, problem is that when n gets big enough it breaks:
#!/bin/ruby
require 'prime'
def isChloePrime?(num)
num = num.to_s
num.chars.each_cons(5) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
num.chars.each_cons(4) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
num.chars.each_cons(3) do |set|
return false unless Prime.prime?(set.inject(0) {|sum, i| sum + i.to_i})
end
return true
end
def primeDigitSums(n)
total = 0
(10**(n-1)..(10**n-1)).each do |i|
total += 1 if isChloePrime?(i)
end
return total
end
puts primeDigitSums(6) # prints 95 as expected
puts primeDigitSums(177779) # runtime error
If anyone could point me in the right direction that would be awesome. Not necessarily looking for a "here's the answer". Ideally would love a "try looking into using this function...".
UPDATE here is version 2:
#!/bin/ruby
require 'prime'
#primes = {}
def isChloePrime?(num)
num = num.to_s
(0..num.length-5).each do |i|
return false unless #primes[num[i,5]]
end
return true
end
def primeDigitSums(n)
total = 0
(10**(n-1)...(10**n)).each do |i|
total += 1 if isChloePrime?(i)
end
return total
end
(0..99999).each do |val|
#primes[val.to_s.rjust(5, "0")] = true if [3,4,5].all? { |n| val.digits.each_cons(n).all? { |set| Prime.prime? set.sum } }
end

I regard every non-negative integer to be valid if the sum of every sequence of 3, 4 and 5 of its digits form a prime number.
Construct set of relevant prime numbers
We will need to determine if the sums of digits of 3-, 4- and 5-digit numbers are prime. The largest number will therefore be no larger than 5 * 9. It is convenient to construct a set of those primes (a set rather than an array to speed lookups).
require 'prime'
require 'set'
primes = Prime.each(5*9).to_set
#=> #<Set: {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43}>
Construct transition hash
valid1 is a hash whose keys are all 1-digit numbers (all of which are valid). The value of the key 0 is an array of all 1-digit numbers. For 1-9 the values are arrays of 2-digit numbers (all of which are valid) that are obtained by appending a digit to the key. Collectively, the values include all 2-digit numbers.
valid1 = (0..9).each_with_object({}) { |v1,h|
h[v1] = 10.times.map { |i| 10 * v1 + i } }
valid2 is a hash that maps 2-digit numbers (all valid) to arrays of valid 3-digit numbers that are obtained by appending a digit to the 2-digit number. Collectively, the values include all valid 3-digit numbers. All values are non-empty arrays.
valid2 = (10..99).each_with_object({}) do |v2,h|
p = 10 * v2
b, a = v2.digits
h[v2] = (0..9).each_with_object([]) { |c,arr|
arr << (p+c) if primes.include?(a+b+c) }
end
Note that Integer#digits returns an array with the 1's digit first.
valid3 is a hash that maps valid 3-digit numbers to arrays of valid 4-digit numbers that are obtained by appending a digit to the key. Collectively, the values include all valid 4-digit numbers. 152 of the 303 values are empty arrays.
valid3 = valid2.values.flatten.each_with_object({}) do |v3,h|
p = 10 * v3
c, b, a = v3.digits
h[v3] = (0..9).each_with_object([]) do |d,arr|
t = b+c+d
arr << (p+d) if primes.include?(t) && primes.include?(t+a)
end
end
valid4 is a hash that maps valid 4-digit numbers to arrays of valid 4-digit numbers that are obtained by appending a digit to the key and dropping the first digit of key. valid5.values.flatten.size #=> 218 is the number of valid 5-digit numbers. 142 of the 280 values are empty arrays.
valid4 = valid3.values.flatten.each_with_object({}) do |v4,h|
p = 10 * v4
d, c, b, a = v4.digits
h[v4] = (0..9).each_with_object([]) do |e,arr|
t = c+d+e
arr << ((p+e) % 10_000) if primes.include?(t) &&
primes.include?(t += b) && primes.include?(t + a)
end
end
We merge these four hashes to form a single hash #transition. The former hashes are no longer needed. #transition has 294 keys.
#transition = [valid1, valid2, valid3, valid4].reduce(:merge)
#=> {0=>[0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
# 1=>[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
# ...
# 9=>[90, 91, 92, 93, 94, 95, 96, 97, 98, 99],
# 10=>[101, 102, 104, 106], 11=>[110, 111, 113, 115, 119],
# ...
# 97=>[971, 973, 977], 98=>[980, 982, 986], 99=>[991, 995],
# 101=>[1011], 102=>[1020], 104=>[], 106=>[], 110=>[1101],
# ...
# 902=>[9020], 904=>[], 908=>[], 911=>[9110], 913=>[], 917=>[],
# 1011=>[110], 1020=>[200], 1101=>[], 1110=>[], 1200=>[],
# ...
# 8968=>[], 9020=>[200], 9110=>[], 9200=>[]}
Transition method
This is the method that will be used to update counts each time n, the number of digits, is incremented by one.
def next_counts(counts)
counts.each_with_object({}) do |(k,v),new_valid|
#transition[k].each do |new_v|
(new_valid[new_v] = new_valid[new_v].to_i + v) if #transition.key?(k)
end
end
end
prime_digit_sum method
def prime_digit_sum(n)
case n
when 1 then 10
when 2 then 90
when 3 then #transition.sum { |k,v| (10..99).cover?(k) ? v.size : 0 }
else
counts = #transition.select { |k,_| (100..999).cover?(k) }.
values.flatten.product([1]).to_h
(n - 4).times { counts = next_counts(counts) }
counts.values.sum % (10**9 + 7)
end
end
Note that, for n = 4 the hash counts has keys that are valid 4-digit numbers and values that all equal 1:
counts = #transition.select { |k,_| (100..999).cover?(k) }.
values.flatten.product([1]).to_h
#=> {1011=>1, 1020=>1, 1101=>1, 1110=>1, 1200=>1, 2003=>1, 2005=>1,
# ...
# 8902=>1, 8920=>1, 8968=>1, 9020=>1, 9110=>1, 9200=>1}
counts.size
#=> 280
As shown, for n >= 5, counts is updated each time n is incremented by one. The sum of the values equals the number of valid n-digit numbers.
The number formed by the last four digits of every valid n-digit numbers is one of count's keys. The value of each key is an array of numbers that comprise the last four digits of all valid (n+1)-digit numbers that are produced by appending a digit to the key.
Consider, for example, the value of counts for n = 6, which is found to be the following.
counts
#=> {1101=>1, 2003=>4, 2005=>4, 300=>1, 302=>1, 304=>1, 308=>1, 320=>1,
# 322=>1, 326=>1, 328=>1, 380=>1, 382=>1, 386=>1, 388=>1, 500=>1,
# 502=>1, 506=>1, 508=>1, 560=>1, 562=>1, 566=>1, 568=>1, 1200=>7,
# 3002=>9, 3020=>4, 3200=>6, 5002=>6, 9200=>4, 200=>9, 1020=>3, 20=>3,
# 5200=>4, 201=>2, 203=>2, 205=>2, 209=>2, 5020=>2, 9020=>1}
Consider the key 2005 and note that
#transition[2005]
#=> [50, 56]
We see that there are 4 valid 6-digit numbers whose last four digits are 2005 and that, for each of those 4 numbers, a valid number is produced by adding the digits 0 and 6, resulting in numbers whose last 5-digits are 20050 and 20056. However, we need only keep the last four digits, 0050 and 0056, which are the numbers 50 and 56. Therefore, when recomputing counts for n = 7--call it counts7--we add 4 to both counts7[50] and counts7[56]. Other keys k of counts (for n=6) may be such that #transition[k] have values that include 50 and 56, so they too would contribute to counts7[50] and counts7[50].
Selective results
Let's try it for various values of n
puts "digits nbr valid* seconds"
[1, 2, 3, 4, 5, 6, 20, 50, 100, 1_000, 10_000, 40_000].each do |n|
print "%6d" % n
t = Time.now
print "%11d" % prime_digit_sum(n)
puts "%10f" % (Time.now-t).round(4)
end
puts "\n* modulo (10^9+7)"
digits nbr valid* seconds
1 10 0.000000
2 90 0.000000
3 303 0.000200
4 280 0.002200
5 218 0.000400
6 95 0.000400
20 18044 0.000800
50 215420656 0.001400
100 518502061 0.002700
1000 853799949 0.046100
10000 590948890 0.474200
40000 776929051 2.531600

I would approach the problem by pre-calculating a list of all the allowed 5-digit sub-sequences: '00002' fails while '28300' is allowed etc. This could perhaps be set up as a binary array or hash set.
Once you have the list, then you can check any number by moving a 5-digit frame over the number one step at a time.

Related

Find efficient algorithm for grouping timstamps by X time

I have a question I was asked and wanted to see if there was a better solution. so I am asking you the same question. I was unable to find anything online about it.
Given an unsorted array of tuples in the form (timstamp (seconds), resource_id) representing the access time of a given resource.
return the most accesses out of any of the resources in any 5 min block of time.
The return will be a tuple with resource id and single number for max accesses.
ex)
input:
[("400", "1"), ("405", "2"),("605", "4"), ("505", "3"),("604", "1"), ("1505", "3"), ("1205", "2")]
output: ("1", 2)
In the following I will use Ruby (in simplified form) to explain the algorithms proposed. With some explanation, the code should be understandable to readers who do not know the language. I find that clearer than describing it in "algorithm speak" and more helpful for implementation.
For the moment forget about resource_id's. I will deal with that at the end.
Construct O(nlog(n)) method max_interval_coverage
The method max_interval_coverage will take an array of timestamps as it's argument and return a hash giving the maximum number of timestamps that appear in a 5-minute interval and the index of the timestamp at which that interval begins.
Clearly, (as suggested by #kdsquare in a comment) a 5-minute (299 second) interval containing the greatest number of timestamps will begin at one of the timestamps.
If the timestamps are not sorted, sort them, which is O(nlog(n)). Suppose
timestamps = [100, 340, 460, 512, 733, 999, 1462, 1581, 1622, 1699, 1833]
Now create an array comprised of the elements of timestamps to which an additional timestamp is appended, one that is larger than the last timestamp in the sorted array by at least 300:
ts = timestamps << Float::INFINITY
#=> [100, 340, 460, 512, 733, 999, 1462, 1581, 1622, 1699, 1833, Infinity]
First I will construct a helper method.
def coverage(ts, start_idx)
idx_arr = (0..ts.size-1).to_a
n = ts[start_idx] + 299
idx_arr.bsearch { |j| ts[j] > n } - start_idx
end
For example,
coverage(ts, 6)
#=> 4
This tells us that the 5-minute minute interval beginning at timestamps[6] #=> 1462 (ending at 1462 + 299 #=> 1761) contains 4 timestamps: 1462, 1581, 1622 and 1699. (Notice that the interval 1581 to 1880 also contains 4 timestamps).
The calculation
idx_arr.bsearch { |j| ts[j] > n }
finds the smallest index j for which
ts[j] > ts[i] + 299
It is guaranteed to find such an index because of the "large-enough" value I appended to timespaces.
See Array#bsearch.
By subtracting start_idx from the index returned by bsearch we obtain the number of timespaces within the 5-minute interval ts[start_idx] to ts[start_idx] + 299.
As array idx_arr is computed each time the method is called, I will instead pass it as a third argument:
def coverage(ts, idx_arr, start_idx)
n = ts[start_idx] + 299
idx_arr.bsearch { |j| ts[j] > n } - start_idx
end
We can now define the method max_interval_coverage.
def max_interval_coverage(timestamps)
ts = timestamps + [Float::INFINITY]
idx_arr = (0..ts.size-1).to_a
start_idx = (0..timestamps.size-1).max_by { |i| coverage(ts, idx_arr, i) }
{ start_idx: start_idx, coverage: coverage(ts, idx_arr, start_idx) }
end
max_interval_coverage(timestamps)
#=> {:start_idx=>6, :coverage=>4}
The line:
start_idx = (0..timestamps.size-1).max_by { |i| coverage(ts, idx_arr, i) }
#=> (0..10).max_by { |i| coverage(ts, idx_arr, i) }
computes the index i of the element of timestamps (and of ts) for which
coverage(ts, idx_arr, i)
is maximum. See Enumerable#max_by.
0..10 is a range of integers from 0 to 10, inclusive, that correspond to indices of the elements of timestamps. The expression computing start_idx reads, "find the element i of the range 0..10 (the index of timestamps) for which coverage(ts, idx_arr, i) is maximized".
coverage(ts, idx_arr, start_idx)
is called again in order to obtain the value of :coverage in the hash that is returned.
bsearch_index is O(log(n)), so executing it for each element of timespaces is O(nlog(n)), the same as sorting timespaces, so it is also the computational complexity of the algorithm.
To better explain how the Ruby code above works, I have modified it to display calculations made at each step.
idx_arr = ​[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
ts = [100, 340, 460, 512, 733, 999, 1462, 1581, 1622, 1699, 1833,
(0..timestamps.size-1).each do |i|
n = ts[i] + 299
j = idx_arr.bsearch { |j| ts[j] > n }
puts "time #{ts[i]} to #{n}, first after interval index = #{j}, value = #{ts[j]}, j-i = #{j-i}"
end
displays the following.
time 100 to 399, 1st aft interval idx = 2, val = 460, j-i = 2
time 340 to 639, 1st aft interval idx = 4, val = 733, j-i = 3
time 460 to 759, 1st aft interval idx = 5, val = 999, j-i = 3
time 512 to 811, 1st aft interval idx = 5, val = 999, j-i = 2
time 733 to 1032, 1st aft interval idx = 6, val = 1462, j-i = 2
time 999 to 1298, 1st aft interval idx = 6, val = 1462, j-i = 1
time 1462 to 1761, 1st aft interval idx = 10, val = 1833, j-i = 4
time 1581 to 1880, 1st aft interval idx = 11, val = Inf, j-i = 4
time 1622 to 1921, 1st aft interval idx = 11, val = Inf, j-i = 3
time 1699 to 1998, 1st aft interval idx = 11, val = Inf, j-i = 2
time 1833 to 2132, 1st aft interval idx = 11, val = Inf, j-i = 1
Map maximum number of timespaces per 5-minute interval to resource_id's
Suppose, as in the example given in the question,
arr = [["400", "1"], ["405", "2"], ["605", "4"], ["505", "3"],
["604", "1"], ["1505", "3"], ["1205", "2"]]
Construct the following hash.
h = {}
arr.each do |timestamp, id|
ts = timestamp.to_i
h[ts] = [] if !h.key?(ts)
h[ts] << id
end
h #=> {400=>["1"], 405=>["2"], 605=>["4"], 505=>["3"], 604=>["1"],
# 1505=>["3"], 1205=>["2"]}
I've saved the values in arrays in case two or more timestamps are equal.
Then
timestamps = h.keys.sort
#=> [400, 405, 505, 604, 605, 1205, 1505]
g = max_interval_coverage(timestamps)
#=> {:start_idx=>0, :coverage=>5}
so the desired result is:
{ resource_ids: h[timestamps[g[:start_idx]]], max_per_interval: g[:coverage] }
#=> {:resource_ids=>["1"], :max_per_interval=>5}
This coincides with the stated requirement, "return the most accesses out of any of the resources in any 5 min. block of time.", but not with the desired return value for the example (which would be {:resource_ids=>["1"], :max_per_interval=>2}), which appears inconsistent with requirements.

Split a number into random number of parts where the sum of each part forms the number again

Let's say I have an integer 50. I'm trying to split this into a random number of parts so that the sum of each part form 50 again. E.g, if I split it in 4 parts: 20 + 10 + 15 + 5 = 50.
The number of parts (always less than the total amount) will have to be randomly generated and provided. How can I achieve this?
python code:
def split_num():
n = 50
parts = random.randint(1,n)
result = []
for i in range(parts-1):
x = random.randint(1,n-parts+i+1)
n = n - x
result.append(x)
result.append(n)
print(result)
print(sum(result))
import random
split_num()
result:
[4, 33, 4, 1, 8]
50

Random selection between 2 or more ranges

I'm trying to make a random number generator which will pick more "evenly" between 3 and 4 digit ranges. If I just do:
result = rand(100..9999)
I'm well aware that in the majority of cases, a 4 digit number will be selected. I want to give 3-digit numbers more chance of being selected, so I did this:
rand_3_digit_num = (100..999)
rand_4_digit_num = (1000..9999)
rand([rand_3_digit_num, rand_4_digit_num].sample)
Is there any other approach to do this? My goal is to just give 3-digit numbers bigger chance of being selected than with an ordinary rand. This problem gets even worse if I introduce 5-digit or 6-digit numbers, the chance of 3-digit or 4-digit numbers to get selected quickly diminishes.
Brute solution:
list = (100..999).to_a*10 + (1000..9999).to_a
=> [100, ..., 9999]
list.size
=> 18000
list.count { |e| e < 1000 }
=> 9000
list.count { |e| 999 < e && e < 10000 }
=> 9000
Now list.sample should give equal probabilities of 3- and 4-digits numbers.
First specify a probability distribution over ranges, say:
range_prob = { (100..999) => 0.2,
(1000..9999) => 0.5,
(10000..43562) => 0.3 }
Given these probabilities, a range can be selected at random thusly:
def select_random_range(range_prob)
rnd_cum_prob = rand
cum_prob = 0.0
range_prob.each_with_object({}) do |(rng, prob),h|
cum_prob += prob
h[rng] = cum_prob
end.find { |rng, cum_prob| rnd_cum_prob <= cum_prob }.first
end
What I have done here is construct a cumulative distribution function ("cdf") from the discrete probability density function ("pdf") range_prob. (See the graph below.) To obtain a random variate we generate a pseudo-random number between zero and one, plot that on the vertical axis, determine where a horizontal line intersects the cdf and select the associated value on the horizontal axis.
For range_prob above,
select_random_range(range_prob) #=> 10000..43562
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 1000..9999
select_random_range(range_prob) #=> 100..999
select_random_range(range_prob) #=> 10000..43562
Selecting a random value in a random range is small additional step.
rand select_random_range(range_prob) #=> 6467
rand select_random_range(range_prob) #=> 16689
rand select_random_range(range_prob) #=> 2282
rand select_random_range(range_prob) #=> 1317
rand select_random_range(range_prob) #=> 9015
See Kernel#rand.
I think your idea is good. What you want to achieve is find the uniformly random N, where N represents number of digits in the number, then find the random number of length N.
You could split that in two functions:
randomSelection(lengths):
K = A random number from the array lengths
return randomNumberForLength(K)
randomNumberForLength(K):
lower_bound = 10^K
upper_bound = 10^(K+1) - 1
return rand(lower_bound, upper_bound)
If you wanted to find a random number between 100 - 9999 giving equal probability to both 2-length and 3-length numbers, you can just call randomSelection([2,3])
It depends entirely on how you want to bias the results. For example, if you want an even chance that you'll get a three or four digit number, you can use something as simple as (pseudo-code):
def getRand():
if rand(0, 1) == 0: // assume inclusive both ends.
return rand(100, 999)
return rand(1000, 9999)
Although the fact that you're calling rand twice may stuff up distributions for truly random requirements, it's probably good enough for most purposes.
To do it in a single call which is therefore likely to preserve distribution, you can just map values:
def getRand():
num = rand(1000, 18999)
if num > 9999:
num = (num - 10000) % 900 + 100
This would generate two equal-sized groups, 1000-9999 and 10000-18999 and would map the values in the upper group to become 100-999 (hence equally likely to get a three- or four-digit number):
10000 - 10899 -> 100 - 999
10900 - 11799 -> 100 - 999
11800 - 12699 -> 100 - 999
12700 - 13599 -> 100 - 999
13600 - 14499 -> 100 - 999
14500 - 15399 -> 100 - 999
15400 - 16299 -> 100 - 999
16300 - 17199 -> 100 - 999
17200 - 18099 -> 100 - 999
18100 - 18999 -> 100 - 999
There are no doubt other ways to do it but it all depends on the desired distribution.
For the problem you described, your solution is good enough.
999 will appear 10 times more often than 1000, though. If you want a smoother transition between the ranges, you could use :
# Defines a distribution for random numbers between min and max.
# Smaller numbers have a higher probably to appear.
class BiasedGenerator
def initialize(min, max)
#range = (Math.log(min)..Math.log(max))
end
def self.digit_range(min_digit, max_digit)
new(10**(min_digit - 1), 10**max_digit - 1)
end
def rand
Math.exp(Kernel.rand(#range)).round
end
end
You just need to initialize it once :
generator = BiasedGenerator.digit_range(3, 4)
and use generator.rand as many times as you want :
random_numbers = (1..1_000_000).map do
generator.rand
end
puts 'Min :'
puts random_numbers.min
puts 'Max :'
puts random_numbers.max
puts
random_numbers.group_by { |n| n.to_s.size }.sort_by(&:first).each do |digits, numbers|
puts "#{digits} digits : #{numbers.size}"
end
it outputs :
Min :
100
Max :
9999
3 digits : 500061
4 digits : 499939
The distribution looks like this :
The green area between 100 and 999 should be almost the same as the one between 1000 and 9999.
Your generator also has this property :
For comparison, here is Kernel.rand :
With BiasedGenerator.digit_range(3, 6) :
Min :
100
Max :
999998
3 digits : 250342
4 digits : 250714
5 digits : 249814
6 digits : 249130

given n, how to find the number of different ways to write n as the sum of 1, 3, 4 in ruby?

Problem: given n, find the number of different ways to write n as the sum of 1, 3, 4
Example:for n=5, the answer is 6
5=1+1+1+1+1
5=1+1+3
5=1+3+1
5=3+1+1
5=1+4
5=4+1
I have tried with permutation method,but its efficiency is very low,is there a more efficient way to do?
Using dynamic programming with a lookup table (implemented with a hash, as it makes the code simpler):
nums=[1,3,4]
n=5
table={0=>1}
1.upto(n) { |i|
table[i] = nums.map { |num| table[i-num].to_i }.reduce(:+)
}
table[n]
# => 6
Note: Just checking one of the other answers, mine was instantaneous for n=500.
def add_next sum, a1, a2
residue = a1.inject(sum, :-)
residue.zero? ? [a1] : a2.reject{|x| residue < x}.map{|x| a1 + [x]}
end
a = [[]]
until a == (b = a.flat_map{|a| add_next(5, a, [1, 3, 4])})
a = b
end
a:
[
[1, 1, 1, 1, 1],
[1, 1, 3],
[1, 3, 1],
[1, 4],
[3, 1, 1],
[4, 1]
]
a.length #=> 6
I believe this problem should be addressed in two steps.
Step 1
The first step is to determine the different numbers of 1s, 3s and 4s that sum to the given number. For n = 5, there are only 3, which we could write:
[[5,0,0], [2,1,0], [1,0,1]]
These 3 elements are respectively interpreted as "five 1s, zero 3s and zero 4s", "two 1s, one 3 and zero 4s" and "one 1, zero 3s and one 4".
To compute these combinations efficiently, I first I compute the possible combinations using only 1s, that sum to each number between zero and 5 (which of course is trivial). These values are saved in a hash, whose keys are the summands and the value is the numbers of 1's needed to sum to the value of the key:
h0 = { 0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5 }
(If the first number had been 2, rather than 1, this would have been:
h0 = { 0 => 0, 2 => 1, 4 => 2 }
since there is no way to sum only 2s to equal 1 or 3.)
Next we consider using both 1 and 3 to sum to each value between 0 and 5. There are only two choices for the number of 3s used, zero or one. This gives rise to the hash:
h1 = { 0 => [[0,0]], 1 => [[1,0]], 2 => [[2,0]], 3 => [[3,0], [0,1]],
4 => [[4,0], [1,1]], 5 => [[5,0], [2,1]] }
This indicates, for example, that:
there is only 1 way to use 1 and 3 to sum to 1: 1 => [1,0], meaning one 1 and zero 3s.
there are two ways to sum to 4: 4 => [[4,0], [1,1]], meaning four 1s and zero 3s or one 1 and one 3.
Similarly, when 1, 3 and 4 can all be used, we obtain the hash:
h2 = { 5 => [[5,0,0], [2,1,0], [1,0,1]] }
Since this hash corresponds to the use of all three numbers, 1, 3 and 4, we are concerned only with the combinations that sum to 5.
In constructing h2, we can use zero 4s or one 4. If we use use zero 4s, we would use one 1s and 3s that sum to 5. We see from h1 that there are two combinations:
5 => [[5,0], [2,1]]
For h2 we write these as:
[[5,0,0], [2,1,0]]
If one 4 is used, 1s and 3s totalling 5 - 1*4 = 1 are used. From h1 we see there is just one combination:
1 => [[1,0]]
which for h2 we write as
[[1,0,1]]
so
the value for the key 5 in h2 is:
[[5,0,0], [2,1,0]] + [[1,0,1]] = [[5,0,0], [2,1,0]], [1,0,1]]
Aside: because of form of hashes I've chosen to represent hashes h1 and h2, it is actually more convenient to represent h0 as:
h0 = { 0 => [[0]], 1 => [[1]],..., 5 => [[5]] }
It should be evident how this sequential approach could be used for any collection of integers whose combinations are to be summed.
Step 2
The numbers of distinct arrangements of each array [n1, n3, n4] produced in Step 1 equals:
(n1+n3+n4)!/(n1!n3!n4!)
Note that if one of the n's were zero, these would be binomial coefficients. If fact, these are coefficients from the multinomial distribution, which is a generalization of the binomial distribution. The reasoning is simple. The numerator gives the number of permutations of all the numbers. The n1 1s can be permuted n1! ways for each distinct arrangement, so we divide by n1!. Same for n3 and n4
For the example of summing to 5, there are:
5!/5! = 1 distinct arrangement for [5,0,0]
(2+1)!/(2!1!) = 3 distinct arrangements for [2,1,0] and
(1+1)!/(1!1!) = 2 distinct arrangements for [1,0,1], for a total of:
1+3+2 = 6 distinct arrangements for the number 5.
Code
def count_combos(arr, n)
a = make_combos(arr,n)
a.reduce(0) { |tot,b| tot + multinomial(b) }
end
def make_combos(arr, n)
arr.size.times.each_with_object([]) do |i,a|
val = arr[i]
if i.zero?
a[0] = (0..n).each_with_object({}) { |t,h|
h[t] = [[t/val]] if (t%val).zero? }
else
first = (i==arr.size-1) ? n : 0
a[i] = (first..n).each_with_object({}) do |t,h|
combos = (0..t/val).each_with_object([]) do |p,b|
prev = a[i-1][t-p*val]
prev.map { |pr| b << (pr +[p]) } if prev
end
h[t] = combos unless combos.empty?
end
end
end.last[n]
end
def multinomial(arr)
(arr.reduce(:+)).factorial/(arr.reduce(1) { |tot,n|
tot * n.factorial })
end
and a helper:
class Fixnum
def factorial
return 1 if self < 2
(1..self).reduce(:*)
end
end
Examples
count_combos([1,3,4], 5) #=> 6
count_combos([1,3,4], 6) #=> 9
count_combos([1,3,4], 9) #=> 40
count_combos([1,3,4], 15) #=> 714
count_combos([1,3,4], 30) #=> 974169
count_combos([1,3,4], 50) #=> 14736260449
count_combos([2,3,4], 50) #=> 72581632
count_combos([2,3,4,6], 30) #=> 82521
count_combos([1,3,4], 500) #1632395546095013745514524935957247\
00017620846265794375806005112440749890967784788181321124006922685358001
(I broke the result the example (one long number) into two pieces, for display purposes.)
count_combos([1,3,4], 500) took about 2 seconds to compute; the others were essentially instantaneous.
#sawa's method and mine gave the same results for n between 6 and 9, so I'm confident they are both correct. sawa's solution times increase much more quickly with n than do mine, because he is computing and then counting all the permutations.
Edit: #Karole, who just posted an answer, and I get the same results for all my tests (including the last one!). Which answer do I prefer? Hmmm. Let me think about that.)
I don't know ruby so I am writing it in C++
say for your example n=5.
Use dynamic programming set
int D[n],n;
cin>>n;
D[0]=1;
D[1]=1;
D[2]=1;
D[3]=2;
for(i = 4; i <= n; i++)
D[i] = D[i-1] + D[i-3] + D[i-4];
cout<<D[i];

Minimum value to add or subtract to generate a sorted array

Given an array of integers, I would like to find the minimum number x such that increasing or decreasing the elements in the array by a number in the range of 0 to x will result in an array sorted in ascending order.
For example, for [5,4,3,2,8], the minimum value of x is 3. This is because [2,3,4,5,8] can be obtained by increasing or decreasing every element by either 0,1,2 or 3:
5-3 = 2
4-1 = 3
3+1 = 4
2+3 = 5
8+0 = 8
Say we had a more complicated array like [52,71,36,92,48]. How would I solve this?
a = [52, 71, 36, 92, 48]
b = a.map.with_index{|e, i| e - i}
((b.max - b.min) / 2.0).ceil
# => 28

Resources