【问题标题】:fill a one dimension space with predefined values [closed]用预定义的值填充一维空间
【发布时间】:2017-06-30 17:20:20
【问题描述】:

我想用预定义的“subLengths”填充一个长度。 假设我的 subLength 是:3,4,5,6,7,10。 要填充 15 的长度,我可以使用 "10+5" 、 "3+4+3+5" 、 "7+4+4" 、 "7+5+3" .... 我怎样才能得到这些结果中的一个数组? 更好:我怎样才能得到一个数组以获得几个好的结果?我的最大长度是 70,我想获得这个值的所有良好结果可能会很耗时。

我是一名 3D 艺术家,我的编码技能非常有限,我只是不知道如何处理这个问题。我可以使用 Python 或类似 C 的语言。

这段代码似乎可以在我的软件中使用:

def fillBuild(length, subLengths):

    for i in range(len(subLengths)):        
            if subLengths[i] == length:
                    yield subLengths[i:i + 1]
            elif subLengths[i] < length:
                    for subResult in fillBuild(length - subLengths[i] ,subLengths[i:] ):
                            yield subLengths[i:i + 1] + subResult

【问题讨论】:

  • 您想获得所有个总和吗?所有的价值观都是正面的吗?
  • “语言看起来像 C” - 不是很有帮助。我猜它是 MEL(如果您使用的是 Maya)或 MaxScript(如果您使用的是 3DsMax)。
  • 所以可以重复使用元素?
  • 我可以重复使用元素。我使用的 C 语言是 Houdini 中的 Vex。所有的值都是积极的。我不需要获得所有金额。

标签: python c algorithm


【解决方案1】:

递归生成器函数 (Python) 产生 pool 的所有可能的子列表排列(重复)加起来 total

from pprint import pprint

def sub_lists(pool, total):
    for i in range(len(pool)):
        if pool[i] == total:
            yield pool[i:i + 1]
        elif pool[i] < total:
            for sub_list in sub_lists(pool, total - pool[i]):
                yield pool[i:i + 1] + sub_list

pprint(list(sub_lists([3, 4, 5, 6, 7, 10], 15)))
[[3, 3, 3, 3, 3],
 [3, 3, 3, 6],
 [3, 3, 4, 5],
 [3, 3, 5, 4],
 [3, 3, 6, 3],
 [3, 4, 3, 5],
 [3, 4, 4, 4],
 [3, 4, 5, 3],
 [3, 5, 3, 4],
 [3, 5, 4, 3],
 [3, 5, 7],
 [3, 6, 3, 3],
 [3, 6, 6],
 [3, 7, 5],
 [4, 3, 3, 5],
 [4, 3, 4, 4],
 [4, 3, 5, 3],
 [4, 4, 3, 4],
 [4, 4, 4, 3],
 [4, 4, 7],
 [4, 5, 3, 3],
 [4, 5, 6],
 [4, 6, 5],
 [4, 7, 4],
 [5, 3, 3, 4],
 [5, 3, 4, 3],
 [5, 3, 7],
 [5, 4, 3, 3],
 [5, 4, 6],
 [5, 5, 5],
 [5, 6, 4],
 [5, 7, 3],
 [5, 10],
 [6, 3, 3, 3],
 [6, 3, 6],
 [6, 4, 5],
 [6, 5, 4],
 [6, 6, 3],
 [7, 3, 5],
 [7, 4, 4],
 [7, 5, 3],
 [10, 5]]

【讨论】:

  • 感谢您的代码,它运行良好。现在,对于我的使用,当我增加 total 时,数字排列会很高。不管怎样,你让我发现了不错的 yield 函数
【解决方案2】:

这是一个递归解决方案,使用 Python 2.7:

def fill(length, sublengths):
    # IMPORTANT: this function will produce INCORRECT RESULTS if sublengths
    # is not a list of unique integers sorted increasingly.

    fillings = []

    for i, sublength in enumerate(sublengths):

        if sublength > length:
            # if sublength is greater than length, there are no more allowable
            # fillings (because sublengths are unique and are sorted
            # increasingly), so we return the fillings collected so far;
            return fillings

        elif sublength == length:
            # if sublength is exactly equal to length, then only one filling is
            # possible, namely [sublength]; we append this filling to the
            # fillings;
            fillings.append([sublength])

       else:
            # we generate all the fillings that begin with sublength by
            # prepending sublength to all the allowable fillings of
            # (length - sublength), which we obtain by making a recursive call.
            fillings.extend([[sublength] + subresult
                             for subresult in
                             fill(length - sublength, sublengths[i:])])

例子:

In [2]: fill(15, [3, 4, 5, 6, 7, 10])
Out[2]: 
[[3, 3, 3, 3, 3],
 [3, 3, 3, 6],
 [3, 3, 4, 5],
 [3, 4, 4, 4],
 [3, 5, 7],
 [3, 6, 6],
 [4, 4, 7],
 [4, 5, 6],
 [5, 5, 5],
 [5, 10]]

顺便说一句:fill(70, [3, 4, 5, 6, 7, 10])) 产生 1657 种可能的填充物,因此您可能需要一些额外的标准来减少替代品。


一些注意事项:

  • 为了避免重复解决方案,我们将要求每次灌装时顺序递增;

  • 关键思想是这样的:假设要填充的长度是L,并且a1 a 2an 是可用的子长度。找出所有以a1开头的L可能的填充物,就等于在前面加上a1L - a1 的所有填充物。这是fillelse 块中递归调用的基本原理。 (当一个函数调用自己时,就像fill 所做的那样,该函数被称为递归。)

  • 由于fill 要求sublengths 不重复且排序越来越多,我们可以使用以下前端函数来确保满足这些条件:

    def do_fill(长度,子长度): 返回填充(长度,排序(设置(子长度)))


注意:下面是对代码作用的相当详细的解释。如果您已经理解代码,则可以放心地跳过本文的其余部分。)

为了更好地了解发生了什么,回到上面的例子,然后根据第一个子长度对解决方案进行分组;您将获得如下所示的三个组:

 # group I
 [3, 3, 3, 3, 3]
 [3, 3, 3, 6]
 [3, 3, 4, 5]
 [3, 4, 4, 4]
 [3, 5, 7]
 [3, 6, 6]

 # group II
 [4, 4, 7]
 [4, 5, 6]

 # group III
 [5, 5, 5]
 [5, 10]

现在,使用子长度 [3, 4, 5, 6, 7, 10] 比较第 I 组中的填充物,所有填充物均以 3 开头,与 15-3 = 12 的填充物:

In [3]: fill(15 - 3, [3, 4, 5, 6, 7, 10])
Out[3]: 
[[3, 3, 3, 3],
[3, 3, 6],
[3, 4, 5],
[4, 4, 4],
[5, 7],
[6, 6]]

如果现在你在所有这些填充物前加上 3,你将得到第一组的填充物。

现在考虑第 II 组中的填充物,所有填充物都以 4 开头。使用子长度 [4, 5, 6, 7, 10] 将它们与 15 - 4 = 11 的填充物进行比较:

In [4]: fill(15 - 4, [4, 5, 6, 7, 10])
Out[4]: 
[4, 7],
[5, 6]

同样,如果您在所有这些填充物前加上 4,您将得到第 II 组中的填充物。

您可能想知道,为什么我在上面对fill 的最后一次调用中使用 [4, 5, 6, 7, 10] 作为子长度,而不是 [3, 4, 5, 6, 7, 10] ?这是因为我只对越来越多的以 4 开头的馅料感兴趣。这排除了任何包括 3 的馅料。

最后,要获得第 III 组中的填充物,在 15 - 5 = 10 的所有填充物前面加上 5,使用子长度 [5, 6, 7, 10]:

In [5]: fill(15 - 5, [5, 6, 7, 10])
Out[5]: 
[[5, 5],
[10]]

如果您愿意,可以对每个子组重复相同类型的分析。例如,可以将fill(15 - 3, [3, 4, 5, 6, 7, 10])生成的填充物按照他们的第一个元素进行分组;你会得到 4 个组:

[3, 3, 3, 3]
[3, 3, 6]
[3, 4, 5]

[4, 4, 4]

[5, 7]

[6, 6]

这些组是通过将 3、4、5 或 6 分别添加到由

fill((15 - 3) - 3, [3, 4, 5, 6, 7, 10])
fill((15 - 3) - 4, [   4, 5, 6, 7, 10])
fill((15 - 3) - 5, [      5, 6, 7, 10])
fill((15 - 3) - 6, [         6, 7, 10])

上面的分析只是“手动”完成了 fill 函数所做的事情。

需要注意的重要一点是,每次递归调用,问题都会变得更简单。

例如,在生成填充 [3, 5, 7] 的过程中,对fill 的以下调用被执行:

fill(15,         [3, 4, 5, 6, 7, 10]) = fill(15, [3, 4, 5, 6, 7, 10])
fill(15 - 3,     [3, 4, 5, 6, 7, 10]) = fill(12, [3, 4, 5, 6, 7, 10])
fill(15 - 3 - 5, [      5, 6, 7, 10]) = fill( 7, [      5, 6, 7, 10])

特别注意最后一个,fill(7, [5, 6, 7, 10])。可以通过检查发现其解决方案:从子长度 [5, 6, 7, 10] 中只能填充 7 个。递归总是以这些平凡案例的解决方案结束。最终的解决方案是由这些琐碎的解决方案组合而成的。

【讨论】:

  • @kjo 谢谢你的解释,但我无法让它工作。 _ TypeError : 'Nonetype' 对象是可迭代的 _
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-10
  • 2018-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多