【问题标题】:Splitting up a list into different length parts under special condition在特殊条件下将列表拆分为不同长度的部分
【发布时间】:2014-11-14 17:43:55
【问题描述】:

我需要一种算法,将不同的制造部件分成不均匀的组。主要条件是组中的最大数量与所有其他数量之间的差异应尽可能小。对于

示例:

如果我们有列表[1,3,4,11,12,19,20,21],并且我们决定将其分为 3 个部分,则应将其分为[1,3,4],[11,12],[19,20,21]。在同样的情况下,如果我们决定将其分成 4 份,我们会得到:

 [1,3,4],[11],[12],[19,20,21].

为了澄清“组中最大数量与所有其他人之间的差异” - [1,3,4] = 4 - 1 + 4 - 3 + 4 - 4 = 4,[11] = 11 - 11 = 0 ,[12,19] = 19 - 12 + 19 - 19 = 7 ,[20,21] = 21 -20 + 21 - 21 = 1. 总差 = 12. 在其他可能的情况下 [1,3,4 ] = 4 - 1 + 4 - 3 + 4 - 4 = 4,[11,12,19] = 19 - 11 + 19 - 12 + 19 - 19 = 12,[20,21] = 21 - 20 + 21 - 21 = 0。总差值 = 16。这是对性能过剩的计算。这是因为较大的数字(例如代表强度)需要替换组中的最小数字(最弱)。使用超强部件会太昂贵或太重,因此需要优化。

所以首先我想把列表分成所有可能的组合,然后计算“组中最大数量与组中所有其他人之间的差异”。然后选择最小差异最小的作为最终结果。

我想知道 python 或 Spyder 或类似中是否有一些内置函数。如果我需要写代码,你能帮我吗?

我正在尝试将随机列表分成 10 个,以便在不同情况下重新应用它。 l = sorted(random.sample(range(100), 10)).

【问题讨论】:

  • 您能否说明测量“组中最大数量与所有其他数量之间的差异”数量的精确公式是什么?例如,在您的“分成 4”示例中,为什么该解决方案是正确的,而不是 [1,3,4],[11],[12,19],[20,21]?而且,您如何解决问题对于给定的一组子列表长度有许多不同的解决方案。就像在分为 3 的示例中一样,您可以简单地说 [1,3,4],[11,12,19],[20,21]——它们的区别是什么?
  • 根据最终的标准,这可能是一个非常复杂的问题。对于不适用于分析解决方案的标准,您可以采用的一种方法是使用模拟退火。但是为此,您需要一种机制来从包含整数的集合空间中随机采样,这些整数加起来就是总长度。 As discussed here,一般来说这不是一个容易的问题。
  • 为了澄清“组中最大数量与所有其他人之间的差异” - [1,3,4] = 4 - 1 + 4 - 3 + 4 - 4 = 4,[11] = 11 - 11 = 0 ,[12,19] = 19 - 12 + 19 - 19 = 7 ,[20,21] = 21 -20 + 21 - 21 = 1. 总差 = 12. 在其他可能的情况下 [ 1,3,4] = 4 - 1 + 4 - 3 + 4 - 4 = 4,[11,12,19] = 19 - 11 + 19 - 12 + 19 - 19 = 12,[20,21] = 21 - 20 + 21 - 21 = 0。总差 = 16。这是对性能过剩的计算。

标签: python algorithm list optimization partitioning


【解决方案1】:

根据您更新的 cmets,听起来您正在寻找 K-Means 算法或类似的东西,它会根据您与建议中心的距离将您的列表元素分成不同的组(这就是您的差异计算真正测量)。

在您的标准中,请注意,从自身中减去每个子组的最大值是没有意义的,因为根据定义,它始终为零。因此,实际上您正在查看所有非最大元素的最大值减去每个元素的总和(如何处理重复项也是您需要回答的问题)。 K-Means 会做一些不同的事情(它会查看每个点与点平均值的距离),但在精神上它是相同的。您可以修改 k-means 以使用您的组分数的概念,尽管在聚类输出方面我并没有真正看到任何好处——我需要看到一些关于限制行为的数学证明要确信它很重要的不同标准。

您可以使用sklearnnumpy 模块轻松实现这一目标:

from sklearn import cluster as cluster
import numpy as np

km = cluster.KMeans(n_clusters=4)
example_data = np.asarray([1,2,3, 11,12, 20,21,22, 30,35])[:,None]

km.fit(example_data)

那就看km.labels_

In [65]: km.labels_
Out[65]: array([0, 0, 0, 3, 3, 1, 1, 1, 2, 2], dtype=int32)

您可以看到,这会将[1,2,3][11, 12][20, 21 , 22][30, 35] 放在一起。下面是一些实际为您获取此信息的代码:

In [74]: example_data.tolist()[0]
Out[74]: [1, 2, 3, 11, 12, 20, 21, 22, 30, 35]

In [75]: [[x for i,x in enumerate(example_data.tolist()[0]) if km.labels_[i] == j] 
          for j in range(km.n_clusters)]

Out[75]: [[1, 2, 3], [20, 21, 22], [30, 35], [11, 12]]

但请注意,这并不完美:它是一种迭代方法,不能保证收敛到任何“真实”解决方案,并且对于足够奇异的输入数据,您可以获得奇异的输出。

或者,对你想要的更基本的理解是选择索引整数i[0]i[k],这样

sub_lists[j] = original_list[i[j]:i[j+1]] 

i[0]=0i[k+1] 被理解为“列表中的所有其他内容”。然后定义:

sub_lens = [len(s) for s in sub_lists]
max_len  = max(sub_lens)
criterion(k, i[0], ..., i[k]) = max(max_len - s_len for s_len in sub_lens)

因此,您的解决方案是参数元组(k, i[0], ..., i[k]),并且您想要最小化上述表达式criterion 的选择。

这个问题的通用解决方案相当复杂。但如果你愿意接受除最终子列表外非常平衡的贪心解决方案,these solutions 中的许多人都会这样做。

【讨论】:

  • 我不想将它分成相同长度的列表,而是将列表分成给定数量的组(例如 4),组中的项目数量会不同。项目的数量取决于一个一般标准,即:最大值减去组中的每个元素应该尽可能小。 [1,3,4] = 4 - 1 + 4 - 3 + 4 - 4 = 4,[11] = 11 - 11 = 0 ,[12,19] = 19 - 12 + 19 - 19 = 7 ,[20 ,21] = 21 -20 + 21 - 21 = 1。总差 = 12。这是对性能过剩的计算。
  • 这将是一个难题。一种方法是使用 k-means 聚类算法。然后将每个点“分类”为属于一个集群。您可以对一系列集群执行此操作,并查看在您的标准下哪个给出最佳结果。一般来说,不会有一个简单的算法来解决这个问题,特别是因为集群的数量是可变的。
  • 我做了一个编辑来展示如何使用 k-means 来做到这一点,使用库 sklearnnumpy
  • EMS,看起来很有希望,你能放一个完整的代码让我自己检查 km.labels_ 和数组吗?我正在使用 Sypeder,所以我应该有我认为的 numpy 和 sklearn,但我仍然看不到。
【解决方案2】:

由于您没有提及开始切片背后的逻辑是什么,我建议使用此功能:

>>> def slicer(l,n):
...  le=len(l)
...  S=int(np.around(float(le)/n))
...  return [l[i:i+S] for i in range(0,le,S)]
... 
>>> slicer([1,3,4,11,12,19,20,21],2)
[[1, 3, 4, 11], [12, 19, 20, 21]]
>>> slicer([1,3,4,11,12,19,20,21],3)
[[1, 3, 4], [11, 12, 19], [20, 21]]
>>> slicer([1,3,4,11,12,19,20,21],4)
[[1, 3], [4, 11], [12, 19], [20, 21]]

在这里我使用numpy.aroundfloat(le)/n 进行四舍五入以获得真正的切片!

【讨论】:

  • 我想以所有可能的方式对其进行切片,然后进行比较,以实现差异最小的结果。从 itertools 导入链中导入随机螺丝 = 排序(random.sample(范围(10),4)),组合定义子集(arr):返回链(* [组合(arr,i + 1)为 i,a in enumerate( arr)]) def k_subset(arr, k): s_arr = sorted(arr) return list([(i) for i in combination(subsets(arr),k) if sorted(chain(*i)) == s_arr] ) 打印 k_subset(screws,3)
【解决方案3】:

编辑:根据已澄清的问题,这是另一种算法。我仍然保留下面的原始回复以防万一。

您可以使用动态规划解决问题。请注意,下面的代码并未针对速度进行优化,因为我认为这会使其难以理解。如果你仔细实现它,你可以在O(N * K)中进行,其中Na的长度,K是要分区的集合数。

a = [1,3,4,11,12,19,20,21]
S = []
K = 3

# memoize results in (len(a) + 1) by K array                                                                                                                             
memo_partitions = [[None for j in xrange(len(a) + 1)] for i in xrange(K + 1)]

def compute_cost(arr):
    # this is the objective to be minimized                                                                                                                              
    if len(arr) == 0:
        return 0
    return sum(arr[-1] - x for x in arr)

def compute_best_partition(k, n):
    # computes the best partition of the first `n` elements of `a`                                                                                                       
    # into `k` parts                                                                                                                                                     
    if n == 0:
        return [[] for _ in xrange(k)], 0
    if k == 1:
        return [a[:n]], compute_cost(a[:n])

    if memo_partitions[k][n] is not None:
        return memo_partitions[k][n]

    best_partition = [[] for _ in xrange(k - 1)] + [a[:n]]
    best_cost = compute_cost(a[:n])
    for i in xrange(1, n):
        last_group = a[i:n]
        additional_cost = compute_cost(last_group)
        partition, cost = compute_best_partition(k - 1, i)

        if cost + additional_cost < best_cost:
            best_partition = partition[:]
            best_partition.append(last_group)
            best_cost = cost + additional_cost

    memo_partitions[k][n] = (best_partition, best_cost)
    return memo_partitions[k][n]

best_partition, cost = compute_best_partition(K, len(a))
print best_partition

原始回复如下。

这里有两种方法可以满足您的需求。假设您的数字按升序排列,

a[0], a[1], ... , a[n - 1]

max_diff(S) 表示集合S 的两个元素之间的最大差异。我们想将数字分成集合S[0], ... , S[k - 1],这样max_diff(S[i]) 就很小。

首先,假设我们试图最小化max_diff(S[i]) 的总和。请注意,max_diff(S[i]) 的总和只是a[n - 1] - a[0] 减去S[i] 之间的“差距”。因此,您可以只找到a[i + 1] - a[i] 中最大的k - 1 并排除它们。在python代码中,

a = [1,3,4,11,12,19,20,21]
S = []
k = 3

diffs = [(a[i + 1] - a[i], i) for i in xrange(len(a) - 1)]
diffs.sort()
best_cuts = [i for diff, i in diffs[-k:]]
best_cuts.sort()

last_cut = 0
for cut in best_cuts:
    S.append(a[last_cut:cut + 1])
    last_cut = cut + 1
S.append(a[last_cut:])
print S

或者,假设我们试图最小化max_diff(S[i]) 的最大值。然后,我们可以对可实现的值进行二分搜索。在代码中,

a = [1,3,4,11,12,19,20,21]
S = []
k = 3

best_partition = None
low, high = 0, max(a)
while low < high:
    mid = (low + high) / 2

    # try to get all max_diffs <= mid                                                                                                                                    
    full_partition = []
    last_set = [a[0]]
    for val in a[1:]:
        if val > last_set[0] + mid:
            full_partition.append(last_set)
            last_set = [val]
        else:
            last_set.append(val)
    full_partition.append(last_set)

    if len(full_partition) > k:
        low = mid + 1
    else:
        high = mid
        best_partition = full_partition

S = best_partition
print S

【讨论】:

  • 我试图运行您的代码,但得到的答案是“无”。你能显示你的回复和完整的代码吗? a = [1,3,4,11,12,19,20,21] S = [] k = 3 def partition(a,k): diffs = [(a[i + 1] - a[i], i) for i in xrange(len(a) - 1)] diffs.sort() best_cuts = [i for diff, i in diffs[-k:]] best_cuts.sort() last_cut = 0 for cut in best_cuts: S .append(a[last_cut:cut + 1]) last_cut = cut + 1 S.append(a[last_cut:]) 打印分区(a,k)
  • 结果存储在S
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-08
  • 2020-10-01
  • 1970-01-01
  • 2014-06-29
  • 2021-04-06
相关资源
最近更新 更多