当前位置: 首页 > 知识库问答 >
问题:

查找最小数量的子集的算法,其中每个集合的总和不超过最大值

郑狐若
2023-03-14

给定一个正整数数组,找出子集的最小数目,其中:

  1. 子集中每个元素的总和不超过一个值,k。
  2. 数组中的每个元素在任何子集中只使用一次
  3. 数组中的所有值都必须出现在任何子集中。

基本上,一个“填充”算法,但需要最小化容器,并需要确保所有东西都被填满。我目前的想法是按降序排序,当总和超过k时开始创建集合,开始下一个,但不确定什么是更好的方法。

编辑:

Ex:

Inputs: arr = [1,2,3,4,5], k= 10
Output: [[1,4,5], [2,3]]
# Other solutions such as [[2,3,4],[1,5]] are also acceptable
# But the important thing is the number of sets returned is 2

在输出集合中,所有1-5都被使用,并且在集合中仅使用一次。希望这能解决问题。

共有1个答案

令狐献
2023-03-14

可能有一种更聪明的方法来找到最小数量的集合,但这里有一些代码使用Knuth的算法X来进行精确的覆盖操作,还有一个我去年编写的函数来生成总和小于给定值的子集。我的测试代码首先为问题中给出的数据找到一个解决方案,然后为一个更大的随机列表找到一个解决方案。它几乎可以立即找到最大和为10的[1,2,3,4,5]的解,但在我的旧32位2GHz机器上,要解决更大的问题几乎需要20秒。

这段代码只打印一个最小大小的解决方案,但修改它以打印最小大小的所有解决方案并不困难。

""" Find the minimal number of subsets of a set of integers
    which conform to these constraints:

    The sum of each subset does not exceed a value, k.
    Each element from the full set is only used once in any of the subsets.
    All values from the full set must be present in some subset.

    See https://stackoverflow.com/q/50066757/4014959

    Uses Knuth's Algorithm X for the exact cover problem,
    using dicts instead of doubly linked circular lists.
    Written by Ali Assaf

    From http://www.cs.mcgill.ca/~aassaf9/python/algorithm_x.html
    and http://www.cs.mcgill.ca/~aassaf9/python/sudoku.txt

    Written by PM 2Ring 2018.04.28
"""

from itertools import product
from random import seed, sample
from operator import itemgetter

#Algorithm X functions
def solve(X, Y, solution):
    if X:
        c = min(X, key=lambda c: len(X[c]))
        for r in list(X[c]):
            solution.append(r)
            cols = select(X, Y, r)
            yield from solve(X, Y, solution)
            deselect(X, Y, r, cols)
            solution.pop()
    else:
        yield list(solution)

def select(X, Y, r):
    cols = []
    for j in Y[r]:
        for i in X[j]:
            for k in Y[i]:
                if k != j:
                    X[k].remove(i)
        cols.append(X.pop(j))
    return cols

def deselect(X, Y, r, cols):
    for j in reversed(Y[r]):
        X[j] = cols.pop()
        for i in X[j]:
            for k in Y[i]:
                if k != j:
                    X[k].add(i)

#Invert subset collection
def exact_cover(X, Y):
    newX = {j: set() for j in X}
    for i, row in Y.items():
        for j in row:
            newX[j].add(i)
    return newX

#----------------------------------------------------------------------

def subset_sums(seq, goal):
    totkey = itemgetter(1)
    # Store each subset as a (sequence, sum) tuple
    subsets = [([], 0)]
    for x in seq:
        subgoal = goal - x
        temp = []
        for subseq, subtot in subsets:
            if subtot <= subgoal:
                temp.append((subseq + [x], subtot + x))
            else:
                break
        subsets.extend(temp)
        subsets.sort(key=totkey)

    for subseq, _ in subsets:
        yield tuple(subseq)

#----------------------------------------------------------------------

# Tests

nums = [1, 2, 3, 4, 5]
k = 10
print("Numbers:", nums, "k:", k)

Y = {u: u for u in subset_sums(nums, k)}
X = exact_cover(nums, Y)
minset = min(solve(X, Y, []), key=len)
print("Minimal:", minset, len(minset))

# Now test with a larger list of random data
seed(42)
hi = 20
k = 2 * hi
size = 10

nums = sorted(sample(range(1, hi+1), size))
print("\nNumbers:", nums, "k:", k)

Y = {u: u for u in subset_sums(nums, k)}
X = exact_cover(nums, Y)
minset = min(solve(X, Y, []), key=len)
print("Minimal:", minset, len(minset))

输出

Numbers: [1, 2, 3, 4, 5] k: 10
Minimal: [(2, 3, 5), (1, 4)] 2

Numbers: [1, 2, 3, 4, 8, 9, 11, 12, 17, 18] k: 40
Minimal: [(1, 8, 9, 18), (4, 11, 17), (2, 3, 12)] 3
 类似资料:
  • 出身背景我有数字1到20(黑色背景上的白色数字),可以出现在屏幕上,我希望识别这些数字。由于它们不能简单地复制粘贴,我将比较屏幕上数字的白色像素位置与所有20个数字的白色像素位置列表。然而,每个数字可以有大量的像素,并且可能不需要比较所有这些像素来识别该数字。因此,我希望尽可能少地进行比较。 算法问题:我有多个集合,其中的元素在每个集合中是唯一的,但在所有集合中可能不是唯一的。如何找到每个集合的最

  • 我想从数组的一部分找到最大值和最小值。我知道我可以通过复制数组将所需的数组部分复制到另一个数组中,但只是想知道是否可以不复制数组,因为我必须为不同的子数组进行循环 例如: 现在我想从1到4找到子数组的最小/最大值(如果可能,不复制子数组)

  • 我试图找到最小的整数中的只是使用两个简单的循环。我最初尝试了一个循环,但是它没有正确更新。我还没有学习,所以这应该在不使用任何代码的情况下完成。 这就是我所拥有的: 我的最小值似乎总是列表中的最后一个值,所以我尝试打印以查看发生了什么,它似乎会更新不同的值,最后更新最后一个值。我不知道为什么会这样。 这是它正在打印的内容,例如:

  • 假设我有一个包含整数的数组。 如何找到大小的子集,使得子集中所有整数对之间的距离,我的意思是它们在最远的距离。 示例:数组和, ,最小距离为10和6之间的<错误的子集: ,最小距离为 ,最小距离为 我想到了一个解决办法: 1) 排序数组2)选择一个[0],现在在数组中查找ceil(a[0])=Y。。。。然后ceil(Y

  • 我有以下问题要解决: 给定一组整数,例如{1,3,2}和一个随机整数数组,例如。 找出包含集合中所有值的最短连续子数组。如果找不到子数组,则返回一个空数组。 结果: 或者 < code>[1,2,2,-5,-4,3,1,1,2,0],{1,3,2}。 结果: 我已经尝试了以下方法,我的第二个循环似乎有问题。我不确定我需要更改什么:

  • 我有一个查询,然后使用foreach循环。 我通过消除空值进行筛选,然后将其推入集合 然后将二维阵列与一维阵列进行平面化转换。 所以,我有收集格式的数据。 我的问题是如何从收集中获得最小最大值? 例如我的数据: 预期产出: 代码: 如有任何帮助,我将不胜感激