10

Estoy buscando un algoritmo eficiente para calcular las particiones multiplicativas para cualquier número entero dado. Por ejemplo, el número de tales particiones de 12 es 4, que son¿Cómo encontrar particiones multiplicativas de cualquier número entero?

12 = 12 x 1 = 4 x 3 = 2 x 2 x 3 = 2 x 6

He leído el wikipedia article para este , pero eso realmente no me da un algoritmo para generar las particiones (solo habla sobre el número de tales particiones, y para ser honesto, ¡incluso eso no es muy claro para mí!).

El problema que estoy buscando requiere que calcule particiones multiplicativas para números muy grandes (> 1 billón), así que estaba tratando de llegar a un enfoque de programación dinámica para que encontrara todas las particiones posibles para una un número más pequeño puede reutilizarse cuando ese número más pequeño es en sí mismo un factor de un número mayor), pero hasta ahora, ¡no sé por dónde empezar!

Cualquier idea/sugerencia sería apreciada - esto no es un problema de tarea, simplemente algo que estoy tratando de resolver porque parece ¡tan interesante!

+0

Con votos cercanos, idealmente debería haber algún tipo de explicación de por qué cree que esto debe cerrarse, para que el OP pueda rectificar sus errores (si corresponde). ¿Puede el votante cercano solitario hablar por favor? – TCSGrad

+0

Votos cerrados sin ninguna explicación, ¡siempre los amé! – TCSGrad

+0

Eché un voto de cerca por error. Mis disculpas. –

Respuesta

4

Lo primero que haría es obtener la factorización prima del número.

A partir de ahí, puedo hacer una permutación de cada subconjunto de los factores, multiplicado por los factores restantes en esa iteración.

Así que si usted toma un número como 24, se obtiene

2 * 2 * 2 * 3 // prime factorization 
a b c d 
// round 1 
2 * (2 * 2 * 3) a * bcd 
2 * (2 * 2 * 3) b * acd (removed for being dup) 
2 * (2 * 2 * 3) c * abd (removed for being dup) 
3 * (2 * 2 * 2) d * abc 

Repita para todas las "rondas" (siendo la vuelta de la serie de factores en el primer número de la multiplicación), la eliminación de duplicados a medida que surgen .

por lo que terminan con algo como

// assume we have the prime factorization 
// and a partition set to add to 
for(int i = 1; i < factors.size; i++) { 
    for(List<int> subset : factors.permutate(2)) { 
     List<int> otherSubset = factors.copy().remove(subset); 
     int subsetTotal = 1; 
     for(int p : subset) subsetTotal *= p; 
     int otherSubsetTotal = 1; 
     for(int p : otherSubset) otherSubsetTotal *= p; 
     // assume your partition excludes if it's a duplicate 
     partition.add(new FactorSet(subsetTotal,otherSubsetTotal)); 
    } 
} 
+0

permutación de los números que la multiplicación se sumará al número original. – DarthVader

+0

(¿permutación? ¿Combinación? Olvidé la palabra correcta) debería ser una permutación. – DarthVader

+0

@glowcoder: algunos problemas: para un número suficientemente grande que tiene muchos factores primos, gran parte del trabajo se haría para identificar y eliminar particiones duplicadas. Estaba buscando una forma de superar eso durante la generación misma. Además, ¿qué factores permuta (2) hacen? No encontré ninguna API en STL correspondiente a eso, por lo tanto, me preguntaba sobre la importancia del parámetro "2". – TCSGrad

0

¿Por qué no encontrar todos los números que se divida el número y luego te enteras permutaciones de los números que multiplicaciones se suman al número?

Encontrar todos los números que pueden dividir su número toma O (n).

Luego puede permutar este conjunto para encontrar todos los conjuntos posibles que la multiplicación de este conjunto le dará el número.

Una vez que encuentre el conjunto de todos los números posibles que dividen el número original, puede hacer una programación dinámica en ellos para encontrar el conjunto de números que al multiplicarlos le dará el número original.

+0

"Una vez que encuentre el conjunto de todos los números posibles que dividen el número original, entonces puede hacer una programación dinámica en ellos" - Esperaba una pista más específica que no sea "hacer programación dinámica" :).Por ejemplo, ¿podría decirme cómo debería usar las particiones para un número entero más pequeño al calcular las particiones para un entero más grande? – TCSGrad

4

Por supuesto, lo primero que debe hacer es encontrar la factorización prima del número, como dijo el glowcoder.Dicen

n = p^a * q^b * r^c * ... 

Entonces

  1. encuentran las particiones multiplicativos de m = n/p^a
  2. para 0 <= k <= a, encontrar las particiones multiplicativos de p^k, que es equivalente a encontrar las particiones aditivos de k
  3. para cada multiplicativo partición de m, encuentre todas las formas distintas de distribuir a-k factores p entre los factores
  4. combinar los resultados de 2. y 3.

Es conveniente para el tratamiento de las particiones multiplicativos como listas (o conjuntos) de (divisor, multiplicidad) pares para evitar la producción de duplicados.

He escrito el código en Haskell, porque es la más conveniente y concisa de las lenguas que conozco para este tipo de cosas:

module MultiPart (multiplicativePartitions) where 

import Data.List (sort) 
import Math.NumberTheory.Primes (factorise) 
import Control.Arrow (first) 

multiplicativePartitions :: Integer -> [[Integer]] 
multiplicativePartitions n 
    | n < 1  = [] 
    | n == 1 = [[]] 
    | otherwise = map ((>>= uncurry (flip replicate)) . sort) . pfPartitions $ factorise n 

additivePartitions :: Int -> [[(Int,Int)]] 
additivePartitions 0 = [[]] 
additivePartitions n 
    | n < 0  = [] 
    | otherwise = aParts n n 
     where 
     aParts :: Int -> Int -> [[(Int,Int)]] 
     aParts 0 _ = [[]] 
     aParts 1 m = [[(1,m)]] 
     aParts k m = withK ++ aParts (k-1) m 
      where 
      withK = do 
       let q = m `quot` k 
       j <- [q,q-1 .. 1] 
       [(k,j):prt | let r = m - j*k, prt <- aParts (min (k-1) r) r] 

countedPartitions :: Int -> Int -> [[(Int,Int)]] 
countedPartitions 0  count = [[(0,count)]] 
countedPartitions quant count = cbParts quant quant count 
    where 
    prep _ 0 = id 
    prep m j = ((m,j):) 
    cbParts :: Int -> Int -> Int -> [[(Int,Int)]] 
    cbParts q 0 c 
     | q == 0 = if c == 0 then [[]] else [[(0,c)]] 
     | otherwise = error "Oops" 
    cbParts q 1 c 
     | c < q  = []  -- should never happen 
     | c == q = [[(1,c)]] 
     | otherwise = [[(1,q),(0,c-q)]] 
    cbParts q m c = do 
     let lo = max 0 $ q - c*(m-1) 
      hi = q `quot` m 
     j <- [lo .. hi] 
     let r = q - j*m 
      m' = min (m-1) r 
     map (prep m j) $ cbParts r m' (c-j) 

primePowerPartitions :: Integer -> Int -> [[(Integer,Int)]] 
primePowerPartitions p e = map (map (first (p^))) $ additivePartitions e 

distOne :: Integer -> Int -> Integer -> Int -> [[(Integer,Int)]] 
distOne _ 0 d k = [[(d,k)]] 
distOne p e d k = do 
    cap <- countedPartitions e k 
    return $ [(p^i*d,m) | (i,m) <- cap] 

distribute :: Integer -> Int -> [(Integer,Int)] -> [[(Integer,Int)]] 
distribute _ 0 xs = [xs] 
distribute p e [(d,k)] = distOne p e d k 
distribute p e ((d,k):dks) = do 
    j <- [0 .. e] 
    dps <- distOne p j d k 
    ys <- distribute p (e-j) dks 
    return $ dps ++ ys 
distribute _ _ [] = [] 

pfPartitions :: [(Integer,Int)] -> [[(Integer,Int)]] 
pfPartitions [] = [[]] 
pfPartitions [(p,e)] = primePowerPartitions p e 
pfPartitions ((p,e):pps) = do 
    cop <- pfPartitions pps 
    k <- [0 .. e] 
    ppp <- primePowerPartitions p k 
    mix <- distribute p (e-k) cop 
    return (ppp ++ mix) 

no es especialmente optimizado, pero hace el trabajo.

Algunas veces y los resultados:

Prelude MultiPart> length $ multiplicativePartitions $ 10^10 
59521 
(0.03 secs, 53535264 bytes) 
Prelude MultiPart> length $ multiplicativePartitions $ 10^11 
151958 
(0.11 secs, 125850200 bytes) 
Prelude MultiPart> length $ multiplicativePartitions $ 10^12 
379693 
(0.26 secs, 296844616 bytes) 
Prelude MultiPart> length $ multiplicativePartitions $ product [2 .. 10] 
70520 
(0.07 secs, 72786128 bytes) 
Prelude MultiPart> length $ multiplicativePartitions $ product [2 .. 11] 
425240 
(0.36 secs, 460094808 bytes) 
Prelude MultiPart> length $ multiplicativePartitions $ product [2 .. 12] 
2787810 
(2.06 secs, 2572962320 bytes) 

El 10^k son, por supuesto, sobre todo fácil porque sólo hay dos números primos implicados (pero los números sin cuadrados son todavía más fácil), los factoriales consiguen lenta antes. Creo que mediante una organización cuidadosa del orden y la elección de mejores estructuras de datos que las listas, hay bastante que ganar (probablemente uno debería ordenar los factores primos por exponente, pero no sé si uno debe comenzar con los exponentes más altos o el más bajo).

+0

No conozco a Haskell, pero supongo que ha ejecutado su código. Me interesaba saber qué tipo de tiempo toma para números grandes (digamos ~ 10,000,000,000). Me daría una idea de qué esperar cuando finalmente codifico mi solución en C++ ... – TCSGrad

+0

@ shan23 He agregado algunos tiempos. Como una suposición salvaje, un factor de mejora de 10 no parece imposible. –

+0

Esa es una muy buena respuesta (con los tiempos) - déjame probar en C++ durante el fin de semana, y ver si mejora. Además, una consulta relacionada: ¿cómo se utilizarían las particiones para $ n $ cuando se calculan las particiones para un número mayor, uno de cuyos factores es $ n $? Estoy buscando obtener las particiones de una gama de números n ... m, ¡así que esto sería particularmente útil para mí si pudiera encontrar la forma de hacerlo! – TCSGrad

Cuestiones relacionadas