【问题标题】:Number clustering/partitioning algorithm数字聚类/分区算法
【发布时间】:2011-12-29 17:09:51
【问题描述】:

我有一个有序的一维数字数组。数组长度和数组中数字的值都是任意的。我想根据数值将数组划分为 k 个分区,例如假设我想要 4 个分区,分布为 30% / 30% / 20% / 20%,即首先​​是前 30% 的值,然后是接下来的 30%,等等。我可以选择 k 和分布的百分比。此外,如果相同的数字在数组中出现多次,则不应包含在两个不同的分区中。这意味着上面的分配百分比并不严格,如果您愿意,可以是“目标”或“起点”。

例如,假设我的数组是ar = [1, 5, 5, 6, 7, 8, 8, 8, 8, 8]

我选择k = 4,数字应该以pA = pB = pC = pD = 25%的百分比分布到分区A、B、C和D中。

鉴于我上面给出的约束,生成的分区应该是:

A = [1] B = [5, 5] C = [6, 7] D = [8, 8, 8, 8, 8]

结果(达到/纠正)百分比pcA = 10%, pcB = 20%, pcC = 20%, pcD = 50%

在我看来,我需要一种改进的 k-means 算法,因为标准算法不能保证尊重我的百分比和/或相同值不能在多个集群/分区中的要求。

那么,有这种聚类的算法吗?

【问题讨论】:

  • 如果指定 4 个分区并有一个数组 [ 1, 1, 1, 1, 1, 1, 1, 8],会发生什么?
  • 首先,您应该创建更多示例以明确要求。例如,当ar=[1,2,3,4,5,6,7,8,9,10] 时,您对 k=4、25% 的分布有何期望?
  • 您需要定义某种度量来量化特定分区与目标的接近程度。如果没有这样的措施,您将不知道哪种解决方案是“最好的”。天真的方法(根据原始百分比进行分区,然后移动分区边界以适应约束)总会给你一个解决方案,你只是不知道它有多好。
  • @Femaref 我也有同样的问题。要求来自客户,显然不是那么技术性的。我的猜测是他们会说数据数量足够多,以确保永远不会出现这种情况。我意识到这无助于正确制定算法。
  • @DocBrown 你的例子其实很简单。类似于A=[1,2], B=[3,4,5], C=[6,7], D=[8,9,10]A=[1,2,3], B=[4,5], C=[6,7,8], D=[9,10],但两者都是可以接受的。这取决于您如何对除法进行四舍五入。

标签: algorithm cluster-analysis partitioning


【解决方案1】:

这是一个动态规划解决方案,它可以找到一个分区,以最大限度地减少零件尺寸误差的平方和。因此,在您的 [1, 5, 5, 6, 7, 8, 8, 8, 8, 8] 示例中,您需要大小为 (2.5, 2.5, 2.5, 2.5) 的部分,并且此代码给出的结果是 ( 9.0,(1、2、2、5))。这意味着选择的分区大小为 1、2、2 和 5,总误差为 9 = (2.5-1)^2 + (2.5-2)^2 + (2.5-2)^2 + (2.5- 5)^2.

def partitions(a, i, sizes, cache):
    """Find a least-cost partition of a[i:].

    The ideal sizes of the partitions are stored in the tuple 'sizes'
    and cache is used to memoize previously calculated results.
    """
    key = (i, sizes)
    if key in cache: return cache[key]
    if len(sizes) == 1:
        segment = len(a) - i
        result = (segment - sizes[0]) ** 2, (segment,)
        cache[key] = result
        return result
    best_cost, best_partition = None, None
    for j in xrange(len(a) - i + 1):
        if 0 < j < len(a) - i and a[i + j - 1] == a[i + j]:
            # Avoid breaking a run of one number.
            continue
        bc, bp = partitions(a, i + j, sizes[1:], cache)
        c = (j - sizes[0]) ** 2 + bc
        if best_cost is None or c < best_cost:
            best_cost = c
            best_partition = (j,) + bp
    cache[key] = (best_cost, best_partition)
    return cache[key]


ar = [1, 5, 5, 6, 7, 8, 8, 8, 8, 8]
sizes = (len(ar) * 0.25,) * 4
print partitions(ar, 0, (2.5, 2.5, 2.5, 2.5), {})

【讨论】:

  • 看起来你正在做一些事情,保罗,谢谢。这是伪代码还是我不知道的一些新奇语言(Scala?)我会仔细研究一下并回复你。
  • 它是 python:它并不完全是新奇的,但它看起来确实像伪代码。
【解决方案2】:

聚类算法用于多维数据。对于一维数据,您应该简单地使用排序算法。

对数据进行排序。然后按照您的示例,将数据集从数组的底部线性分区到顶部。

【讨论】:

    【解决方案3】:

    天真的方法是这样的:

    假设 p1...pk 是分区的百分比 (p1+...+pk = 1)

    假设数组中有 N 个元素

    初始边界(其中有 k+1 个,包括数组结尾,因为您有 k 个分区)是: 0, p1*N, (p1+p2)*N, ..., N(需要四舍五入)。

    对于移动边界,您可以查看边界每一侧的两个数组元素(对于您可以移动的 k-1 个边界)。如果两个元素相等,则需要移动到边界,从左到右,至少直到满足约束。一种天真的方法是从左侧开始并进行最小的调整(只需将约束调整到导致移动最少的一侧,并且不要进一步移动边界)。

    虽然这个算法并没有覆盖整个分区空间。它只是给你一个解决方案。要找到最佳解决方案,您需要对整个分区空间进行强力搜索,并进行某种修剪(例如动态编程,您可以记住初始数组的子数组的最佳分区)。

    【讨论】:

    • 让我们在这样的场景中尝试您的算法:ar = [1, 8, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10]Pi=0.25k=4N=12。所以b0 = 0, b1 = 3, b2 = 6, b3 = 9, b4 = 12。我们显然不能改变 b0 或 b4 所以我们从b1 = 3 开始。 ar[3] = ar[2] = ar[4] = 9。我检查左边还是右边?如果我向左走,我将在 ar[0] 处到达 1,我的第一个边界将是 b1 = 8。如果我向右走,我将在 ar[7] 到达 10,我的第一个边界将是 b1 = 8
    • 很明显,如果我走对了,我将没有最佳解决方案,甚至没有关闭,因为我将无法继续通过 b1,我最终将只有 2 个分区。如果我向左走,我会有一个稍微好一点的分区,但仍然只有 2 个分区。相反,在ar = [1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 9, 10] 这样的场景中,我会遇到类似的问题。
    • 换句话说,当分布不均匀时,我不确定这种幼稚的方法是否有效。此外,向左或向右移动边界可能会对最终结果产生重大影响,在我看来,有人需要能够回溯并按照相反的方向重新开始。
    • 对 - 你的例子是这样的,只有一个解决方案甚至不接近预期的百分比。同样,要获得完整的解决方案,您需要探索分区空间。
    猜你喜欢
    • 2014-11-10
    • 2011-05-18
    • 1970-01-01
    • 2016-07-11
    • 2012-03-30
    • 2014-07-24
    • 2022-09-27
    • 2018-02-27
    • 1970-01-01
    相关资源
    最近更新 更多