10

Tôi đang tìm một thuật toán hiệu quả để tính toán phân vùng nhân cho bất kỳ số nguyên nhất định nào. Ví dụ, số lượng các phân vùng như vậy trong vòng 12 là 4, đó làCách tìm phân vùng nhân của bất kỳ số nguyên nào?

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

Tôi đã đọc các wikipedia article cho điều này , nhưng điều đó không thực sự mang lại cho tôi một thuật toán để tạo phân vùng (nó chỉ nói về số lượng phân vùng như vậy, và thành thật mà nói, ngay cả điều đó cũng không rõ ràng đối với tôi!). Vấn đề tôi đang xem yêu cầu tôi tính toán phân vùng nhân với số lượng rất lớn (> 1 tỷ), vì vậy tôi đã cố gắng tìm ra phương pháp lập trình động cho nó (để tìm tất cả các phân vùng có thể cho một số nhỏ hơn có thể được sử dụng lại khi số đó nhỏ hơn là một yếu tố của một số lớn hơn), nhưng cho đến nay, tôi không biết bắt đầu từ đâu!

Bất kỳ ý tưởng/gợi ý nào sẽ được đánh giá cao - đây không phải là vấn đề về bài tập về nhà, chỉ là điều tôi đang cố gắng giải quyết vì nó có vẻ như thật thú vị!

+0

Với phiếu bầu, lý tưởng nên có một số giải thích về lý do tại sao bạn nghĩ rằng điều này cần phải được đóng lại, để OP có thể sửa chữa sai lầm của mình (nếu có). Người bầu cử gần như có thể nói chuyện được không? – TCSGrad

+0

Đóng phiếu bầu mà không có bất kỳ giải thích nào - luôn yêu thích những người đó! – TCSGrad

+0

Tôi bỏ phiếu kết thúc do lỗi. Lời xin lỗi của tôi. –

Trả lời

4

Điều đầu tiên tôi làm là lấy hệ số chính của số.

Từ đó, tôi có thể thực hiện hoán vị cho từng tập con của các yếu tố, nhân với các yếu tố còn lại tại lần lặp lại đó.

Vì vậy, nếu bạn thực hiện một số như 24, bạn sẽ có được

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 

Lặp lại cho tất cả các "vòng" (vòng là số yếu tố trong số đầu tiên của phép nhân), loại bỏ các bản sao khi họ đưa ra .

Vì vậy, bạn kết thúc với một cái gì đó giống như

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

hoán vị các con số nhân lên sẽ cộng số nguyên bản. – DarthVader

+0

(hoán vị? Combintion? Tôi quên từ thích hợp) nó nên được hoán vị. – DarthVader

+0

@glowcoder: Một số vấn đề - đối với một số lượng đủ lớn có nhiều yếu tố chính, phần lớn công việc sẽ được thực hiện trong việc xác định và xóa các phân vùng trùng lặp. Tôi đang tìm kiếm một cách để vượt qua điều đó trong chính thế hệ. Ngoài ra, factor.permutate (2) làm gì? Tôi không tìm thấy bất kỳ API trong STL tương ứng với điều đó, do đó đã tự hỏi về tầm quan trọng của tham số "2". – TCSGrad

0

Tại sao không bạn tìm thấy tất cả những con số mà có thể chia số lượng và sau đó bạn tìm hoán vị của những con số mà phép nhân sẽ thêm lên đến số lượng?

Tìm tất cả các số có thể chia số của bạn sẽ có O (n).

Sau đó, bạn có thể hoán vị bộ này để tìm tất cả các tập hợp có thể mà phép nhân của tập hợp này sẽ cung cấp cho bạn số.

Khi bạn tìm thấy tập hợp tất cả các số có thể chia số gốc, thì bạn có thể lập trình động trên chúng để tìm tập các số nhân với số sẽ cho bạn số nguyên gốc.

+0

"Khi bạn tìm thấy tập hợp tất cả các số có thể chia số gốc, thì bạn có thể lập trình động trên chúng" - Tôi đã hy vọng một gợi ý cụ thể hơn là "lập trình động" :).Ví dụ, bạn có thể cho tôi biết làm thế nào tôi nên sử dụng các phân vùng cho một số nguyên nhỏ hơn trong khi tính toán các phân vùng cho một số nguyên lớn hơn? – TCSGrad

4

Tất nhiên, điều đầu tiên cần làm là tìm số nguyên tố chính của số, như bộ phát sáng.Nói

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

Sau đó

  1. tìm ra phân vùng nhân của m = n/p^a
  2. cho 0 <= k <= a, tìm ra các phân vùng nhân của p^k, tương đương với việc tìm kiếm các phân vùng phụ gia của k
  3. cho mỗi nhân giống phân vùng của m, tìm tất cả các cách khác nhau để phân phối a-k các yếu tố.210 trong những yếu tố
  4. kết hợp kết quả 2. và 3.

Đó là thuận lợi để điều trị các phân vùng nhân giống như các danh sách (hoặc bộ) của (số chia, đa) cặp để tránh sản xuất bản sao.

Tôi đã viết mã trong Haskell bởi vì đó là thuận tiện nhất và ngắn gọn về các ngôn ngữ tôi biết điều này loại:

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) 

Nó không đặc biệt tối ưu hóa, nhưng nó không được công việc.

Một số lần và kết quả:

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) 

Các 10^k là tất nhiên đặc biệt dễ dàng vì chỉ có hai số nguyên tố liên quan (nhưng số squarefree vẫn còn dễ dàng hơn), các thừa được chậm trước đó. Tôi nghĩ rằng bằng cách tổ chức cẩn thận thứ tự và lựa chọn cấu trúc dữ liệu tốt hơn danh sách, có một chút để đạt được (có lẽ một nên sắp xếp các yếu tố chính theo số mũ, nhưng tôi không biết liệu có nên bắt đầu với số mũ cao nhất hay không thấp nhất).

+0

Tôi không biết Haskell, nhưng tôi cho rằng bạn đã chạy mã của mình - Tôi đã quan tâm đến việc biết thời gian cần thiết cho số lượng lớn (nói ~ 10.000.000.000)? Nó sẽ cho tôi một ý tưởng về những gì mong đợi khi tôi cuối cùng mã giải pháp của tôi trong C++ ... – TCSGrad

+0

@ shan23 Tôi đã thêm một số thời gian. Như một dự đoán hoang dã, một yếu tố của 10 cải tiến không nhìn không thể. –

+0

Thats một câu trả lời thực sự tuyệt vời (với thời gian) - hãy để tôi thử trên C + + vào cuối tuần, và xem nếu nó được tốt hơn. Ngoài ra, một truy vấn liên quan - làm thế nào người ta sẽ sử dụng các phân vùng cho $ n $ khi tính toán các phân vùng cho một số lớn hơn, một trong những yếu tố của nó là $ n $? Tôi đang tìm cách để có được các phân vùng của một loạt các số n ... m, do đó, điều này sẽ đặc biệt hữu ích cho tôi nếu tôi có thể tìm ra một cách cho điều đó! – TCSGrad

Các vấn đề liên quan