Tìm tập hợp trọng số tối ưu để thêm vào một tập hợp trọng số nhất định


8

Trong thử thách này, bạn sẽ nhận được một danh sách các trọng số được phân tách bằng dấu phẩy làm đầu vào, chẳng hạn như

1,3,4,7,8,11

Và bạn phải xuất số lượng trọng lượng nhỏ nhất có thể thêm vào tập hợp đó. Ví dụ: đầu ra cho bộ này sẽ là

1,3,7

Bởi vì bạn có thể đại diện cho tất cả các trọng số đó chỉ với ba trọng số đó:

1     = 1
3     = 3
1+3   = 4
7     = 7
1+7   = 8
1+3+7 = 11

Có thể có nhiều hơn một giải pháp. Ví dụ: giải pháp của bạn cho đầu vào 1,2có thể là 1,1hoặc 1,2. Miễn là nó tìm thấy số lượng trọng lượng tối thiểu có thể đại diện cho bộ đầu vào, thì đó là một giải pháp hợp lệ.

Trọng lượng có thể không được sử dụng nhiều hơn một lần. Nếu bạn cần sử dụng một lần hai lần, bạn phải xuất nó hai lần. Ví dụ: 2,3không phải là giải pháp hợp lệ 2,3,5,7vì bạn không thể sử dụng 2hai lần cho 2+2+3=7.

Đầu vào được đảm bảo không có số trùng lặp.

Đây là nên mã ngắn nhất theo số ký tự thắng.

Truy cập mạng bị cấm (vì vậy không ai trong số "thông minh" của bạn wgetgiải pháp @JohannesKuhn ho ho );)

Trường hợp đơn giản nhất:

1,5,6,9,10,14,15               => 1,5,9
7,14,15,21,22,29               => 7,14,15
4,5,6,7,9,10,11,12,13,15,16,18 => 4,5,6,7
2,3,5,7                        => 2,2,3 or 2,3,7

Và một số cái khó hơn:

10,16,19,23,26,27,30,37,41,43,44,46,50,53,57,60,61,64,68,71,77,80,84,87
  => 3,7,16,27,34
20,30,36,50,56,63,66,73,79,86
  => 7,13,23,43
27,35,44,46,51,53,55,60,63,64,68,69,72,77,79,81,86,88,90,95,97,105,106,114,123,132
  => 9,18,26,37,42


@Jan, một sự khác biệt đáng kể là thử thách mà bạn trích dẫn được yêu cầu cho một bộ, trong khi thử thách này cho phép trùng lặp (ví dụ: 7,7,7,8ở trên), làm tăng độ phức tạp lên gấp nhiều lần.
Cary Swoveland

Chúng ta có thể giả sử các trọng số đầu vào là duy nhất (vì vậy chúng ta không phải loại bỏ các dups, đơn giản như vậy)? Ngoài ra, bạn có thể xem xét yêu cầu các giải pháp có thể giải quyết một trường hợp thử nghiệm nhất định; mặt khác, giải pháp ngắn nhất có thể là một điều tra viên vũ phu chỉ có thể giải quyết các vấn đề nhỏ (ví dụ: nếu có ntrọng số đầu vào và mlà lớn nhất, hãy liệt kê tất cả các chuỗi sau (1..m)và cho mỗi chuỗi sau, liệt kê mọi kết hợp giữa 1 và các ntrường hợp của mỗi yếu tố của chuỗi.)
Cary Swoveland

@CarySwoveland Đã chỉnh sửa cho phần "duy nhất". Tôi đã có trường hợp thử nghiệm.
Doorknob

Làm thế nào {7,7,7,8} có thể là một giải pháp? 8 không có trong bộ đầu vào.
DavidC

Câu trả lời:


3

Toán học 80 75

Cập nhật: Xem ở dưới cùng một bản cập nhật về bài kiểm tra cuối cùng đầy thách thức của Doorknob, được thêm vào ngày 5 tháng 11


Điều này vượt qua tất cả trừ bài kiểm tra cuối cùng. Tuy nhiên, nó không cố gắng sử dụng một chữ số nhiều hơn một lần. Và nó chỉ tìm kiếm từ các giải pháp là tập hợp con của tập dữ liệu lớn hơn.

Hàm tạo ra tất cả các tập hợp con của tập dữ liệu đầu vào và sau đó kiểm tra tập hợp con nào có thể được sử dụng để xây dựng tập hoàn chỉnh. Sau khi tìm thấy các tập con khả thi, nó chọn các tập nhỏ nhất.

s=Subsets
f@i_:=GatherBy[Select[s@i,Complement[i, Total /@ s@#]=={}&],Length]〚1〛

Xét nghiệm

f[{1, 3, 4, 7, 8, 11}]

{{1, 3, 7}}


f[{1, 5, 6, 9, 10, 14, 15}]

{{1, 5, 9}}


f[{7, 14, 15, 21, 22, 29}]

{{7, 14, 15}}


f[{4, 5, 6, 7, 9, 10, 11, 12, 13, 15, 16, 18}]

{{4, 5, 6, 7}}


f[{2, 3, 5, 7}]

{{2, 3, 5}, {2, 3, 7}}


Cập nhật

Dưới đây tôi sẽ cung cấp một phân tích ban đầu có thể giúp bắt đầu hướng tới một giải pháp.

Dữ liệu:

data = {10, 16, 19, 23, 26, 27, 30, 37, 41, 43, 44, 46, 50, 53, 57, 60, 61, 64, 68, 71, 77, 80, 84, 87};

Khác với cách tiếp cận trước đó, chúng tôi muốn xem xét, trong bộ giải pháp, các số KHÔNG xuất hiện trong tập dữ liệu.

Cách tiếp cận sử dụng sự khác biệt tuyệt đối giữa các cặp số trong tập dữ liệu.

g[d_] := DeleteCases[Reverse@SortBy[Tally[Union[Sort /@ Tuples[d, {2}]] /. {a_, b_} :> Abs[a - b]], Last], {0, _}] 

Hãy xem số lần mỗi sự khác biệt xuất hiện; chúng tôi sẽ chỉ lấy 8 trường hợp đầu tiên, bắt đầu từ sự khác biệt phổ biến nhất].

g[data][[1;;8]]

{{7, 14}, {27, 13}, {34, 12}, {3, 11}, {20, 10}, {16, 10}, {4, 10}, {11, 9}}

14 cặp khác nhau bởi 7; 13 cặp khác nhau bởi 27, và như vậy.

Bây giờ, hãy kiểm tra các tập hợp con bắt đầu bằng {differ1}, {differ1, Dif2}, v.v., cho đến khi chúng ta có thể hy vọng có thể tính đến tất cả các yếu tố ban đầu trong tập dữ liệu.

h cho thấy những con số từ tập hợp ban đầu không thể được xây dựng bằng cách tính tổng từ tập hợp con.

h[t_] := Complement[data, Total /@ Subsets@t]

Đến lần thử thứ năm, vẫn còn 10 yếu tố không thể hình thành từ {7, 27, 34, 3, 20}:

h[{7, 27, 34, 3, 20}]

{16, 19, 26, 43, 46, 53, 60, 77, 80, 87}

Nhưng trong lần thử tiếp theo, tất cả các số của tập dữ liệu được tính:

h[{7, 27, 34, 3, 20, 16}]

{}

Điều này vẫn không kinh tế như {3,7,16,27,34}, nhưng nó đã kết thúc.


Vẫn còn một số điều bổ sung cần tính đến.

  1. Nếu 1 nằm trong tập dữ liệu, nó sẽ được yêu cầu trong tập giải pháp.
  2. Cũng có thể có một số "kẻ cô độc" trong bộ gốc không thể được tạo ra từ những khác biệt phổ biến nhất. Chúng cần được đưa vào ngoài các thử nghiệm khác biệt.

Đây là nhiều vấn đề hơn tôi có thể xử lý tại thời điểm này. Nhưng tôi hy vọng nó làm sáng tỏ thử thách rất thú vị này.


hmm ... hiện đang phát minh ra testcase yêu cầu trùng lặp: P
Doorknob

Tôi sẽ để lại giải pháp của mình được đăng bây giờ và xem liệu tôi có thể thêm một điều kiện để kiểm tra các bản sao không.
DavidC

3
Nếu một giải pháp tồn tại khi trọng số wđược lặp lại, thì cùng một giải pháp với một trong những wthay đổi thành 2 * wcũng hoạt động, bởi vì bạn có thể sử dụng 2 * wmọi nơi bạn đã sử dụng w + wtrước đó. Điều này có thể được lặp lại cho đến khi giải pháp không có sự lặp lại. Do đó, bạn không cần phải cố gắng sử dụng lặp lại.
tông_box

Bạn không thực sự cần dấu ngoặc đơn. Thoát s=Subsets;khỏi chức năng
Tiến sĩ belisarius

Đúng về dấu ngoặc đơn.
DavidC

0

Hồng ngọc

Đây là một phép liệt kê thẳng, vì vậy nó sẽ thu được các giải pháp tối thiểu, nhưng có thể mất nhiều năm - có thể là năm nhẹ - để giải quyết một số vấn đề. Tất cả các "trường hợp đơn giản nhất" giải quyết tối đa trong vài giây (mặc dù tôi đã nhận được 7,8,14 và 1,2,4 cho trường hợp thứ 3 và thứ 5, tương ứng). Tricky # 2 đã giải quyết trong khoảng 3 giờ, nhưng hai cái còn lại thì quá lớn để liệt kê, ít nhất là cho con đường tôi đã đi.

Một mảng có kích thước ntạo ra mảng đã cho bằng cách tính tổng các tập hợp con của các phần tử của nó có kích thước tối thiểu nếu có thể chỉ ra rằng không có mảng kích thước < nnào làm được điều đó. Tôi không thể thấy cách nào khác để chứng minh sự tối ưu, vì vậy tôi bắt đầu liệt kê với các tập con có kích thước m, trong đó mgiới hạn dưới đã biết, và sau đó tăng kích thước lên m+1sau khi liệt kê các tập con có kích thước mvà cho thấy chúng không thuộc "khoảng" nào được đưa ra mảng, và như vậy, cho đến khi tôi tìm thấy một tối ưu. Tất nhiên, nếu tôi đã liệt kê tất cả các tập hợp con có kích thước n, tôi có thể sử dụng một heuristic cho kích thước n + 1, để nếu tôi tìm thấy một mảng bao trùm có kích thước đó, tôi sẽ biết nó là tối ưu. Bất cứ ai cũng có thể đề xuất một cách khác để chứng minh một giải pháp là tối ưu trong trường hợp chung?

Tôi đã bao gồm một vài kiểm tra tùy chọn để loại bỏ một số kết hợp sớm. Loại bỏ những kiểm tra sẽ lưu 87 ký tự. Chúng là như sau ( alà mảng đã cho):

  • một mảng kích thước ncó thể tạo ra hầu hết các 2^n-1số dương khác biệt; do đó, 2^n-1 >= a.sizehoặc n >= log2(a.size).ceil("giới hạn dưới" mà tôi đã đề cập ở trên).
  • một ứng cử viên tạo ra bkích thước ncó thể được loại trừ nếu:
    • b.min > a.min
    • sum of elements of b < a.max hoặc là
    • b.max < v, trong đó v = a.max.to_f/n+(n-1).to_f/2.ceil( to_fđang chuyển đổi để nổi).

Cái cuối cùng, được kiểm tra trước, thực hiện

sum of elements of b <= sum(b.max-n+1..b.max) < a.max

Lưu ý vlà không đổi cho tất cả các mảng máy phát có kích thước n.

Tôi cũng đã sử dụng quan sát rất hữu ích của @ tông_box rằng không cần phải xem xét các bản sao trong mảng tạo.

Trong mã của tôi,

(1..a.max).to_a.combination(n) 

tạo ra tất cả các kết hợp của các số từ 1 đến a.max, được thực hiện ntại một thời điểm (trong đó a.max = a.last = a[-1]). Đối với mỗi kết hợp b:

(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}

điền vào một hàm băm hvới tất cả các số được tính trên các tập con không trống của b. Các khóa băm là những con số đó; các giá trị là tùy ý. (Tôi đã chọn đặt cái sau về không.)

a.all?{|e|h[e]}}

kiểm tra xem mọi phần tử của mảng đã cho acó phải là khóa trong hàm băm ( h[e] != nilhoặc chỉ h[e]).

Giả sử

n = 3 and b=[2,5,7].

Sau đó, chúng tôi lặp lại trong phạm vi:

(1...2**8) = (1...8) # 1,2,..,7

Biểu diễn nhị phân của mỗi số trong phạm vi này được sử dụng để đâm ra các phần tử của btổng. Đối với j = 3( jlà chỉ số phạm vi),

3.to_s(2)                  # =>  "11"
"11".rjust(3,?0)           # => "011"
"011".split('')            # => ["0","1","1"]
[2,5,7].zip(["1","0","1"]) # => [[2,"0"],[5,"1"],[7,"1"]]
[[2,"0"],[5,"1"],[7,"1"]].reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0 # => t = 0+5+7 = 12 

Mật mã:

x=a[-1]
n=Math.log2(a.size).ceil
loop do
v=(1.0*x/n+(n-1)/2.0).ceil
(1..x).to_a.combination(n).each{|b|
next if b[-1]<v||b[0]>a[0]||b.reduce(&:+)<x
h={}
(1...2**n).each{|j|h[b.zip(j.to_s(2).rjust(n,?0).split('')).reduce(0){|t,(u,v)|t+(v==?1?u:0)}]=0}
(p b;exit)if a.all?{|e|h[e]}}
n+=1
end
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.