【问题标题】:How to shuffle colour balls?如何洗牌彩球?
【发布时间】:2011-10-21 10:50:00
【问题描述】:

我有 400 个球,其中 100 个是红色的,40 个是黄色的,50 个是绿色的,60 个是蓝色的,70 个是紫色的,80 个是黑色的。 (相同颜色的球是相同的)

我需要一个高效的洗牌算法,这样洗牌后,球在一个列表中,并且

任何连续的 3 个球的颜色都不相同。例如,我不能有“红、红、红、黄……”

而且,所有排列都“均等地”发生。 (好吧,如果效率与公正性的权衡足够好,我不介意效率高于公正性)。

我尝试改编Fisher-Yates-Knuth,但结果并不理想。

为什么 Fisher-Yates 不够好?由于 FY 采用蒙特卡洛逆变换。并且输出分布对相同颜色的球的处理方式不同,即它会根据我的需要产生有偏差的结果。

而且,幼稚的想法是从整个空间中过滤/回溯所有不良排列。当限制非常强时,例如,如果我们只有 300 个球,其中 100 个是红色的,那么在获得适当的排列之前会出现太多的回溯/失败。

所以,最终,我希望能够遍历所有好的排列。但是,由于有效排列的数量太多,我只能随机抽取其中一些。

【问题讨论】:

  • 您是如何尝试适应 Fisher-Yates-Knuth 的,结果如何不“理想”?
  • 我认为用一个幼稚的解决方案你最终会回溯以确保你的规则成立。想象一下,如果您还剩下三个球可以选择,并且三个球的颜色相同;那么你可以用一个简单的线性算法,不能保证没有三个连续的颜色是一种颜色。
  • @joey:我之前尝试过使用回溯,但是当输入限制太强时,算法会花费大量时间回溯,即大量失败和重试
  • @colinfang 澄清问题:您是否尝试实际生成 400 个项目的特定“随机播放”(恰好与无三合一约束匹配),或者您是否尝试提出三个球的随机组合,其中特定组合的概率与“连续随机选择三个球,就好像它们来自有效的 [实际] 洗牌一样”。

标签: c# combinations


【解决方案1】:

据我了解,FYK 算法正在交换数组中的随机位置。为什么不能像我在伪代码中描述的那样只生成颜色?

public IEnumerable<Color> GetColors()
{
   int count = 400;
   // queue or another data structure to hold the last generated colors
   Queue<Color> lastColors = new Queue<Color>(); 
   var availableColor = new Dictionary<Color, int> { 
     {Red, 100}, {Yellow, 40}, ...
   };
   Color nextColor = null;
   while(count > 0)
   {
     do {
       /* randomly pick from color buckets */
       nextColor = /* choose random color based on the weights*/;
     } while(/*it satisfies the condition, that it is not 3rd same color in a row*/)
     yield return nextColor;
     count--;
   }
}

【讨论】:

  • 当你跑到终点只剩下 3 个红球时会发生什么?
  • 我不知道 - 它隐藏在“根据权重选择随机颜色”中......算法的成功取决于输入值,100xR和1xG你不会得到结果正确。
  • ur 算法是经典 FY 算法或蒙特卡罗逆变换的一些变体。对输入的限制太强,效率不高,会导致失败太多,输出也太偏了。
【解决方案2】:

大声思考,我会尝试

  • “设计”(想想)一个有效组合的(递归)生成器;确保它以确定的顺序生成组合;
  • 将生成器转换为确定性编号方案(一个唯一标识您的任何有效组合的神奇数字)2
  • 实现将目标幻数作为参数的生成器算法,这样您就不必做所有的工作来获得'第n个有效组合',而是可以“跳转”到所需的组合(这要求您的递归生成函数是显式确定性;任何回溯都会使这成为不可能)。

现在,您可以简单地在 [0..max_number_of_valid_combinations]1 之间生成一个均匀分布的随机“幻数”。对于每个选定的幻数,您可以打印生成的有效组合。

如果您有兴趣,我可能会抽出时间来尝试一下。 (我更喜欢在 C++/Python 中这样做,但 C# 应该是可能的(.Net 4.0 还提供 BigInteger 类吗?)


1(这可能是一个巨大的数字,因此您可能会被某种 BigInteger 卡住,并生成多个随机数以得出大数。确保您了解分布算法确保这仍然是均匀分布的......

2 有大量的组合可以得出适当的乘数,以在生成过程中的特定点找到可能的“尾部组合”的数量。这是 IMO 的复杂性瓶颈

【讨论】:

  • 是的,BigInteger is in .Net 4 可用。
  • max_number_of_valid_combinations 太大。我更愿意将 rng 嵌入到生成器中。为了提高效率,我不介意失去一些公正性。毕竟,我想从总空间中获得 10,000 个独特的排列。最重要的是,c++ 对我来说还可以
  • @colinfang:太大了怎么办?它只需要是可计算的,而不是可枚举的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-11-27
  • 2013-04-28
  • 2011-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多