【问题标题】:Find a subset of a set that create a balance set of value找到创建平衡值集的集合的子集
【发布时间】:2019-03-02 04:02:15
【问题描述】:

我知道标题有点含糊。请阅读更多详细信息。

输入

我有已知数量的可变长度集合(如 10000 个),每个集合都是英文字母表的一个子集。它看起来像这样:

a = ['a', 'b', 'c', 'a']
b = ['c', 'd', 'a', 'b']
c = ['x', 'y', 'z']
....

unique_value = set((*a, *b, *c, ...))
# {'a', 'b', 'c', 'd', 'e', 'f', ..., 'u', 'v', 'w', 'x', 'y', 'z'}

我需要什么

我需要从10000个以上的集合中选择一个固定数量的集合(比如100个),其中这个子集包含所有英文字符,并且每个字符的计数尽可能balancebalance 表示字符分布均匀。我知道很难选择完全均匀的分布,因此定义 balance criteria 也很重要。

我的问题

  1. 如何从原始集合中选择子集(具有上述属性)
  2. 平衡标准的定义

请建议我实现这一目标的方法。任何建议将不胜感激。
提前致谢!

【问题讨论】:

    标签: python algorithm


    【解决方案1】:

    我会尝试的一般算法是概率算法。我将创建一个从字符到子集 ID 的反向查找表,然后继续添加和删除子集以平衡固定数量的子集的 +0/+1。添加子集时,我会添加一个随机选择的包含最少填充字母的子集,删除时我会从包含最多填充字母的子集中进行选择。还应该存在很小的“变异”机会并选择一个完全随机的子集来添加/删除,以防止陷入局部最小值。

    我尝试编写此解决方案的代码,但由于我修复了边缘情况和错误,它很快就退化为一些意大利面条式代码。它远非完美的解决方案,甚至可能返回错误的答案,但至少它可能会给你一些想法。

    # Make lookup table
    lookup = defaultdict(set)
    for idx, subset in enumerate(subsets):
        for character in subset:
            lookup[character].add(idx)
    
    best_score, best_subsets = 1, None
    size = 10 # number of subsets to pick
    subset_indices = set() # subset_ids
    character_subsets = defaultdict(set) # subset_ids per letter
    # loop some large number of times
    for _ in range(10000):
        if len(subset_indices) > size: # remove elements
            idx = choice(list(subset_indices)) # maybe pick a random
            if random() < 0.9: # 90% chance pick an existing subset to remove
                indices = max(character_subsets.values(), key=len) # indices to pick from
                idx = choice(list(indices)) # pick one
            for character in subsets[idx]: # remove index/subset_id from lookup
                character_subsets[character].remove(idx)
            subset_indices.remove(idx) # remove subset_id from random draw pool
        else: # add a new subset
            idx = choice(list(set(range(len(subsets))) - subset_indices)) # invert random selection
            if random() < 0.9: # 90% chance to pick a new subset from the min populated
                i, indices = min(character_subsets.items(), key=lambda x:len(x[1]), default=(randint(0, len(lookup)-1),set()))
                indices = lookup[i] - indices # invert
                if not indices: continue # abort if empty
                idx = choice(list(indices)) # pick
            for character in subsets[idx]:
                character_subsets[character].add(idx) # update dict
            subset_indices.add(idx) # update random selection set
        score = pstdev(map(len, character_subsets.values())) # measure distribution
        if score < best_score and len(subset_indices) == size: # if better
            best_subsets = dict(character_subsets) # record it
            best_score = score
    
    # do logic to pretty-print or process best_subset however you like
    

    【讨论】:

    • 抱歉回复晚了。你能详细说明这部分吗? When adding subsets, I'd add a randomly selected subset that contains the least populated letter, and when removing I'd select from the subsets containing the most populated letter
    • @enamoria 你想平衡每个字母的出现次数,所以当我们添加一个新的子集时,我们会选择一个包含出现次数最少的字母的子集。但是,为了稍微“打乱”一些东西,希望它能够进入全局最小值,我们还删除了一个子集(为添加另一个腾出空间)。我们删除的这个子集应该是包含出现次数最多的字母的子集,以降低它并希望能平衡一下。
    • 在设计这个算法时,我从 walksat 中汲取了一些想法,如果这有助于让你对我的推理有任何直觉的话。
    猜你喜欢
    • 2018-07-28
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-23
    • 1970-01-01
    • 2012-02-09
    • 2017-06-12
    相关资源
    最近更新 更多