如何找到任何整数的乘法分区?

TCS*_*rad 13 algorithm combinatorics discrete-mathematics divide-and-conquer

我正在寻找一种有效的算法来计算任何给定整数的乘法分区.例如,12的这种分区的数量是4,即

12 = 12×1 = 4×3 = 2×2×3 = 2×6

我已经阅读了维基百科文章,但这并没有真正给我一个生成分区的算法(它只讨论了这些分区的数量,说实话,即使这对我来说也不是很清楚!) .

我正在看的问题要求我为非常大的数字(> 10亿)计算乘法分区,所以我试图为它提出一种动态编程方法(以便找到所有可能的分区,用于较小的数字可以是当较小的数字本身是一个较大数字的因素时重复使用),但到目前为止,我不知道从哪里开始!

任何想法/提示将不胜感激 - 这不是一个家庭作业问题,只是我试图解决的问题,因为它看起来很有趣!

cor*_*iKa 6

我要做的第一件事是获取数字的素因式分解。

从那里,我可以对因子的每个子集进行置换,然后乘以该迭代中的其余因子。

因此,如果您输入24之类的数字,

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
Run Code Online (Sandbox Code Playgroud)

对所有“回合”重复(回合是乘法的第一个数中的因子数),并在出现重复项时将其删除。

所以你最终会得到类似

// 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));
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*her 5

当然,首先要做的是找到数字的素因式分解,如glowcoder所说。说

n = p^a * q^b * r^c * ...
Run Code Online (Sandbox Code Playgroud)

然后

  1. 找到...的乘法分区 m = n / p^a
  2. 对于0 <= k <= a,找到的乘法分区p^k,等于找到的加法分区k
  3. 对于的每个乘法分区m,找到在a-k因子p之间分配因子的所有不同方法
  4. 合并2.和3.的结果

将乘法分区视为(除数,多重性)对的列表(或集合)以避免生成重复是很方便的。

我用Haskell编写了代码,因为它是我所知道的最方便,最简洁的语言:

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)
Run Code Online (Sandbox Code Playgroud)

它不是特别优化的,但是可以完成工作。

一些时间和结果:

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)
Run Code Online (Sandbox Code Playgroud)

10^k当然是特别容易,因为只有两个素数涉及(但无平方数仍比较容易),该阶乘很慢更早。我认为,通过精心组织顺序和选择比列表更好的数据结构,有很多收获(也许应该按指数对主要因素进行排序,但是我不知道应该从最高指数开始还是要从最高指数开始)。最低的)。