Algorithm 如何找到任何整数的乘法分区?
我正在寻找一种有效的算法来计算任意给定整数的乘法分区。例如,12的此类分区的数量为4,即 12=12x1=4x3=2x2x3=2x6 我已经阅读了这方面的文章,但这并没有真正给我一个生成分区的算法(它只讨论了这样的分区的数量,老实说,这对我来说还不是很清楚!) 我正在研究的问题要求我计算非常大的数(>10亿)的乘法分区,因此我试图提出一种动态规划方法(这样,当较小的数本身是较大数的一个因子时,可以重新使用较小数的所有可能分区),但到目前为止,我不知道从哪里开始Algorithm 如何找到任何整数的乘法分区?,algorithm,combinatorics,discrete-mathematics,divide-and-conquer,Algorithm,Combinatorics,Discrete Mathematics,Divide And Conquer,我正在寻找一种有效的算法来计算任意给定整数的乘法分区。例如,12的此类分区的数量为4,即 12=12x1=4x3=2x2x3=2x6 我已经阅读了这方面的文章,但这并没有真正给我一个生成分区的算法(它只讨论了这样的分区的数量,老实说,这对我来说还不是很清楚!) 我正在研究的问题要求我计算非常大的数(>10亿)的乘法分区,因此我试图提出一种动态规划方法(这样,当较小的数本身是较大数的一个因子时,可以重新使用较小数的所有可能分区),但到目前为止,我不知道从哪里开始 任何想法/提示都将不胜感激-这不是
任何想法/提示都将不胜感激-这不是家庭作业问题,只是我试图解决的问题,因为它看起来很有趣 我要做的第一件事是得到这个数的素因子分解 从那里,我可以对每个因子子集进行排列,乘以迭代中剩余的因子 所以如果你取一个像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
重复所有“轮数”(轮数是乘法第一个数中的因子数),在出现重复项时移除重复项
所以你最终会得到这样的结果
// 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));
}
}
//假设我们有素数分解
//以及要添加到的分区集
对于(int i=1;i
你为什么不找出所有能除以这个数的数,然后找出乘法加起来的数的排列
找到所有可以除以数字的数字需要O(n)
然后你可以排列这个集合,找到所有可能的集合,这个集合的乘法会给你这个数
一旦你找到了所有可能的数除以原始数的集合,你就可以对它们进行动态规划,找到将它们相乘得到原始数的一组数。当然,首先要做的是找到这个数的素数分解,就像glowcoder说的那样。说
n = p^a * q^b * r^c * ...
然后
m=n/p^a
0置换(置换?组合?我忘记了正确的单词),它应该是置换。@glowcoder:一些问题-对于一个有很多素数因子的足够大的数字,大部分工作将在识别和删除重复分区方面完成。我一直在寻找一种方法,在这一代人的成长过程中,克服这一点。还有,factors.permutate(2)的作用是什么?我在STL中没有找到任何与之对应的API,因此我想知道“2”参数的意义。@shan23-您可以进行一些优化,以减少其破坏性,但这是一个固有的昂贵操作。permutate(2)
是一个输入错误-它应该是permutate(i)
,不,我不相信它在任何STL库中。这是一个函数的伪代码,该函数将返回一个值列表,每个值的大小i
,完成所有可能的子列表。“一旦您找到所有可能的数字集,然后您可以对它们进行动态编程”-我希望得到一个比“进行动态编程”更具体的提示:。例如,你能告诉我,在计算较大整数的分区时,我应该如何使用较小整数的分区吗?对于接近投票,理想情况下应该有某种解释,说明为什么你认为这需要关闭,以便OP可以纠正他/她的错误(如果有)。请唯一的投票人大声说出来好吗?没有任何解释的投票-总是喜欢那些!我错误地投了一票。我很抱歉。@Mods,有没有办法修改错误的投票结果?shan23,这真的不应该造成问题;这一问题还需要几票才能结束。如果真的发生了这种情况,我会尽快重新投票。我不知道Haskell,但我想你已经运行了你的代码-我很想知道大数字(比如1000000000)需要多少时间?当我最终在C++中编码我的解决方案时,它会给我一个想法。……作为一个疯狂的猜测,10的改进因素看起来不可能。这是一个非常好的答案(用时间)-让我在周末尝试C++,看看它是否变得更好。另外,还有一个相关的查询——当计算一个更大的数(其中一个因子是$n$)的分区时,如何利用$n$的分区?我想得到一系列数字n…m的分区,所以如果我能找到一种方法的话,这对我特别有用!由于分区的形状仅取决于素因子分解中的指数,我们还可以重用n
的分区来生成m
的分区,如果n
不是m
的因子,那么我们所需要的就是素因子分解的合适结构。例如,12=2^2*3^1
,因此我们可以重用12的分区来查找90=2^1*3^2*5^1
的分区。我们只需要在12的分区中交换2和3,就可以得到18的分区,然后再加上5。如果您为p^k,p^a*q^b,p^a形式的数字存储分区*
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)
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)