创建列表的许多受约束的随机排列

时间:2020-03-06 14:21:46  来源:igfitidea点击:

我需要随机排列排列的列表。元素可以是任何东西,但前提是它们是从0到x-1的整数。我要制作y列表,每个列表包含z元素。规则是,列表中不能包含两次相同的元素,并且在所有列表中,每个元素的使用次数是相同的(或者尽可能接近)。例如,如果我的元素是0、1、2、3,y是6,z是2,那么一种可能的解决方案是:

0,3
1,2
3,0
2,1
0,1
2,3

每行只有唯一的元素,并且没有元素被使用超过3次。如果y为7,则2个元素将使用4次,其余3个。

解决方案

好的,一种近似的方法:

1随机播放列表

2将y个第一个元素作为下一行

只要列表中有数字,请重复4个(2)

5如果我们没有足够的数字来完成列表,请重新排列原始列表并删除缺少的元素,确保我们不重新获取数字。

6只要需要行,请从步骤(2)重新开始

我认为这应该尽可能地随机,并且一定会遵循标准。另外,我们对重复元素的测试很少。

首先,我们总是可以在最后对列表进行随机排序,因此不必担心进行"随机排列"(困难);只需担心1)进行排列(容易)和2)将它们随机化(容易)即可。

如果我们想要"真正的"随机组,则必须接受这种本质上的随机性实际上并没有约束结果"均匀分布"的约束-我们可能会得到,或者可能会看到一系列相似的结果。如果我们确实想要平均分配,请首先使集合均匀分配,然后将它们随机分组。

我们是否必须平均使用集合x中的每个元素?从规则尚不清楚,我不能仅仅做出以下解释:

请注意以下几点:"在所有列表中,每个元素的使用次数相同(或者尽可能接近)"

基于此标准和z <x *的规则,我假设我们可以简单地枚举所有列表中的所有项目。因此,我们会自动将y列出的项目列到位置z。示例没有像我的版本那样完全满足上述规则。使用x = {0,1,2,3} y = 6和z = 2的示例,我得到:
0,1 0,1 0,1 0,1 0,1 0,1

现在我没有使用2或者3,但是我们没有说我必须全部使用它们。如果我必须全部使用它们,而又不希望能够证明我"尽可能"接近甚至使用它们,那么我将列举列表中的所有项目,例如:
0,1 2,3 0,1 2,3 0,1 2,3

最后,假设我确实必须使用所有元素。为了计算每个元素可以重复多少次,我只取(y * z)/(x的数量)。这样,我不必坐下来担心如何划分列表中的项目。如果有余数,或者结果小于1,那么我知道我将无法获得确切的重复次数,因此,在那种情况下,尝试浪费计算能力以使其完美无所谓。我认为最快的结果仍然是如上所述,并且使用此处的计算来说明为什么达到或者没有达到完美的结果。一种花哨的算法可以从该计算中提取出多少个位置将重复,但是"太长了,无法在此处容纳空白"。

*每个列表具有相同的z个元素数,因此将不可能创建z大于x的列表,并且仍然满足以下规则:没有列表可以包含两次相同的元素。因此,此规则要求z不能大于x。

基于评论中的新细节,该解决方案可以简单地是标准随机排列生成算法的实现。这里对随机排列生成算法进行了冗长的讨论:

http://www.techuser.net/randpermgen.html

(来自Google搜索:随机排列生成)

可以改进,但是似乎可以完成此工作(Python):

import math, random

def get_pool(items, y, z):
    slots = y*z

    use_each_times = slots/len(items)
    exceptions = slots - use_each_times*len(items)

    if (use_each_times > y or
        exceptions > 0 and use_each_times+1 > y):
        raise Exception("Impossible.")

    pool = {}
    for n in items:
        pool[n] = use_each_times

    for n in random.sample(items, exceptions):
        pool[n] += 1

    return pool

def rebalance(ret, pool, z):
    max_item = None
    max_times = None

    for item, times in pool.items():
        if times > max_times:
            max_item = item
            max_times = times

    next, times = max_item, max_times

    candidates = []
    for i in range(len(ret)):
        item = ret[i]

        if next not in item:
            candidates.append( (item, i) )

    swap, swap_index = random.choice(candidates)

    swapi = []
    for i in range(len(swap)):
        if swap[i] not in pool:
            swapi.append( (swap[i], i) )

    which, i = random.choice(swapi)

    pool[next] -= 1
    pool[swap[i]] = 1
    swap[i] = next

    ret[swap_index] = swap

def plist(items, y, z):
    pool = get_pool(items, y, z)

    ret = []
    while len(pool.keys()) > 0:
        while len(pool.keys()) < z:
            rebalance(ret, pool, z)

        selections = random.sample(pool.keys(), z)

        for i in selections:
            pool[i] -= 1
            if pool[i] == 0:
                del pool[i]

        ret.append( selections )

    return ret

print plist([0,1,2,3], 6, 2)

这适用于Ruby:

# list is the elements to be permuted
# y is the number of results desired
# z is the number of elements per result
# equalizer keeps track of who got used how many times
def constrained_permutations list, y, z
  list.uniq! # Never trust the user. We want no repetitions.
  equalizer = {}
  list.each { |element| equalizer[element] = 0 }

  results = []
  # Do this until we get as many results as desired
  while results.size < y
    pool = []
    puts pool
    least_used = equalizer.each_value.min
    # Find how used the least used element was
    while pool.size < z
      # Do this until we have enough elements in this resultset
      element = nil
      while element.nil?
        # If we run out of "least used elements", then we need to increment
        # our definition of "least used" by 1 and keep going.
        element = list.shuffle.find do |x|
          !pool.include?(x) && equalizer[x] == least_used
        end
        least_used += 1 if element.nil?
      end
      equalizer[element] += 1
      # This element has now been used one more time.
      pool << element
    end
    results << pool
  end
  return results
end

用法示例:

constrained_permutations [0,1,2,3,4,5,6], 6, 2
=> [[4, 0], [1, 3], [2, 5], [6, 0], [2, 5], [3, 6]]
constrained_permutations [0,1,2,3,4,5,6], 6, 2
=> [[4, 5], [6, 3], [0, 2], [1, 6], [5, 4], [3, 0]]
enter code here

http://en.wikipedia.org/wiki/Fisher-Yates_shuffle