寻找有限的shuffle算法

时间:2015-08-20 04:46:07

标签: algorithm random shuffle array-algorithms

我有一个洗牌问题。有很多关于完全改组数值的页面和讨论,比如一叠牌。

我需要的是一个shuffle,它将数组元素从最初的N个位置均匀地移位。

即如果N为2,则元素I最多将被拖曳到从I-2到I + 2的位置(在数组的范围内)。

事实证明,使用一些简单的解决方案会导致对元素移动的方向性偏差,或者通过不均匀的量,这是非常棘手的。

2 个答案:

答案 0 :(得分:2)

你是对的,这很棘手!首先,我们需要制定更多规则,以确保我们不会创造人为的非随机结果:

  • 元素可以保留在它们开始的位置。这是任何公平洗牌的必要部分,并且还确保我们的洗牌适用于N = 0.
  • 当N大于元素距阵列起点或终点的距离时,允许它移动到另一侧。我们可以调整算法来禁止这个,但它会违反"统一"要求 - 两端附近的元素比中间附近的元素更容易放置。

现在我们可以解决问题了。

  1. 生成i + [-N, N]范围内的随机值数组,其中i是数组中的当前索引。规范化数组范围之外的值(例如-1应该变为length-1length应变为0。)
  2. 查找数组中的重复值(碰撞)对,然后重新计算它们。你有几个选择:
    • 重新计算这两个值,直到它们不相互碰撞,它们仍然可能会与其他值发生碰撞。
    • 只重新计算一个,直到它不与另一个碰撞,第一个值仍然可能会发生碰撞,但第二个现在应该是唯一的,这可能意味着对RNG的调用次数减少。 / LI>
    • 确定每次碰撞的可用索引集(例如,在[3, 1, 1, 0]索引2中可用),从该集合中选择一个随机值,并将其中一个数组值设置为选定的结果。这避免了在解决冲突之前需要循环,但是代码更复杂,并且在集合为空的情况下可能会遇到风险。
  3. 但是,您可以解决单个冲突,重复此过程,直到数组中的每个值都是唯一的。
  4. 现在将原始数组中的每个元素移动到我们生成的数组中指定的索引。
  5. 我不确定如何最好地实施#2,我建议您对其进行基准测试。如果您不想花时间进行基准测试,我会选择第一个选项。其他的是可能更快的优化,但实际上可能会变慢。

    此解决方案在理论上具有无限运行时间,但在实践中应该合理地快速终止。再次,在使用它之前进行基准测试并对其进行测试。

答案 1 :(得分:0)

我已经提出了一个可能的解决方案,但是如何“天真地”'我不确定。特别是在边缘处,尤其是远边缘。

  1. 创建一个标志数组(布尔值)N long(表示已交换的元素)

  2. For At每个索引检查它是否已经被交换(根据flags数组中的第一个元素),如果是的话,继续下一个(见下文)

  3. 旋转flags数组,删除第一个元素(表示这个 元素),并添加一个新的' not swapped'元素结束。 ASIDE:这个 也许使用模数阵列查找,以避免实际 移动数组内容,特别是对于大N

  4. ...循环

    • 选择一个从0到N的数字(或小于N,如果N加上当前   数据被洗牌的索引更大。
    • 如果为0,则元素与自身交换,移至下一个。
    • 否则,如果该元素标记为已交换,请循环并再次尝试。 请注意,flags数组中总有2个元素可以自行选择 和最后一个元素(除非接近数组的结尾被洗牌)
  5. 使用选定的未切换元素交换当前元素,将所选元素标记为flags数组中的交换。循环到下一个元素