通过邻近将一组数字划分为集合

时间:2013-12-28 20:10:45

标签: arrays algorithm

假设我们有一个类似

的数组
[37, 20, 16, 8, 5, 5, 3, 0]

我可以使用什么算法,以便我可以指定分区的数量并将数组分解为它们。

对于2个分区,它应该是

[37] and [20, 16, 8, 5, 5, 3, 0]

对于3,它应该是

[37],[20, 16] and [8, 5, 5, 3, 0]

我能够通过简单地用左右数字减去元素来将它们分解,但这并不能确保正确的分区数。 有什么想法吗?

我的代码是ruby,但任何语言/算法/伪代码就足够了。


以下是Vikram算法的红宝石代码

def partition(arr,clusters)

# Return same array if clusters are less than zero or more than array size
return arr if (clusters >= arr.size) || (clusters < 0) 

edges = {}

# Get weights of edges
arr.each_with_index do |a,i|
  break if i == (arr.length-1)
  edges[i] = a - arr[i+1]
end

# Sort edge weights in ascending order
sorted_edges =  edges.sort_by{|k,v| v}.collect{|k| k.first}

# Maintain counter for joins happening. 

prev_edge = arr.size+1
joins = 0


sorted_edges.each do |edge|
    # If join is on right of previous, subtract the number of previous joins that happened on left
    if (edge > prev_edge)

        edge -= joins   
    end
            joins += 1
    # Join the elements on the sides of edge. 
    arr[edge] = arr[edge,2].flatten
    arr.delete_at(edge+1)

    prev_edge = edge

    # Get out when right clusters are done
    break if arr.size == clusters
end
end

3 个答案:

答案 0 :(得分:4)

(假设数组按降序排序)

37, 20, 16, 8, 5, 5, 3, 0

计算相邻数字之间的差异:

 17,  4,  8,  3, 0, 2, 3

然后按降序排序:

17, 8, 4, 3, 3, 2, 0

然后取前几个数字。例如,对于4个分区,请取3个数字:

17, 8, 4

现在查看原始数组并找到具有这些给定差异的元素(您应该将原始数组中的索引附加到差异数组中的每个元素以使其最简单)。

17 - difference between 37 and 20
8 - difference between 16 and 8
4 - difference between 20 and 16

现在打印东西:

37 | 20 | 16 | 8, 5, 5, 3, 0

答案 1 :(得分:1)

我认为使用kruskal算法的k-clustering可以解决你的问题。 Kruskal算法用于查找聚类,使它们之间存在最大间距。

算法: -

从您的数据集构建路径图,如下所示: -

  

[37,20,16,8,5,5,3,0]

     

路径图: - 0 - &gt; 1 - &gt; 2 - &gt; 3 - &gt; 4 - &gt; 5 - &gt; 6 - &gt; 7

     

然后每个边缘的权重将是它们的值之间的差异

  edge(0,1) = abs(37-20) = 17
  edge(1,2) = abs(20-16) = 4
  edge(2,3) = abs(16-8) = 8
  edge(3,4) = abs(8-5) = 3
  edge(4,5) = abs(5-5) = 0
  edge(5,6) = abs(5-3) = 2
  edge(6,7) = abs(3-0) = 3

在此图表上使用kruskal,直到只剩下k个簇: -

Sort the edges first according to weights in ascending order:-
     

(4,5),(5,6),(6,7),(3,4),(1,2),(2,3),(0,1)

     

使用krushkal确切找到k = 3个群集: -

   iteration 1 :  join (4,5) clusters = 7 clusters: [37,20,16,8,(5,5),3,0]
   iteration 2 :  join (5,6) clusters = 6 clusters: [37,20,16,8,(5,5,3),0]
   iteration 3 :  join (6,7) clusters = 5 clusters: [37,20,16,8,(5,5,3,0)]
   iteration 4 :  join (3,4) clusters = 4 clusters: [37,20,16,(8,5,5,3,0)]
   iteration 5 :  join (1,2) clusters = 3 clusters: [37,(20,16),(8,5,5,3,0)]
   stop as clusters = 3
     

重建解决方案:[(37),(20,16),(8,5,5,3,0)]是什么   你想要的

答案 2 :(得分:0)

虽然@ anatolyg的解决方案可能没问题,但你也应该看看k-means聚类。它通常在更高的维度上完成,但应该在1d内正常工作。

你选择k;你的例子是k = 2和k = 3。该算法试图将输入放入k个集合中,以最小化从集合的元素到集合的质心(平均位置)的平方距离之和。这为你对正确结果的模糊定义增加了一点严谨性。

虽然获得最佳结果是NP难,但有一个简单的贪心解决方案。

这是一个迭代。开始猜测。随机选择k个元素作为初始均值或将所有元素随机地放入k个集合并计算其均值。这里需要注意,因为k组中的每一组必须至少有一个元素。

此外,因为您的整数集可以有重复,所以您必须确保初始k均值是不同的。这很容易。只需从一组“不合格”中挑选。

现在迭代。对于每个元素找到最接近的平均值。如果它已经在与该均值相对应的集合中,请将其留在那里。不管怎么说吧。在考虑了所有元素之后,重新计算均值。重复,直到没有元素需要移动。

Wikipedia page对此非常好。