2010-10-18 65 views
7

Với Một tập hợp các số n[1], n[2], n[3], .... n[x] Và một số Mthuật toán để chọn một tập hợp các con số để đạt mức tối thiểu tổng

Tôi muốn tìm ra sự kết hợp tốt nhất của

n[a] + n[b] + n[c] + ... + n[?] >= M 

Sự kết hợp nên đạt đến mức tối thiểu cần thiết để tiếp cận hoặc vượt ra ngoài M mà không có kết hợp nào khác cho kết quả tốt hơn.

Sẽ làm điều này trong PHP để sử dụng thư viện PHP là ok. Nếu không, chỉ cần một thuật toán chung sẽ làm. Cảm ơn!

+0

Ví dụ tốt nhất tôi có thể nghĩ là Với phiếu mua hàng trị giá M, tôi đi đến cửa hàng và chọn sản phẩm sao cho tôi sử dụng đầy đủ phiếu thưởng và thanh toán tối thiểu tiền mặt để nạp tiền. Tôi cũng sẽ chọn kết hợp với số lượng sản phẩm ít nhất nếu có nhiều hơn 1 kết hợp tạo ra cùng một giá trị tổng. –

+0

Số lượng sản phẩm ít nhất để chọn sẽ là tổng {n [?] + N [c] + ...} khi tổng hợp> M. Tôi có sai không? – Chris

+0

tổng của nó> = M, ưu tiên chuyển đến tập hợp có sự khác biệt giữa tổng và M là nhỏ nhất. nếu hai bộ có cùng số tiền, tập hợp có số 'sản phẩm' ít nhất sẽ thắng –

Trả lời

1
pseudocode: 

list<set> results; 
int m; 
list<int> a; 

// ...  

a.sort(); 

for each i in [0..a.size] 
    f(i, empty_set);  

// ... 

void f(int ind, set current_set) 
{ 
    current_set.add(a[ind]); 

    if (current_set.sum > m) 
    { 
     results.add(current_set); 
    } 
    else 
    { 
     for (int i=ind + 1; i<a.size; ++i) 
     { 
      f(i, current_set); // pass set by value 

      // if previous call reached solution, no need to continue 
      if (a[ind] + current_set.sum) > m 
       break; 
     } 
    } 
} 

// choose whatever "best" result you need from results, the one 
// with the lowest sum or lowest number of elements 
+0

Điều này có thể làm việc không may không trả lại kết quả tối ưu. Ví dụ: M = 21, các số là 22, 15, 5, 3, 2, 2, 1. Nếu tôi không sai, mã sẽ trả về 2 kết quả [22] và [15, 5, 3, 2, 2]. Nhưng câu trả lời hay nhất là [15,5,3,2,1] mà sẽ không được xem xét? Đúng nếu tôi sai ở đây. –

+0

đã thêm một sửa chữa nhỏ, nhưng đối với một vấn đề khác. giải pháp đó sẽ làm việc, nó kinda đi qua tất cả các giải pháp có thể cắt ra các chi nhánh sai càng sớm càng tốt. vì vậy đối với các đầu vào của bạn, nó sẽ trả về [1, 2, 2, 3, 5, 15] [1, 2, 3, 5, 15] [1, 3, 5, 15] [1, 5, 15] [2, 2, 3, 5, 15] v.v. Chọn tập hợp có số tiền thấp nhất hoặc số phần tử thấp nhất hoặc bất kỳ tiêu chí "tốt nhất" nào của bạn. – Grozz

+0

Được quản lý để dịch mã này thành mã. Làm việc đủ tốt. Cảm ơn –

2

Tôi nghĩ rằng cách tiếp cận greedy algorithm sẽ hoạt động. Bạn mong đợi bao nhiêu số trong bộ này? Nếu nó là hợp lý thấp, bạn có thể thử backtrack, nhưng tôi sẽ không khuyên bạn nên nó cho bộ lớn.

+0

Cảm ơn. Tôi sẽ có một cái nhìn. Hiện tại, tôi đang xem một bộ có tối đa 136 giá trị (có khả năng tăng trưởng trong tương lai) –

+0

Bạn nên quay lại, nhưng cố gắng cải thiện thời gian thực hiện bằng cách sử dụng một số thủ thuật như sắp xếp đầu tiên và sau đó quay lại . Hãy cho tôi biết nếu bạn cần trợ giúp –

+0

Hiện tại, quay lại trông giống như cách tốt nhất để đi ... Tôi cần nghiên cứu thêm một chút để xem nó có hoạt động không. Cảm ơn –

0

Tôi nghĩ rằng đây là NP-hoàn thành (không thể tìm cách nhanh chóng để làm điều đó nói chung). Cách bạn đã đặt ra câu hỏi làm cho tôi nghĩ đến việc giải quyết liên tiếp Subset sum problems với số nguyên mục tiêu tăng đều từ M.

Trong ví dụ của bạn, không có phương pháp xác định có thể đạt được M hay không. Chỉ có một giải pháp brute-force sẽ cho bạn biết rằng nó không thể, và chỉ sau đó bạn có thể kiểm tra M + 1.

Tuy nhiên, bạn có thể muốn xem DP solution, vì điều này ít nhất sẽ cung cấp cho bạn ý tưởng về cách giải quyết vấn đề (mặc dù chậm). Ngoài ra còn có một giải pháp approximate sẽ là chính xác nếu số của bạn nhỏ. Sử dụng tính năng này. Cuối cùng, điều đáng chú ý là kích thước của bài toán được đo bằng số bit, không phải kích thước (hoặc tổng) của tập hợp.

Như đã đề cập ở trên, điều này có liên quan đến Knapsack problem.

1

Loại sự cố này được gọi là số nhị phân linear programming (trường hợp đặc biệt của lập trình tuyến tính số nguyên). Nó nổi tiếng là NP-hard - tức là không hiệu quả để giải quyết nói chung.

Tuy nhiên, có tồn tại những người giải quyết tốt, cả sử dụng thương mại và miễn phí, ví dụ: trình giải mã nguồn mở lpsolve mà bạn có thể gọi từ chương trình của mình.

/EDIT: Câu trả lời cũ là giường. Tôi nhầm lẫn các yếu tố.

+0

Tôi không nghĩ rằng đây là một vấn đề lập trình tuyến tính, vì bạn có thể có 0 hoặc 1 của mỗi số, với một vấn đề lập trình tuyến tính bạn có thể có nhiều phân số –

+0

@Nick: damn, bạn nói đúng. Đó là BLP. –

-1

Một giải pháp tham lam sẽ làm việc Một giả:

algo(input_set , target_sum, output_set) 
    if input_set is NULL 
     error "target_sum can never be reached with all input_set" 
     return 
    the_max = maximum(input_set) 
    remove the_max from input_set 
    if the_max > target_sum 
     algo(input_set, target_sum, ouptput_set) 
     return 
    add the_max to output_set 
    algo(input_set, target_sum - the_max, output_set) 

cuộc gọi với output_set = NULL. sắp xếp intput_set để có hiệu quả.

+0

Không phải là giải pháp cho vấn đề của op. – Grozz

+0

Tôi nghĩ rằng giải pháp này chỉ hoạt động nếu tôi muốn các giá trị tổng hợp chính xác target_sum. Tuy nhiên, đối với trường hợp của tôi, tổng số được phép vượt quá target_sum. –

0

Vấn đề này gần như chính xác là subset sum problem, có liên quan đến vấn đề Knapsack nhưng khác nhau và hoàn thành NP. Tuy nhiên, có một giải pháp tuyến tính nếu tất cả các số nhỏ hơn một số hằng số và một xấp xỉ thời gian Đa thức. Xem trang wikipedia được tham chiếu ở trên.

4

này trông giống như một Dynamic Programming vấn đề cổ điển (còn thể hiện bằng câu trả lời khác đề cập tương đồng so với 0-1 vấn đề Knapsack và tập hợp con Sum).Toàn bộ điều này chỉ là một lựa chọn đơn giản: đối với mỗi phần tử trong danh sách, chúng ta có sử dụng nó trong tổng của chúng ta hay không. Chúng tôi có thể viết lên một hàm đệ quy đơn giản để tính toán câu trả lời:

f(index,target_sum)= 
    0  if target_sum<=0 (i.e. we don't need to add anymore) 
    infinity if target_sum>0 and index is past the length of n (i.e. we have run out of numbers to add) 
    min(f(index+1,target_sum), f(index+1,target_sum-n[index])+n[index]) otherwise (i.e. we explore two choices - 1. take the current number 2. skip over the current number and take their minimum) 

Kể từ khi chức năng này có bài toán chồng chéo (nó khám phá những bài toán tương tự hơn và hơn nữa), nó là một ý tưởng tốt để memoize chức năng với bộ nhớ cache để giữ các giá trị đã được tính trước đó.

Dưới đây là mã trong Python:

#! /usr/bin/env python 

INF=10**9 # a large enough number of your choice 

def min_sum(numbers,index,M, cache): 
    if M<=0: # we have reached or gone past our target, no need to add any more 
     return 0 
    elif len(numbers)==index: # we have run out of numbers, solution not possible 
     return INF 
    elif (index,M) in cache: # have been here before, just return the value we found earlier 
     return cache[(index,M)] 
    else: 
     answer=min(
      min_sum(numbers,index+1,M,cache), # skip over this value 
      min_sum(numbers,index+1,M-numbers[index],cache)+numbers[index] # use this value 
     ) 
     cache[(index,M)]=answer # store the answer so we can reuse it if needed 
     return answer 

if __name__=='__main__': 
    data=[10,6,3,100] 
    M=11 

    print min_sum(data,0,M,{}) 

Giải pháp này chỉ trả về tổng mức tối thiểu, không phải là yếu tố thực tế sử dụng để thực hiện nó. Bạn có thể dễ dàng mở rộng ý tưởng để thêm ý tưởng đó vào giải pháp của mình.

0

Giải pháp tham lam hoạt động nếu câu hỏi yêu cầu số lượng mục tối thiểu. Nhưng hàm tối đa (..) sẽ tính đến khi M là âm cực đại nên trả về khoảng cách của số tới 0. (abs() của giá trị).

Hàm maximum() có thể được triển khai thông qua heap nhị phân. Độ phức tạp là O (n.logn).

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