Limit of digit-by-digit calculation of square roots - algorithm

I am trying to get as good an estimate of pi as I can using the Chudnovsky algorithm in Python. This algorithm implies getting the square root of 640 320.
After doing some research, I found a quite effective way to compute square roots; the method is called "digit-by-digit calculation" (see here). So, after trying to implement it, I found out that the first 13 decimals are correct, and then I get strange results (the next one is a 0 instead of a 4, and then the next 'digit' is 128, then -1024...)
I tried checking my function, but it looks fine to me (besides, I would probably not find the correct first 13 decimals otherwise). Thus, my question is : are there some limits in this digit-by-digit calculation method?
If, by any chance, you would like to see my code, here it is:
def sqrt(input_number,accuracy):
"""input_number is a list that represents a number we want to get the square root of.
For example, 12.56 would be [[1,2], [5,6], '+']"""
if input_number[2]!="+":
raise ValueError("Cannot find the real square root of a negative number: '"+pl(input_number)+"'")
"""Below creates the right number of elements for the required accuracy of the
square root"""
if len(input_number[0])%2==1:
input_number[0].insert(0,0)
if len(input_number[1])<2*accuracy:
for i in range(2*accuracy-len(input_number[1])):
input_number[1].append(0)
if len(input_number[1])%2==1:
input_number[1].append(0)
# Below makes the pairs of digits required in the algorithm
pairs=[[10*input_number[0][2*i]+input_number[0][2*i+1] for i in range(int(len(input_number[0])/2))],[10*input_number[1][2*i]+input_number[1][2*i+1] for i in range(int(len(input_number[1])/2))]]
"""Performs the algorithm, where c,x,y and p have the same definition
as on the Wikipedia link above. r is the remainder. pairs[0] is the pairs
of digits before the decimal dot, and pairs[1] represents the pairs of
digits after the dot. square_root is the computed square root of input_number."""
p=0
r=0
square_root=[[],[],"+"]
for i in range(len(pairs[0])):
c=100*r+pairs[0][i]
x=int((-20*p+(400*p**2+4*c)**.5)/2)
y=20*p*x+x**2
r=c-y
p=10*p+x
square_root[0].append(x)
for i in range(len(pairs[1])):
print(p,r,c)
c=100*r+pairs[1][i]
x=int((-20*p+(400*p**2+4*c)**.5)/2)
y=20*p*x+x**2
r=c-y
p=10*p+x
square_root[1].append(x)
return square_root

The problem is this code.
x = int((-20 * p + (400 * p ** 2 + 4 * c) ** .5) / 2)
This code performs Floating-point subtraction. It causes loss of significance, because of two close numbers are subtracted.
>>> p = 10**15
>>> c = 10**15
>>> x = (-20 * p + (400 * p ** 2 + 4 * c) ** .5) / 2
>>> x
0.0
so, you should use integer sqrt instead of **.5. and change loop like this.
for i in range(len(pairs[0])):
c = 100 * r + pairs[0][i]
#x = int((-20 * p + (400 * p ** 2 + 4 * c) ** .5) / 2)
x = (-20 * p + isqrt(400 * p ** 2 + 4 * c)) // 2
y = 20 * p * x + x ** 2
r = c - y
p = 10 * p + x
square_root[0].append(x)
for i in range(len(pairs[1])):
#print(p,r,c)
c = 100 * r + pairs[1][i]
#x = int((-20 * p + (400 * p ** 2 + 4 * c) ** .5) / 2)
x = (-20 * p + isqrt(400 * p ** 2 + 4 * c)) // 2
y = 20 * p * x + x ** 2
r = c - y
p = 10 * p + x
square_root[1].append(x)
and define isqrt -- the integer sqrt function
# from http://stackoverflow.com/questions/15390807/integer-square-root-in-python
def isqrt(n):
x = n
y = (x + 1) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
Then, you could get an accurated value of sqrt(2).
>>> print( sqrt([[2], [0], '+'], 25) )
[[1], [4, 1, 4, 2, 1, 3, 5, 6, 2, 3, 7, 3, 0, 9, 5, 0, 4, 8, 8, 0, 1, 6, 8, 8, 7], '+']

Related

Efficient algorithm to find the n-th digit in the string 112123123412345

What is an efficient algorithm for finding the digit in nth position in the following string
112123123412345123456 ... 123456789101112 ...
Storing the entire string in memory is not feasible for very large n, so I am looking for an algorithm that can find the nth digit in the above string which works if n is very large (i.e. an alternative to just generating the first n digits of the string).
There are several levels here: the digit is part of a number x, the number x is part of a sequence 1,2,3...x...y and that sequence is part of a block of sequences that lead up to numbers like y that have z digits. We'll tackle these levels one by one.
There are 9 numbers with 1 digit:
first: 1 (sequence length: 1 * 1)
last: 9 (sequence length: 9 * 1)
average sequence length: (1 + 9) / 2 = 5
1-digit block length: 9 * 5 = 45
There are 90 numbers with 2 digits:
first: 10 (sequence length: 9 * 1 + 1 * 2)
last: 99 (sequence length: 9 * 1 + 90 * 2)
average sequence length: 9 + (2 + 180) / 2 = 100
2-digit block length: 90 * 100 = 9000
There are 900 numbers with 3 digits:
first: 100 (sequence length: 9 * 1 + 90 * 2 + 1 * 3)
last: 999 (sequence length: 9 * 1 + 90 * 2 + 900 * 3)
average sequence length: 9 + 180 + (3 + 2,700) / 2 = 1,540.5
3-digit block length: 900 * 1,540.5 = 1,386,450
If you continue to calculate these values, you'll find which block (of sequences up to how many digits) the digit you're looking for is in, and you'll know the start and end point of this block.
Say you want the millionth digit. You find that it's in the 3-digit block, and that this block is located in the total sequence at:
start of 3-digit block: 45 + 9,000 + = 9,045
start of 4-digit block: 45 + 9,000 + 1,386,450 = 1,395,495
So in this block we're looking for digit number:
1,000,000 - 9,045 = 990,955
Now you can use e.g. a binary search to find which sequence the 990,955th digit is in; you start with the 3-digit number halfway in the 3-digit block:
first: 100 (sequence length: 9 + 180 + 1 * 3)
number: 550 (sequence length: 9 + 180 + 550 * 3)
average sequence length: 9 + 180 + (3 + 1650) / 2 = 1,015.5
total sequence length: 550 * 1,015.5 = 558,525
Which is too small; so we try 550 * 3/4 = 825, see if that is too small or large, and go up or down in increasingly smaller steps until we know which sequence the 990,995th digit is in.
Say it's in the sequence for the number n; then we calculate the total length of all 3-digit sequences up to n-1, and this will give us the location of the digit we're looking for in the sequence for the number n. Then we can use the numbers 9*1, 90*2, 900*3 ... to find which number the digit is in, and then what the digit is.
We have three types of structures that we would like to be able to search on, (1) the sequence of concatenating d-digit numbers, for example, single digit:
123456...
or 3-digit:
100101102103
(2) the rows in a section,
where each section builds on the previous section added to a prefix. For example, section 1:
1
12
123
...
or section 3:
1234...10111213...100
1234...10111213...100102
1234...10111213...100102103
<----- prefix ----->
and (3) the full sections, although the latter we can just enumerate since they grow exponentially and help build our section prefixes. For (1), we can use simple division if we know the digit count; for (2), we can binary search.
Here's Python code that also answers the big ones:
def getGreatest(n, d, prefix):
rows = 9 * 10**(d - 1)
triangle = rows * (d + rows * d) // 2
l = 0
r = triangle
while l < r:
mid = l + ((r - l) >> 1)
triangle = mid * prefix + mid * (d + mid * d) // 2
prevTriangle = (mid-1) * prefix + (mid-1) * (d + (mid-1) * d) // 2
nextTriangle = (mid+1) * prefix + (mid+1) * (d + (mid+1) * d) // 2
if triangle >= n:
if prevTriangle < n:
return prevTriangle
else:
r = mid - 1
else:
if nextTriangle >= n:
return triangle
else:
l = mid
return l * prefix + l * (d + l * d) // 2
def solve(n):
debug = 1
d = 0
p = 0.1
prefixes = [0]
sections = [0]
while sections[d] < n:
d += 1
p *= 10
rows = int(9 * p)
triangle = rows * (d + rows * d) // 2
section = rows * prefixes[d-1] + triangle
sections.append(sections[d-1] + section)
prefixes.append(prefixes[d-1] + rows * d)
section = sections[d - 1]
if debug:
print("section: %s" % section)
n = n - section
rows = getGreatest(n, d, prefixes[d - 1])
if debug:
print("rows: %s" % rows)
n = n - rows
d = 1
while prefixes[d] < n:
d += 1;
if prefixes[d] == n:
return 9;
prefix = prefixes[d - 1]
if debug:
print("prefix: %s" % prefix)
n -= prefix
if debug:
print((n, d, prefixes, sections))
countDDigitNums = n // d
remainder = n % d
prev = 10**(d - 1) - 1
num = prev + countDDigitNums
if debug:
print("num: %s" % num)
if remainder:
return int(str(num + 1)[remainder - 1])
else:
s = str(num);
return int(s[len(s) - 1])
ns = [
1, # 1
2, # 1
3, # 2
100, # 1
2100, # 2
31000, # 2
999999999999999999, # 4
1000000000000000000, # 1
999999999999999993, # 7
]
for n in ns:
print(n)
print(solve(n))
print('')
Well, you have a series of sequences each increasing by a single number.
If you have "x" of them, then the sequences up to that point occupy x * (x + 1) / 2 character positions. Or, another way of saying this is that the "x"s sequence starts at x * (x - 1) / 2 (assuming zero-based indexing). These are called triangular numbers.
So, all you need to do is to find the "x" value where the cumulative amount is closest to a given "n". Here are three ways:
Search for a closed from solution. This exists, but the formula is rather complicated. (Here is one reference for the sum of triangular numbers.)
Pre-calculate a table in memory with values up to, say, 1,000,000. that will get you to 10^10 sizes.
Use a "binary" search and the formula. So, generate the sequence of values for 1, 2, 4, 8, and so on and then do a binary search to find the exact sequence.
Once you know the sequence where the value lies, determining the value is simply a matter of arithmetic.

Finding natural numbers having n Trailing Zeroes in Factorial

I need help with the following problem.
Given an integer m, I need to find the number of positive integers n and the integers, such that the factorial of n ends with exactly m zeroes.
I wrote this code it works fine and i get the right output, but it take way too much time as the numbers increase.
a = input()
while a:
x = []
m, n, fact, c, j = input(), 0, 1, 0, 0
z = 10*m
t = 10**m
while z - 1:
fact = 1
n = n + 1
for i in range(1, n + 1):
fact = fact * i
if fact % t == 0 and ((fact / t) % 10) != 0:
x.append(int(n))
c = c + 1
z = z - 1
for p in range(c):
print x[p],
a -= 1
print c
Could someone suggest me a more efficient way to do this. Presently, it takes 30 seconds for a test case asking for numbers with 250 trailing zeros in its factorial.
Thanks
To get number of trailing zeroes of n! efficiently you can put
def zeroes(value):
result = 0;
d = 5;
while (d <= value):
result += value // d; # integer division
d *= 5;
return result;
...
# 305: 1234! has exactly 305 trailing zeroes
print zeroes(1234)
In order to solve the problem (what numbers have n trailing zeroes in n!) you can use these facts:
number of zeroes is a monotonous function: f(x + a) >= f(x) if a >= 0.
if f(x) = y then x <= y * 5 (we count only 5 factors).
if f(x) = y then x >= y * 4 (let me leave this for you to prove)
Then implement binary search (on monotonous function).
E.g. in case of 250 zeroes we have the initial range to test [4*250..5*250] == [1000..1250]. Binary search narrows the range down into [1005..1009].
1005, 1006, 1007, 1008, 1009 are all numbers such that they have exactly 250 trainling zeroes in factorial
Edit I hope I don't spoil the fun if I (after 2 years) prove the last conjecture (see comments below):
Each 5**n within facrtorial when multiplied by 2**n produces 10**n and thus n zeroes; that's why f(x) is
f(x) = [x / 5] + [x / 25] + [x / 125] + ... + [x / 5**n] + ...
where [...] stands for floor or integer part (e.g. [3.1415926] == 3). Let's perform easy manipulations:
f(x) = [x / 5] + [x / 25] + [x / 125] + ... + [x / 5**n] + ... <= # removing [...]
x / 5 + x / 25 + x / 125 + ... + x / 5**n + ... =
x * (1/5 + 1/25 + 1/125 + ... + 1/5**n + ...) =
x * (1/5 * 1/(1 - 1/5)) =
x * 1/5 * 5/4 =
x / 4
So far so good
f(x) <= x / 4
Or if y = f(x) then x >= 4 * y Q.E.D.
Focus on the number of 2s and 5s that makes up a number. e.g. 150 is made up of 2*3*5*5, there 1 pair of 2&5 so there's one trailing zero. Each time you increase the tested number, try figuring out how much 2 and 5s are in the number. From that, adding up previous results you can easily know how much zeros its factorial contains.
For example, 15!=15*...*5*4*3*2*1, starting from 2:
Number 2s 5s trailing zeros of factorial
2 1 0 0
3 1 0 0
4 2 0 0
5 2 1 1
6 3 1 1
...
10 5 2 2
...
15 7 3 3
..
24 12 6 6
25 12 8 8 <- 25 counts for two 5-s: 25 == 5 * 5 == 5**2
26 13 8 8
..
Refer to Peter de Rivaz's and Dmitry Bychenko's comments, they have got some good advices.

Is there a Ruby method to grab the ones/tenths/hundredths place for an integer?

I'm doing a Ruby kata that asks me to find the sum of the digits of all the numbers from 1 to N (both ends included).
So if I had these inputs, I would get these outputs:
For N = 10 the sum is 1+2+3+4+5+6+7+8+9+(1+0) = 46
For N = 11 the sum is 1+2+3+4+5+6+7+8+9+(1+0)+(1+1) = 48
For N = 12 the sum is 1+2+3+4+5+6+7+8+9+(1+0)+(1+1) +(1+2)= 51
Now I know in my head what needs to be done. Below is the code that I have to solve this problem:
def solution(n)
if n <= 9
return n if n == 1
solution(n-1) + n
elsif n >= 10
45 + (10..n) #How can I grab the ones,tenths, and hundreds?
end
end
Basically everything is fine until I hit over 10.
I'm trying to find some sort of method that could do this. I searched Fixnum and Integer but I haven't found anything that could help me. I want is to find something like "string"[0] but of course without having to turn the integer back in forth between a string and integer. I know that there is a mathematical relationship there but I'm having a hard time trying to decipher that.
Any help would be appreciated.
You can use modulo and integer division to calculate it recursively:
def sum_digits(n)
return n if n < 10
(n % 10) + sum_digits(n / 10)
end
sum_digits(123)
# => 6
A beginner would probably do this:
123.to_s.chars.map(&:to_i)
# => [1, 2, 3]
but a more thoughtful person would do this:
n, a = 123, []
until n.zero?
n, r = n.divmod(10)
a.unshift(r)
end
a
# => [1, 2, 3]
Rather than computing the sum of the digits for each number in the range, and then summing those subtotals, I have computed the total using combinatorial methods. As such, it is much more efficient than straight enumeration.
Code
SUM_ONES = 10.times.with_object([]) { |i,a| a << i*(i+1)/2 }
S = SUM_ONES[9]
def sum_digits_nbrs_up_to(n)
pwr = n.to_s.size - 1
tot = n.to_s.chars.map(&:to_i).reduce(:+)
sum_leading_digits = 0
pwr.downto(0).each do |p|
pwr_term = 10**p
leading_digit = n/pwr_term
range_size = leading_digit * pwr_term
tot += sum_leading_digits * range_size +
sum_digits_to_pwr(leading_digit, p)
sum_leading_digits += leading_digit
n -= range_size
end
tot
end
def sum_digits_to_pwr(d, p)
case
when d.zero? && p.zero?
0
when d.zero?
10**(p-1) * S * d * p
when p.zero?
10**p * SUM_ONES[d-1]
else
10**p * SUM_ONES[d-1] + 10**(p-1) * S * d * p
end
end
Examples
sum_digits_nbrs_up_to(456) #=> 4809
sum_digits_nbrs_up_to(2345) #=> 32109
sum_digits_nbrs_up_to(43021) #=> 835759
sum_digits_nbrs_up_to(65827359463206357924639357824065821)
#=> 10243650329265398180347270847360769369
These calculations were all essentially instantaneous. I verified the totals for the first three examples by straight enumeration, using #sawa's method for calculating the sum of digits for each number in the range.
Explanation
The algorithm can best be explained with an example. Suppose n equals 2345.
We begin by defining the following functions:
t(n) : sum of all digits of all numbers between 1 and n, inclusive (the answer)
sum(d): sum of all digits between 1 and d, inclusive, (for d=1..9, sum(d) = 0, 1, 3, 6, 10, 15, 21, 28, 36, 45).
g(i) : sum of digits of the number i.
f(i,j): sum of all digits of all integers between i and j-1, inclusive.
g(m) : sum of digits of the number m.
h(d,p): sum of all digits of all numbers between 0 and d*(10^p)-1 (derived below).
Then (I explain the following below):
t(2345) = f(0-1999)+f(2000-2299)+f(2300-2339)+f(2340-2344)+g(2345)
f( 0-1999) = h(2,3) = h(2,3)
f(2000-2299) = 2 * (2299-2000+1) + h(3,2) = 600 + h(3,2)
f(2300-2339) = (2+3) * (2339-2300+1) + h(4,1) = 200 + h(4,1)
f(2340-2344) = (2+3+4) * (2344-2340+1) + h(5,0) = 45 + h(5,0)
g(2345) = 2+3+4+5 = 14
so
t(2345) = 859 + h(2,3) + h(3,2) + h(4,1) + h(5,0)
First consider f(2000-2299). The first digit, 2, appears in every number in the range (2000..2299); i.e., 300 times. The remaining three digits contribute (by definition) h(3,2) to the total:
f(2000-2299) = 2 * 300 + h(3,2)
For f(2300-2339) the first two digits, 2 and 3, are present in all 40 numbers in the range (2300..2339) and the remaining two digits contribute h(4,1) to the total:
f(2300-2339) = 5 * 40 + h(4,1)
For f(2340-2344), the first three digits, '2,3and4, are present in all four number in the range ``(2340-2344) and the last digit contributes h(5,0) to the total.
It remains to derive an expression for computing h(d,p). Again, this is best explained with an example.
Consider h(3,2), which is the sum of the all digits of all numbers between 0 and 299.
First consider the sum of digits for the first digit. 0, 1 and 2 are each the first digit for 100 numbers in the range 0-299. Hence, the first digit, summed, contributes
0*100 + 1*100 + 2*100 = sum(2) * 10^2
to the total. We now add the sum of digits for the remaining 2 digits. The 300 numbers each have 2 digits in the last two positions. Each of the digits 0-9 appears in 1/10th of 2 * 300 = 600 digits; i.e, 60 times. Hence, the sum of all digits in last 2 digit positions, over all 300 numbers, equals:
sum(9) * 2 * 300 / 10 = 45 * 2 * 30 = 2700.
More generally,
h(d,p) = sum(d-1) * 10**p + sum(9) * d * p * 10**(p-1) if d > 0 and p > 0
= sum(d-1) * 10**p if d > 0 and p == 0
= sum(9) * d * p * 10**(p-1) if d == 0 and p > 0
= 0 if d == 0 and p == 0
Applying this to the above example, we have
h(2,3) = sum(1) * 10**3 + (45 * 2 * 3) * 10**2 = 1 * 1000 + 270 * 100 = 28000
h(3,2) = sum(2) * 10**2 + (45 * 3 * 2) * 10**1 = 3 * 100 + 270 * 10 = 3000
h(4,1) = sum(3) * 10**1 + (45 * 4 * 1) * 10**0 = 6 * 10 + 180 * 1 = 240
h(5,0) = sum(4) * 10**0 = 10 * 1 = 10
Therefore
t(2345) = 859 + 28000 + 3000 + 240 + 10 = 32109
The code above implements this algorithm in a straightforward way.
I confirmed the results for the first three examples above by using using #sawa's code to determine the sum of the digits for each number in the range and then summed those totals:
def sum_digits(n)
a = []
until n.zero?
n, r = n.divmod(10)
a.unshift(r)
end
a.reduce(:+)
end
def check_sum_digits_nbrs_up_to(n)
(1..n).reduce(0) {|t,i| t + sum_digits(i) }
end
check_sum_digits_nbrs_up_to(2345) #=> 32109

Octave - Split number into matrix

Using Octave I want to take a number for example
x = 14
And split it into a matrix like the following.
m = [1, 4]
So far I've tried converting the number into a string, and then using the str2mat function without result.
Another example would be
x = 23445
Converted to
m = [2, 3, 4, 4, 5]
Thanks in advance for the help.
x = 14;
sprintf('%d',x) - '0'
>> 1 4
If you want to do this via string manipulation, provided there's no decimal parts, this is the way to go:
octave> x = 23445
x = 23445
octave> s = int2str (x)
s = 23445
octave> m = arrayfun (#(x) str2double (s(x)), 1:numel(s))
m =
2 3 4 4 5
Not tested, but the idea is: try to divide the number x = 23445 by 10 and take the decimal part.
For example, iterate:
x = 23445;
t = 23445/10; # t is 2344,5
r = floor(t); # r is 2344
d = x - r * 10 # d is 5 = 23445 - 2344 * 10
d will have value 5 (you have the last digit of x, add it to the array). r will have value 2344. So now:
x = r; # so x = 2344;
t = 2344/10; # t is 234,4
r = floor(t); # r is 234
d = x - r * 10 # d is 4 = 2344 - 234 * 10
d will have value 4. r will have value 234.
And iterate till r = 0.

how many n digit numbers are there with product p

What algorithm should we use to get the count of n digit numbers such that the product of its digits is p; the special condition here is that none of the digits should be 1;
What i have thought so far is to do a prime factorization of p. Say n=3 and p=24.
we first do a prime factorization of 24 to get : 2*2*2*3.
now i have problem in determining the combinations of these which are
4*2*3 , 2*4*3, .... etc
Even if can do so... how will I scale for n is way smaller than the count of primes.
I am not too sure if thats the right direction... any inputs are welcome.
First, you don't really need full prime decomposition, only decomposition to primes smaller than your base (I guess you mean 10 here but the problem can be generalized to any base). So we only need factorization into the first 4 primes: 2, 3, 5 and 7. If the rest (prime or not) factor is anything bigger than 1, then the problem has 0 solutions.
Now, lets assume that the number p is factored into:
p = 2^d1 * 3^d2 * 5^d3 * 7^d4
and is also composed from the n digits:
p = d(n-1)d(n-2)...d2d1d0
Then, rearranging the digits, is will also be:
p = 2^q2 * 3^q3 * 4^q4 * 5^q3 * ... * 9^q9
where qi >= 0 and q2 + q3 + ... q9 = n
and also (due to the factorization):
for prime=2: d1 = q2 + 2*q4 + q6 + 3*q8
for prime=3: d2 = q3 + q6 + 2*q9
for prime=5: d3 = q5
for prime=7: d4 = q7
So the q5 and q7 are fixed and we have to find all non-negative integer solutions to the equations:
(where the unknowns are the rest qi: q2, q3, q4, q6, q8 and q9)
d1 = q2 + 2*q4 + q6 + 3*q8
d2 = q3 + q6 + 2*q9
n - d3 - d4 = q2 + q3 + q4 + q6 + q8 + q9
For every one of the above solutions, there are several rearrangements of the digits, which can be found by the formula:
X = n! / ( q2! * q3! * ... q9! )
which have to be summed up.
There may be a closed formula for this, using generating functions, you could post it at Math.SE
Example for p=24, n=3:
p = 2^3 * 3^1 * 5^0 * 7^0
and we have:
d1=3, d2=1, d3=0, d4=0
The integer solutions to:
3 = q2 + 2*q4 + q6 + 3*q8
1 = q3 + q6 + 2*q9
3 = q2 + q3 + q4 + q6 + q8 + q9
are (q2, q3, q4, q6, q8, q9) =:
(2, 0, 0, 1, 0, 0)
(1, 1, 1, 0, 0, 0)
which give:
3! / ( 2! * 1! ) = 3
3! / ( 1! * 1! * 1! ) = 6
and 3+6 = 9 total solutions.
Example for p=3628800, n=10:
p = 2^8 * 3^4 * 5^1 * 7^1
and we have:
d1=8, d2=4, d3=1, d4=1
The integer solutions to:
8 = q2 + 2*q4 + q6 + 3*q8
4 = q3 + q6 + 2*q9
8 = q2 + q3 + q4 + q6 + q8 + q9
are (q2, q3, q4, q6, q8, q9) (along with the corresponding digits and the rearrangements per solution):
(5, 0, 0, 0, 1, 2) 22222899 57 10! / (5! 2!) = 15120
(4, 0, 2, 0, 0, 2) 22224499 57 10! / (4! 2! 2!) = 37800
(4, 1, 0, 1, 1, 1) 22223689 57 10! / (4!) = 151200
(3, 2, 1, 0, 1, 1) 22233489 57 10! / (3! 2!) = 302400
(4, 0, 1, 2, 0, 1) 22224669 57 10! / (4! 2!) = 75600
(3, 1, 2, 1, 0, 1) 22234469 57 10! / (3! 2!) = 302400
(2, 2, 3, 0, 0, 1) 22334449 57 10! / (3! 2! 2!) = 151200
(2, 4, 0, 0, 2, 0) 22333388 57 10! / (4! 2! 2!) = 37800
(3, 2, 0, 2, 1, 0) 22233668 57 10! / (3! 2! 2!) = 151200
(2, 3, 1, 1, 1, 0) 22333468 57 10! / (3! 2!) = 302400
(1, 4, 2, 0, 1, 0) 23333448 57 10! / (4! 2!) = 75600
(4, 0, 0, 4, 0, 0) 22226666 57 10! / (4! 4!) = 6300
(3, 1, 1, 3, 0, 0) 22234666 57 10! / (3! 3!) = 100800
(2, 2, 2, 2, 0, 0) 22334466 57 10! / (2! 2! 2! 2!) = 226800
(1, 3, 3, 1, 0, 0) 23334446 57 10! / (3! 3!) = 100800
(0, 4, 4, 0, 0, 0) 33334444 57 10! / (4! 4!) = 6300
which is 2043720 total solutions, if I haven't done any mistakes..
I don't think I'd start by tackling what is known to be a 'hard' problem, computing the prime decomposition. By I don't think I mean my gut feeling, rather than any rigorous computation of complexity, tells me.
Since you are ultimately only interested in the single-digit divisors of p I'd start by dividing p by 2, then by 3, then 4, all the way up to 9. Of course, some of these divisions won't produce an integer result in which case you can discard that digit from further consideration.
For your example of p = 24 you'll get {{2},12}, {{3},8}, {{4},6}, {{6},4}, {{8},3} (ie tuples of divisor and remainder). Now apply the approach again, though this time you are looking for the 2 digit numbers whose digits multiply to the remainder. That is, for {{2},12} you would get {{2,2},6},{{2,3},4},{{2,4},3},{{2,6},2}. As it happens all of these results deliver 3-digit numbers whose digits multiply to 24, but in general it is possible that some of the remainders will still have 2 or more digits and you'll need to trim the search tree at those points. Now go back to {{3},8} and carry on.
Note that this approach avoids having to separately calculate how many permutations of a set of digits you need to consider because it enumerates them all. It also avoids having to consider 2*2 and 4 as separate candidates for inclusion.
I expect you could speed this up with a little memoisation too.
Now I look forward to someone more knowledgeable in combinatorics telling us the closed-form solution to this problem.
You can use dynamic programming approach based on the following formula:
f[ n ][ p ] = 9 * ( 10^(n-1) - 9^(n-1) ), if p = 0
0, if n = 1 and p >= 10
1, if n = 1 and p < 10
sum{ f[ n - 1 ][ p / k ] for 0 < k < 10, p mod k = 0 }, if n > 1
The first case is a separate case for p = 0. This case calculates in O(1), besides helps to exclude k = 0 values from 4th case.
The 2nd and 3rd cases are the dynamic base.
The 4th case k sequentially takes all possible values of the last digit, and we sum up quantities of numbers with product p with last digit k by reducing to the same problem of smaller size.
This will have O( n * p ) running time if you implement dp with memorization.
PS: My answer is for more general problem than OP described. If condition that no digit must be equal to 1 must be satisfied, formulas can be adjusted as follows:
f[ n ][ p ] = 8 * ( 9^(n-1) - 8^(n-1) ), if p = 0
0, if n = 1 and p >= 10 or p = 1
1, if n = 1 and 1 < p < 10
sum{ f[ n - 1 ][ p / k ] for 1 < k < 10, p mod k = 0 }, if n > 1
For the N digit numbers and product of its digits is p;
For example if n = 3 and p =24
Arrangement would be as follow (Permutation)
= (p!)/(p-n)!
= (24!) /(24 -3)!
= (24 * 23 * 22 * 21 )! / 21 !
= (24 * 23 * 22 )
= 12144
So it would be 12144 arrangement can be made
And for Combination is as follow
= (p!)/(n!) * (p-n)!
= (24!) /(3!) * (24 -3)!
= (24 * 23 * 22 * 21 )! / (3!) * 21 !
= (24 * 23 * 22 ) / 6
= 2024
May this will help you
The problems seems contrived but in any case there are upper bounds to what you seen. For example p can have no prime divisor > 7 since it needs to be a single digit ("such that the product of its digits").
Hence suppose p = 1 * 2^a * 3^b * 5^c * 7^d.
2^a can come from ceil(a/3) to 'a' digits. 3^b can come from ceil(b/2) to 'b' digits. 5^c and 7^d can come from 'c' and 'd' digits respectively. The remaining digits can be filled with 1s.
Hence n can range from ceil(a/3)+ceil(b/2)+c+d to infinity while p has a set of fixed values.
Prime factorization feels like the right direction, though you don't need any prime greater than 7, so you can just divide by 2,3,5,7 repeatedly. (No solution if we don't get a prime, or get one > 7).
Once we have the prime factors, p % x and p / x can be implemented as constant time operations (you don't actually need p, you can just keep the prime factors).
My idea is, calculate the combinations with the algorithm below, and the permutations from there is easy.
getCombinations(map<int, int> primeCounts, int numSoFar, string str)
if (numSoFar == n)
if (primeCounts == allZeroes)
addCombination(str);
else
;// do nothing, too many digits
else if (primeCounts[7] >= 1) // p % 7
getCombinations(primeCounts - [7]->1, numSoFar-1, str + "7")
else if (primeCounts[5] >= 1) // p % 5
getCombinations(primeCounts - [5]->1, numSoFar-1, str + "5")
else if (primeCounts[3] >= 2) // p % 9
getCombinations(primeCounts - [3]->2, numSoFar-1, str + "9")
getCombinations(primeCounts - [3]->2, numSoFar-2, str + "33")
else if (primeCounts[2] >= 3) // p % 8
getCombinations(primeCounts - [2]->3, numSoFar-1, str + "8")
getCombinations(primeCounts - [2]->3, numSoFar-2, str + "24")
getCombinations(primeCounts - [2]->3, numSoFar-3, str + "222")
else if (primeCounts[3] >= 1 && primeCounts[2] >= 1) // p % 6
getCombinations(primeCounts - {[2]->1,[3]->1}, numSoFar-1, str + "6")
getCombinations(primeCounts - {[2]->1,[3]->1}, numSoFar-2, str + "23")
else if (primeCounts[2] >= 2) // p % 4
getCombinations(primeCounts - [2]->2, numSoFar-1, str + "4")
getCombinations(primeCounts - [2]->2, numSoFar-2, str + "22")
else if (primeCounts[3] >= 1) // p % 3
getCombinations(primeCounts - [3]->1, numSoFar-1, str + "3")
else if (primeCounts[2] >= 1) // p % 2
getCombinations(primeCounts - [2]->1, numSoFar-1, str + "2")
else ;// do nothing, too few digits
Given the order in which things are done, I don't think there would be duplicates.
Improvement:
You needn't look at p%7 again (deeper down the stack) once you've looked at p%5, since we know it can't be divisible by 7 any more, so a lot of those checks can be optimised away.
primeCounts needn't be a map, it can just be an array of length 4, and it needn't be copied, one can just increase and decrease the values appropriately. Something similar can be done with str as well (character array).
If there were too many digits for getCombinations(..., str + "8"), there's no point in checking "24" or "222". This and similar checks shouldn't be too difficult to implement (just have the function return a bool).

Resources