【问题标题】:Create many constrained, random permutation of a list创建列表的许多受约束的随机排列
【发布时间】:2010-09-10 17:41:48
【问题描述】:

我需要制作一个随机排列列表。元素可以是任何东西,但假定它们是整数 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 次。

【问题讨论】:

    标签: random list permutation


    【解决方案1】:

    这可以改进,但它似乎可以完成这项工作(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)
    

    【讨论】:

    • 它确实比我所拥有的任何东西都更好,但重新平衡功能使运行时不确定。无赖。
    • 这当然不是人们所希望的优雅解决方案。我会考虑更多,但不能保证我会想出更好的东西。
    【解决方案2】:

    好的,一种近似的方法:

    1 - 随机播放列表

    2 - 取第 y 个元素组成下一行

    4 - 只要列表中有数字就重复 (2)

    5 - 如果您没有足够的数字来完成列表,请重新排列原始列表并取走缺失的元素,确保不要重新取走数字。

    6 - 只要您需要行,就从第 (2) 步开始

    我认为这应该尽可能随机,并且肯定会遵循您的标准。此外,您几乎不需要对重复元素进行测试。

    【讨论】:

    • 那肯定会给出解决方案,但不会给出所有解决方案。我想要一种算法,它会以相等的概率生成解决方案集中的答案之一。在我的示例中使用您的算法,第一行和第二行不可能有相同的元素。
    【解决方案3】:

    首先,你总是可以在最后对列表进行随机排序,所以我们不用担心做出“随机排列”(困难);并且只需担心 1) 进行排列(容易)和 2) 将它们随机化(容易)。

    如果您想要“真正的”随机组,您必须接受随机化本质上并没有真正考虑到结果“均匀分布”的约束——你可能会得到它,或者你可能会得到类似的结果——看的。如果你真的想要均匀分布,首先使集合均匀分布,然后将它们随机化为一组。

    你必须均匀地使用集合 x 中的每个元素吗?从规则中不清楚我不能只做出以下解释:

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

    基于这个标准,以及 z

    现在我没有使用 2 或 3,但你没有说我必须全部使用它们。如果我必须全部使用它们并且我不在乎能够证明我“尽可能接近”甚至使用,我会通过列表枚举所有项目,如下所示: 0,1 2,3 0,1 2,3 0,1 2,3

    最后,假设我真的必须使用所有元素。要计算每个元素可以重复多少次,我只需要 (y*z)/(count of x)。这样,我就不必坐下来担心如何划分列表中的项目。如果有余数,或者结果小于 1,那么我知道我不会得到准确的重复次数,所以在这些情况下,尝试浪费计算能量来使其完美并不重要。我认为最快的结果仍然是像上面那样枚举,并使用这里的计算来说明为什么达到或没有达到完美的结果。一个奇特的算法可以从这个计算中提取多少个位置将被重复,但是“它太长了,不适合在空白处”。

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

    【讨论】:

    • 您的第一个解决方案 0,1 0,1 0,1 0,1 0,1 0,1 没有使用每个值 3 次,因此这不是一个有效的解决方案。解决方案 0,1 2,3 0,1 2,3 0,1 2,3 是一个有效的解决方案。我需要一种算法,它会以相等的概率生成所有有效的解决方案。如果确定性更好。
    【解决方案4】:

    根据 cmets 中的新细节,解决方案可能只是标准随机排列生成算法的实现。这里对随机排列生成算法进行了冗长的讨论:

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

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

    【讨论】:

      【解决方案5】:

      这适用于 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
      

      【讨论】:

        【解决方案6】:
        猜你喜欢
        • 2013-08-29
        • 2017-09-17
        • 1970-01-01
        • 2013-08-23
        • 2020-06-15
        • 2021-04-29
        • 2016-05-24
        • 2012-02-17
        • 2013-10-17
        相关资源
        最近更新 更多