물론, 가장 먼저 할 일은 숫자의 소수 요소를 찾는 것입니다. 예를 들어 글로우 코더는 다음과 같이 말했습니다.
n = p^a * q^b * r^c * ...
그럼
- 각 곱셈위한
k
- 의 첨가제 파티션을 발견 등가 인
p^k
의 곱셈 파티션을 찾을 0 <= k <= a
위한 m = n/p^a
- 의 곱셈 파티션을 발견 말해
m
의 파티션은 배포 할 모든 뚜렷한 방법을 찾습니다. a-k
팩터요인
- 중 33,210 중복 제조 피하기 (제수, 다수) 쌍 목록 (또는 세트)로 곱셈 파티션을 치료하는 편리한 2. 3.
결과를 결합한다.
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)
10^k
은 계승 느린 이전 얻을 물론이 포함 된 두 개의 소수가 (그러나 squarefree 숫자는 여전히 쉽게) 때문에 특히 쉽다. 목록보다는 신중한 조직과 선택으로 더 나은 데이터 구조를 얻을 수 있다고 생각합니다. 아마도 소수점을 지수로 정렬해야하지만, 지수가 가장 높은 지수로 시작해야할지, 가장 낮은).
가까운 투표를 통해 OP가 자신의 실수 (있을 경우)를 수정할 수 있도록 왜 이것이 닫혀 야한다고 생각하는지에 대한 설명이 있어야합니다. 고독한 폐쇄 유권자가 말할 수 있습니까? – TCSGrad
아무런 설명없이 닫음표 - 항상 그 사람들을 사랑했습니다! – TCSGrad
나는 가까운 투표를 잘못했다. 내 사과. –