Reputation: 32309
I would like to decompose a number into a tuple of numbers as close to each other in size as possible, whose product is the initial number. The inputs are the number n
we want to factor and the number m
of factors desired.
For the two factor situation (m==2
), it is enough to look for the largest factor less than a square root, so I can do something like this
def get_factors(n):
i = int(n**0.5 + 0.5)
while n % i != 0:
i -= 1
return i, n/i
So calling this with 120
will result in 10,12
.
I realize there is some ambiguity as to what it means for the numbers to be "close to each other in size". I don't mind if this is interpretted as minimizing Σ(x_i - x_avg)
or Σ(x_i - x_avg)^2
or something else generally along those lines.
For the m==3
case, I would expect that 336
to produce 6,7,8
and 729
to produce 9,9,9
.
Ideally, I would like a solution for general m
, but if someone has an idea even for m==3
it would be much appreciated. I welcome general heuristics too.
EDIT: I would prefer to minimize the sum of the factors. Still interested in the above, but if someone has an idea for a way of also figuring out the optimal m
value such that the sum of factors is minimal, it'd be great!
Upvotes: 10
Views: 2997
Reputation: 1
m=5 n=4 then m^(1/n)
you get:
Answer=1.495
then
1.495*1.495*1.495*1.495 = 5
in C#
double Result = Math.Pow(m,1/(double)n);
Upvotes: -2
Reputation: 12795
To answer your second question (which m
minimizes the sum of factors), it will always be optimal to split number into its prime factors. Indeed, for any positive composite number except 4
sum of its prime factors is less that the number itself, so any split that has composite numbers can be improved by splitting that composite numbers into its prime factors.
To answer your first question, greedy approaches suggested by others will not work, as I pointed out in the comments 4104
breaks them, greedy will immediately extract 8
as the first factor, and then will be forced to split the remaining number into [3, 9, 19]
, failing to find a better solution [6, 6, 6, 19]
. However, a simple DP can find the best solution. The state of the DP is the number we are trying to factor, and how many factors do we want to get, the value of the DP is the best sum possible. Something along the lines of the code below. It can be optimized by doing factorization smarter.
n = int(raw_input())
left = int(raw_input())
memo = {}
def dp(n, left): # returns tuple (cost, [factors])
if (n, left) in memo: return memo[(n, left)]
if left == 1:
return (n, [n])
i = 2
best = n
bestTuple = [n]
while i * i <= n:
if n % i == 0:
rem = dp(n / i, left - 1)
if rem[0] + i < best:
best = rem[0] + i
bestTuple = [i] + rem[1]
i += 1
memo[(n, left)] = (best, bestTuple)
return memo[(n, left)]
print dp(n, left)[1]
For example
[In] 4104
[In] 4
[Out] [6, 6, 6, 19]
Upvotes: 5
Reputation: 3534
How about this, for m=3 and some n:
For 336, the largest factor smaller than the cube root of 336 is 6 (I think). Dividing 336 by 6 gives 56 (another factor, go figure!) Performing the same math for 56 and looking for two factors, we get 7 and 8.
Note that doesn't work for any number with fewer than 3 factors. This method can be expanded for m > 3, maybe.
If this is right, and I'm not too crazy, the solution would be a recursive function:
factors=[]
n=336
m=3
def getFactors(howMany, value):
if howMany < 2:
return value
root=getRoot(howMany, value) # get the root of value, eg square root, cube, etc.
factor=getLargestFactor(value, root) # get the largest factor of value smaller than root
otherFactors=getFactors(howMany-1, value / factor)
otherFactors.insert(factor)
return otherFactors
print getFactors(n, m)
I'm too lazy to code the rest, but that should do it.
Upvotes: 1
Reputation: 3387
You can start with the same principle: look for numbers under or equal to the m
th root that are factors. Then you can recurse to find the remaining factors.
def get_factors(n, m):
factors = []
factor = int(n**(1.0/m) + .1) # fudged to deal with precision problem with float roots
while n % factor != 0:
factor = factor - 1
factors.append(factor)
if m > 1:
factors = factors + get_factors(n / factor, m - 1)
return factors
print get_factors(729, 3)
Upvotes: 1