【问题标题】:Evenly distributing all items in a list均匀分布列表中的所有项目
【发布时间】:2010-07-30 10:20:23
【问题描述】:

我遇到这个问题有一段时间了,仍在努力寻找解决方案。

在列表中均匀分布项目且差异较小的最佳方法是什么?

假设我们有一个列表或数组中的元素:

Red, Red, Red, Red, Red, Blue, Blue, Blue, Green, Green, Green, Yellow

理想情况下,输出会产生如下内容:

Red, Blue, Red, Green, Red, Yellow, Blue, Red, Green, Red, Blue, Green.

每个实例都尽可能“远离”自身的另一个实例...

当我第一次尝试解决这个问题时,我必须承认我很天真,所以我只是使用某种形式的种子随机数来打乱列表,但这会导致实例聚集。

建议从频率最高的项目开始,因此红色将放置在位置 n*12/5 处,n 从 0 到 4(含)。

然后将下一个重复次数最多的元素(蓝色)放置在位置 n*12/3 + 1 中,n 从 0 到 2(含)。如果那里已经放了东西,那就把它放在下一个空的地方。等等等等。但是,当在纸上记下它时,这并不适用于所有情况,

说列表只是

Red, Red, Red, Red, Red, Blue

它会失败。

其中任一选项具有三个相同颜色的邻接

Red, Red, Blue, Red, Red, Red
Red, Red, Red, Blue, Red, Red

所以请,任何想法或实现如何做到这一点都会很棒。

如果这很重要,我正在研究objective-c,但现在我只关心如何去做。

【问题讨论】:

  • 不知道我理解的对不对。在第一个示例中,输入列表有 12 个元素,输出列表有 14 个。这样正确吗?
  • 对不起,我的错。我会改正的。

标签: algorithm math


【解决方案1】:

只是一个快速的想法:为每种类型的项目使用单独的列表。然后使用诸如合并排序之类的东西将每个列表中的一项插入新列表中,始终以相同的顺序。跳过空列表。

这当然不会产生完美的解决方案,但它很容易实现并且应该很快。一个简单的改进是按大小对列表进行排序,最大的在前。这比随机排列列表的结果要好一些。

更新:也许这可以使它变得更好:在算法开始时获取最大列表的大小并将其命名为LARGEST_SIZE - 这个将在每一轮中轮到它。现在对于所有其他列表,它们应该只在starting_size_of_the_list/LARGEST_SIZE 轮次中使用。我希望你知道我的意思。这样,您应该能够均匀地间隔所有项目。但是,它仍然不完美!

好的,所以我会尝试更具体。假设您有 4 个尺寸列表:30 15 6 3

对于第一个列表,您将每 30/30 轮使用一次,即 1,因此每 1 轮使用一次。这意味着每次。对于第二个列表,您将使用它 15/30,即 0.5,因此每 2 轮。第三个列表:6/30 -> 每 5 轮。最后列表:3/30 -> 每 10 轮。这真的应该给你一个很好的项目间距。

这当然是一个很好的例子,对于其他数字它变得有点难看。对于非常少量的项目,这不会为您带来完美的结果。但是对于大量的项目,它应该工作得很好。

【讨论】:

  • 这样你永远不会建造:蓝色,蓝色,红色,蓝色,蓝色
  • 我知道。我从来没有说过这是完美的,它只是一个产生良好结果的快速解决方案。
  • 谢谢,嗯,关于第一个建议,这是我尝试过的事情之一,正如你所说,它在大多数情况下都有效。我尝试在 Python 中使用 itertools 和循环法。关于您的更新,抱歉,我相信大多数情况下这是有道理的,但我的数学很差:(
  • 更新了答案以澄清一些事情。希望这会有所帮助。
【解决方案2】:

您可以生成一系列有理数,指示每种颜色的均匀间距。然后,对所有这些数字进行排序。

例子:

  • 5 × B: 数字是 (1/10 3/10 5/10 7/10 9/10)
  • 3 × R:数字是(1/6 3/6 5/6)
  • 已排序: ((1/10 "B") (1/6 "R") (3/10 "B") (5/10 "B") (3/6 "R") (7/10 "B") (5/6 "R") (9/10 "B"))
  • => B R B B R B R B

当数字相等时,应用二次排序,它可以是任意的,但应该是一致的。

请注意,各个序列已经排序,因此您可以通过合并进行排序,在这种情况下,这只是 O(n·log m)n 是所有计数的总和,m 颜色的数量)。这可以通过延迟生成数字来进一步优化。

最终算法不需要显式排序:

  • B 计数器设置为 (/ (* 2 5)) => 1/10
  • R 计数器设置为 (/ (* 2 3)) => 1/6
  • B 步骤设置为B 计数器的两倍
  • R 步骤设置为R 计数器的两倍
  • 循环
    • 取一种颜色最低的颜色并将其放入您的结果中
    • 按步长计数的步数
    • 直到所有计数器 >= 1

由于您需要循环的 n 个步骤,并且每次都必须找到 m 个数字的最小值,因此这似乎运行在 O(n·米)。但是,您可以将计数器保持在最小堆中,以再次将其降低到 O(n·log m)

【讨论】:

  • 我了解这个方向,但想知道算法是否可以更简单一些... 1) 计算每种颜色的权重或乘数(例如 Bweight=8/5, Rweight=8/ 3)
  • 对不起,没时间了..再次:我理解这是前进的方向,但想知道算法是否可以更简单一些...... 1)计算每种颜色的权重或乘数(例如Bweight=8/5, Rweight=8/3), 2) 通过分配合理值的每种颜色递增(例如 Blues = 1*8/5,2*8/5,3*8/5,4*/8/ 5,5*8/5;红色=1*8/3、2*8/3、3*8/3)。然后按这些值对整个 8 个项目进行排序,并给出相同的 BRBBRBRB 序列。我错过了什么吗?
  • 乍一看似乎是一样的。
  • 您很可能是正确的,尽管我不明白 2* 因子从何而来,导致 1/10 和 1/6(而不是 8/5 和 8/3),或者加倍B/R 柜台?但无论如何,我已经成功地使用了你的想法,对你的答案投了赞成票,我对你的建议的简单性感到非常满意。谢谢。
  • 我将最后一组数字标准化,使其均匀地落入 0 到 1 的范围内。当然,这个范围完全是任意的。
【解决方案3】:

我将在此处发布我在算法竞赛中针对此问题的一些案例中使用的解决方案。

您将拥有最大堆的对(计数器,颜色),按计数器排序,因此具有最大计数器的颜色将位于顶部。每次您都会遇到两种情况:如果顶部的值与列表中的最后一个元素不相等,您将从堆中删除 pair(COUNTERx, COLOURx),将 COLOURx 添加到列表的末尾,如果 (COUNTERx) - 1 != 0 则将 pair( (COUNTERx) - 1, COLOURx) 添加到堆中。在另一种情况下,从堆中取出第二大 COUNTER 对而不是第一个,并对第一对执行相同的操作.时间复杂度为 o(S log N),其中 N 是颜色的数量,S 是列表的大小。

【讨论】:

    【解决方案4】:

    您可以对K-means clustering 进行逆运算,目标是:

    • 最大化集群数量
    • 使用某种逆函数定义项目与相似项目的接近程度,以便从相距较远而不是靠在一起的相似项目创建集群。

    【讨论】:

      【解决方案5】:

      我认为您需要针对某种改进功能进行优化 - 比如说计算在某个位置插入 Blue 会“更好”多少,并对所有可能的插入位置执行此操作,然后插入到任何位置用这个“增益”函数的最大值继续。

      【讨论】:

        【解决方案6】:

        使用动态评分函数对列表进行排序,该函数对列表中的每个元素返回与具有相同值的最近元素的距离。

        【讨论】:

          猜你喜欢
          • 2011-11-30
          • 2021-10-08
          • 1970-01-01
          • 2021-04-23
          • 2017-06-28
          • 2021-07-07
          • 2012-12-20
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多