用于分隔相同类型的项的算法

时间:2012-09-11 18:35:13

标签: algorithm sorting

我有一个元素列表,每个元素都用类型标识,我需要将列表重新排序为最大化相同类型元素之间的最小距离。 / p>

该套装很小(10至30件),因此表现并不重要。

每种类型的物品数量或类型数量没有限制,数据可以被认为是随机的。

例如,如果我有一个列表:

  • A
  • 的5项
  • 3项B
  • C项
  • 2项
  • D项目
  • 1项E
  • F项目

我想生产类似的东西: ABCADFBA,{{ 1}},ECADB

  • A之间至少有两项出现
  • B之间至少有4个项目
  • C之间有6个项目
  • D之间有6个项目出现

是否有算法来实现这一目标?

-Update -

在交换了一些评论后,我得出了次要目标的定义:

  • 主要目标:最大限度地考虑相同类型元素之间的最小距离,仅考虑距离较短的类型。
  • 次要目标:最大化每个类型上元素之间的最小距离。 IE:如果组合增加某种类型的最小距离而不减少其他类型,则选择它。

- 更新2 -

关于答案。 有很多有用的答案,虽然没有一个是两个目标的解决方案,特别是第二个目标很棘手。

关于答案的一些想法:

  • PengOne :听起来不错,虽然它没有提供具体的实施方案,但根据第二个目标并不总能带来最好的结果。
  • Evgeny Kluev :为主要目标提供具体实施,但根据次要目标,它不会带来最佳结果。
  • tobias_k:我喜欢随机方法,它并不总能带来最好的结果,但它是一个很好的近似和成本效益。

我尝试了Evgeny Kluev,回溯和tobias_k公式的组合,但它需要太多时间才能得到结果。

最后,至少对于我的问题,我认为tobias_k是最合适的算法,因为它的简单性和及时的好结果。可能使用模拟退火可以改善它。

6 个答案:

答案 0 :(得分:5)

首先,您还没有明确定义的优化问题。如果你想最大化两个相同类型的项目之间的最小距离,那就很好了。如果你想最大化两个A之间以及两个B和......之间以及两个Z之间的最小距离,那么这个定义并不明确。您如何比较两种解决方案:

  1. A至少相隔4分,B分别至少4分,C分别至少2分
  2. A至少3分开,B分别至少3分,C分别至少4分
  3. 您需要明确定义的“好”(或更准确地说,“更好”)。我现在假设该措施是: 最大化同一项目 中任意两项之间的最小距离。

    这是一种算法,其最小距离为ceiling(N/n(A)),其中N是项目总数,n(A)是实例A的项目数,假设A是最多的。

    • 订购项目类型A1, A2, ... , Ak,其中n(Ai) >= n(A{i+1})
    • 将列表L初始化为空。
    • 对于从jk的{​​{1}},请在1中尽可能统一地分发Ak类型的项目。

    示例:给定问题中的分布,算法产生:

    L

答案 1 :(得分:3)

这听起来像一个有趣的问题,所以我试了一下。这是我用Python完成的超简单随机方法:

def optimize(items, quality_function, stop=1000):
    no_improvement = 0
    best = 0
    while no_improvement < stop:
        i = random.randint(0, len(items)-1)
        j = random.randint(0, len(items)-1)
        copy = items[::]
        copy[i], copy[j] = copy[j], copy[i]
        q = quality_function(copy)
        if q > best:
            items, best = copy, q
            no_improvement = 0
        else:
            no_improvement += 1
    return items

正如评论中已经讨论的那样,真正棘手的部分是质量函数,作为参数传递给优化器。经过一番尝试后,我想出了一个几乎总能产生最佳效果的产品。感谢 pmoleri ,指出如何提高效率。

def quality_maxmindist(items):
    s = 0
    for item in set(items):
        indcs = [i for i in range(len(items)) if items[i] == item]
        if len(indcs) > 1:
            s += sum(1./(indcs[i+1] - indcs[i]) for i in range(len(indcs)-1))
    return 1./s

这里有一些随机结果:

>>> print optimize(items, quality_maxmindist)
['A', 'B', 'C', 'A', 'D', 'E', 'A', 'B', 'F', 'C', 'A', 'D', 'B', 'A']

请注意,传递另一个质量函数,相同的优化器可用于不同的列表重新排列任务,例如,作为一个(相当愚蠢的)随机分拣机。

答案 2 :(得分:3)

这是一种算法,它只能最大化相同类型元素之间的最小距离,并且不会做任何事情。以下列表用作示例:

AAAAA BBBBB CCCC DDDD EEEE FFF GG
  • 按降序排列每个类型的元素数对元素集进行排序。实际上,只有最大集(A&amp; B)应放在列表的头部以及那些具有一个元素较少的元素集(C&amp; D&amp; E)。其他套装可能未分类。
  • 为每个最大集合中的一个元素保留R数组中的最后位置,将剩余数组均匀地划分在最大集合的S-1个剩余元素之间。这给出了最佳距离:K =(N-R)/(S-1)。将目标阵列表示为具有K列和L = N / K个完整行的2D矩阵(并且可能是具有N%K个元素的一个部分行)。例如,我们有R = 2,S = 5,N = 27,K = 6,L = 4。
  • 如果矩阵具有S-1个完整行,则用最大集合(A&amp; B)的元素填充此矩阵的前R列,否则从最后一个列开始依次填充所有列。

对于我们的例子,这给出了:

AB....
AB....
AB....
AB....
AB.

如果我们尝试以相同的顺序用其他集填充剩余的列,则会出现问题:

ABCDE.
ABCDE.
ABCDE.
ABCE..
ABD

最后一个'E'距离第一个'E'只有5个位置。

  • 从最后一列开始,按顺序填充所有列。

对于我们的例子,这给出了:

ABFEDC
ABFEDC
ABFEDC
ABGEDC
ABG

回到线性数组我们有:

ABFEDCABFEDCABFEDCABGEDCABG

这是尝试对此问题使用模拟退火(C源):http://ideone.com/OGkkc

答案 3 :(得分:0)

我相信你可以看到你的问题就像一堆物理排斥彼此的粒子。你可以迭代到一个稳定的&#39;情况。

基本伪代码:

force( x, y ) = 0 if x.type==y.type
                1/distance(x,y) otherwise 

nextposition( x, force ) = coined?(x) => same
                           else => x + force

notconverged(row,newrow) = // simplistically
   row!=newrow

row=[a,b,a,b,b,b,a,e]; 
newrow=nextposition(row);
while( notconverged(row,newrow) )
   newrow=nextposition(row);

我不知道它是否收敛,但这是一个想法:)

答案 4 :(得分:0)

我确信可能有更有效的解决方案,但这是您的一种可能性:

首先,请注意,很容易找到一个产生最小距离之间相同类型的1的排序。只需使用任何随机排序,MDBIOST将至少为1,如果不多了。

因此,首先假设MDBIOST为2.根据MDBIOST为2 的假设,对可能的排序空间进行递归搜索,。您可以使用许多条件来修剪此搜索中的分支。如果您发现有效的订单,请终止搜索。

如果你发现一个有效,请再试一次,假设MDBIOST为3.然后4 ......依此类推,直到搜索失败。

更新:号码开头实际上会更好,因为这会更多地限制可能的选择。然后逐渐减少数量,直到找到有效的顺序。

答案 5 :(得分:0)

这是另一种方法。

如果每个项目必须与相同类型的其他项目保持至少k个位置,则从左向右记下项目,跟踪每种类型剩余的项目数量。在每个点放下一个剩下最多的物品,你可以合法放下。

如果没有超过相同类型的ceil(N / k)项目,这将适用于N个项目,因为它将保留此属性 - 在放下k个项目之后我们减少k个项目并且我们放下了每种类型中至少有一种以ceil(N / k)类型的物品开始。

考虑到一系列混合物品,你可以计算出你能支持的最大k,然后列出要解决这个问题的物品。