10

Sto cercando un algoritmo efficiente per calcolare le partizioni moltiplicative per ogni intero dato. Ad esempio, il numero di tali partizioni per 12 è 4, che sonoCome trovare partizioni moltiplicative di qualsiasi intero?

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

aver letto la wikipedia article per questo , ma questo non mi dà davvero un algoritmo per generare le partizioni (parla solo del numero di tali partizioni, e ad essere onesti, anche questo non mi è molto chiaro!).

Il problema che sto guardando mi impone di calcolare partizioni moltiplicativi per numeri molto grandi (> 1 miliardo), così mi stava cercando di venire con un approccio di programmazione dinamica per esso (in modo che la ricerca di tutte le possibili partizioni per un un numero più piccolo può essere riutilizzato quando quel numero più piccolo è di per sé un fattore di un numero maggiore), ma finora non so da dove cominciare!

Tutte le idee/suggerimenti sarebbe apprezzato - questo non è un problema a casa, solo una cosa che sto cercando di risolvere, perché sembra così interessante!

+0

Con stretti voti, ci idealmente dovrebbe essere una sorta di una spiegazione sul motivo per cui si pensa che questo deve essere chiuso, in modo che l'OP può rettificare le sue/suoi errori (se presente). L'elettore chiuso da solo può parlare per favore? – TCSGrad

+0

Chiusura ravvicinata senza spiegazioni - sempre amate quelle! – TCSGrad

+0

Ho espresso un voto ravvicinato per errore. Mie scuse. –

risposta

4

La prima cosa che farei è ottenere la fattorizzazione primaria del numero.

Da lì, posso effettuare una permutazione per ogni sottoinsieme dei fattori, moltiplicato per i fattori rimanenti a tale iterazione.

Quindi, se si prende un numero come 24, si ottiene

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 

Ripetere l'operazione per tutte le "ronde" (rotonda essendo il numero dei fattori nel primo numero della moltiplicazione), rimuovendo i duplicati come si presentano .

Così si finisce con qualcosa come

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

permutazione dei numeri che la moltiplicazione aggiungerà al numero originale. – DarthVader

+0

(permutazione? Combinazione? Ho dimenticato la parola corretta) dovrebbe essere permutazione. – DarthVader

+0

@glowcoder: Alcuni problemi - per un numero sufficientemente ampio che ha un sacco di fattori primi, gran parte del lavoro sarebbe stato fatto per identificare e rimuovere le partizioni duplicati. Stavo cercando un modo per superarlo durante la generazione stessa. Inoltre, cosa fa factor.permutate (2)? Non ho trovato alcuna API in AWL corrispondente, quindi mi chiedevo quale fosse il significato del parametro "2". – TCSGrad

0

Perché non si trovano tutti i numeri che possono dividere il numero e poi si trovano permutazioni dei numeri che moltiplicazioni si sommano al numero?

Trovare tutti i numeri che possono dividere il numero richiede O (n).

Quindi puoi permutare questo set per trovare tutti i set possibili che la moltiplicazione di questo set ti darà il numero.

Una volta trovato l'insieme di tutti i numeri possibili che dividono il numero originale, è possibile fare una programmazione dinamica su di essi per trovare l'insieme di numeri che moltiplicandoli ti darà il numero originale.

+0

"Una volta trovato insieme di tutti i possibili numeri che dividono il numero originale, allora si può fare programmazione dinamica su di loro" - speravo in un pizzico più specifica diversa da "fare programmazione dinamica" :).Per esempio, potresti dirmi come dovrei usare le partizioni per un intero più piccolo mentre calcoliamo le partizioni per un intero più grande? – TCSGrad

4

Ovviamente, la prima cosa da fare è trovare la fattorizzazione principale del numero, come indicato da glowcoder.Dire

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

Poi

  1. trovare le partizioni moltiplicativi di m = n/p^a
  2. per 0 <= k <= a, trovare le partizioni moltiplicativi di p^k, che è equivalente a trovare le partizioni additivi di k
  3. per ogni moltiplicativo partizione di m, trovare tutti modi distinti per distribuire a-k fattori p tra i fattori
  4. combinano risultati di 2. e 3.

È conveniente trattare le partizioni moltiplicativi come liste (o set) di (divisore) molteplicità coppie per evitare di produrre duplicati.

ho scritto il codice in Haskell, perché è il più conveniente e conciso delle lingue che conosco per questo genere di cose:

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) 

Non è particolarmente ottimizzato, ma fa il lavoro.

Alcune tempi e risultati:

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) 

La 10^k sono ovviamente particolarmente facile perché ci sono solo due numeri primi coinvolti (ma i numeri sono ancora più facili senza quadrati), i fattoriali ottenere lento in precedenza. Credo che da un'attenta organizzazione dell'ordine e la scelta di migliori strutture di dati di elenchi, c'è un bel po 'da guadagnare (probabilmente si dovrebbe ordinare i fattori primi di esponente, ma non so se si debba iniziare con i più alti esponenti o il più basso).

+0

Non conosco Haskell, ma presumo che tu abbia eseguito il tuo codice - ero interessato a sapere che tipo di tempo ci vuole per grandi numeri (ad esempio ~ 10.000.000.000)? Mi darebbe un'idea di cosa aspettarmi quando finalmente codificherò la mia soluzione in C++ ... – TCSGrad

+0

@ shan23 Ho aggiunto alcuni tempi. Come una congettura selvaggia, un fattore di miglioramento di 10 non sembra impossibile. –

+0

Questa è davvero una grande risposta (con i tempi) - lasciami provare C++ durante il fine settimana, e vedere se migliora. Inoltre, una query correlata: come si utilizzano le partizioni per $ n $ quando si calcolano le partizioni per un numero maggiore, uno dei cui fattori è $ n $? Sto cercando di ottenere le partizioni di una serie di numeri n ... m, quindi questo sarebbe particolarmente utile per me se riesco a capire un modo per quello! – TCSGrad