簡體   English   中英

從每個集合中選擇一個項目而不重復 - 找到所有解決方案

[英]Choose One item from each Set without Duplication -- find all Solutions

我查看了多個論壇,似乎無法找到我正在尋找的確切內容。 我想生成所有可能的解決方案,我從每組數字中選擇一個數字,其中一個解決方案包含所有存在的數字並且不 A)重復數字和 B)從一組中提取多個數字。 舉一個簡單的例子,考慮我有這五組:

A: {1, 2, 3, 4, 5} 
B: {1, 2, 4}
C: {2, 3, 5}
D: {1, 3, 4, 5}
E: {3, 4}

可能的解決方案如下所示:

{A:1, B:2, C:3, E:4, D:5}
{D:1, C:2, E:3, B:4, A:5}
...
etc.

如您所見,所有數字1-5僅使用一次,所有 AE 集合僅使用一次。 我知道這里的解決方案少於1205! ),因為我無法找到 E2 或 D2 或 C4 等的解決方案。

對於諸如此類的簡潔示例,我可以手動完成工作,但我有興趣為一組50組執行此操作,每組最多包含50數字,全部在1-50范圍內。 有些類別看起來像A ,其中使用了范圍內的完整數字集,有些看起來像E ,其中只使用了幾個數字(例如, Set X 可能只是{11, 19} )。

所以,如果每組都有50數字,我就會有50! (我認為?)可能的解決方案。 不過,就我而言,我知道我可以排除 Set X 貢獻除1119以外的數字的任何解決方案,這大大減少了我的解決方案空間,尤其是當這些類別中的大多數只有少數可能的貢獻時。

有沒有一種簡單的方法來生成解決方案,或者至少計算解決方案的數量?

我想我可以:

1) sort all the sets from smallest length to largest length
2) choose a number from the first set (which may only have 1 possible choice)
3) eliminate that number from the remaining sets
4) move to the next set: If it has `0` elements, there is no possible solution using the previous selection of numbers
5) Repeat steps `1-4` until all sets have contributed a number: that is a solution (either print in full or add 1)

理論上,我會有(n1)\*(n2-1)\*(n3-1)\*....\*(1)可能的解決方案(其中n1n2等是該集合的大小),但是我被絆倒的地方是當我考慮到集合做和不取其他集合有的數字的時間時,因為我不能有重復。 例如,給定 Set A 有每個數字: {1, 2, ..., 50}並且 Set B 只有數字1-25{1, 2, ..., 25} ,如果我選擇一個數字>25來自 A 組,那么我有25種可能的選擇來自 B 組。如果我從 A 組中選擇一個<=25的數字,那么我只有24種可能的選擇來自 B 組。我不知道如何在我的計算,尤其是當我將此趨勢外推到多組數字時。

我有一種感覺,可能的解決方案的數量對於我的計算機來說太多了,但我至少想知道有多少 如果這對計算有用的話,我有所有50組數字。 任何幫助將不勝感激。 謝謝!

編程版本(僅適用於小型數據集)可能是反轉映射,生成所有組合並確保我們有一組大小為 5:

d = {'A': {1, 2, 3, 4, 5},
     'B': {1, 2, 4},
     'C': {2, 3, 5},
     'D': {1, 3, 4, 5},
     'E': {3, 4}}

from collections import defaultdict
from itertools import product

d2 = defaultdict(set)
for k, s in d.items():
    for v in s:
        d2[v].add(k)
# {1: {'A', 'B', 'D'},
#  2: {'A', 'B', 'C'},
#  3: {'A', 'C', 'D', 'E'},
#  4: {'A', 'B', 'D', 'E'},
#  5: {'A', 'C', 'D'}}

out = [x for x in product(*d2.values()) if len(set(x))==len(d2)]

output:

[('A', 'C', 'E', 'B', 'D'),
 ('A', 'B', 'E', 'D', 'C'),
 ('A', 'B', 'D', 'E', 'C'),
 ('A', 'B', 'C', 'E', 'D'),
 ('D', 'A', 'E', 'B', 'C'),
 ('D', 'C', 'E', 'B', 'A'),
 ('D', 'B', 'E', 'A', 'C'),
 ('D', 'B', 'A', 'E', 'C'),
 ('D', 'B', 'C', 'E', 'A'),
 ('B', 'A', 'E', 'D', 'C'),
 ('B', 'A', 'D', 'E', 'C'),
 ('B', 'A', 'C', 'E', 'D'),
 ('B', 'C', 'E', 'A', 'D'),
 ('B', 'C', 'E', 'D', 'A'),
 ('B', 'C', 'D', 'E', 'A'),
 ('B', 'C', 'A', 'E', 'D')]

首先,值得一提的是,術語組合在這里並不適用。

組合是從具有不同成員的集合中選擇的項目

排列表示排列組合元素的不同方式。

關鍵點在於,組合是在單一數據源上進行的選擇。 同時,我們有多個集合。

如果你注意結果,問題中提供的樣本數據預計會給出,你會發現所有這些“組合”都由完全相同的元素1,2,3,4,5組成(因為你'在所有集合中只使用了這些數字),但組合不能由完全相同的元素組成。

需要生成的序列與笛卡爾積非常相似,但需要注意的是:由於唯一性要求,我們需要丟棄其中的一些。 所以這個詞可能也不合適。 因此,我將它們稱為樣本。

我們可以通過以下步驟解決問題:

  • 創建一個包含所有結果樣本Queue 每個樣本將由一個Set表示。
  • 將第一個空樣本添加到隊列中。
  • 遍歷給定的Set Collection ,並為基於隊列中現有樣本的每個集合生成新樣本,其中包含當前集合中以前不存在的所有元素。 如果沒有此類元素,則應丟棄樣品

實現可能如下所示:

public static Queue<Set<Integer>> findSamples(List<Set<Integer>> sets) {
    
    Queue<Set<Integer>> samples = new ArrayDeque<>(); // queue of samples
    samples.add(Collections.emptySet());              // the fist empty sample
    
    for (Set<Integer> set : sets) {
        Queue<Set<Integer>> updated = new ArrayDeque<>(); // updated samples removed from the queue would be accumulated here
        
        while (!samples.isEmpty()) {
            Set<Integer> sample = samples.remove();
            updated.addAll(tryAdd(sample, set));
        }
        
        samples = updated;
    }
    
    return samples;
}

public static List<Set<Integer>> tryAdd(Set<Integer> sample, Set<Integer> set) {
    List<Set<Integer>> newSamples = new ArrayList<>();
    
    for (Integer next : set) {
        if (sample.contains(next)) continue;
        
        Set<Integer> copy = new LinkedHashSet<>(sample); // NB: HashSet would be sufficient here, LinkedHashSet is used ONLY for demonstration purposes
        copy.add(next);
        newSamples.add(copy);
    }
    return newSamples;
}

main()

public static void main(String[] args) {
    List<Set<Integer>> sets = List.of(
        Set.of(1, 2, 3, 4, 5),
        Set.of(1, 2, 4),
        Set.of(2, 3, 5),
        Set.of(1, 3, 4, 5),
        Set.of(3, 4)
    );
    
    findSamples(sets)
        .forEach(System.out::println); // printing the result
}

Output:

[1, 4, 2, 5, 3]
[1, 2, 5, 4, 3]
[1, 2, 5, 3, 4]
[1, 2, 3, 5, 4]
[5, 4, 2, 1, 3]
[5, 2, 3, 1, 4]
[5, 1, 2, 4, 3]
[5, 1, 2, 3, 4]
[4, 2, 5, 1, 3]
[4, 1, 2, 5, 3]
[3, 2, 5, 1, 4]
[3, 1, 2, 5, 4]
[2, 4, 5, 1, 3]
[2, 1, 5, 4, 3]
[2, 1, 5, 3, 4]
[2, 1, 3, 5, 4]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM