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

发布于 2024-07-05 02:34:25 字数 281 浏览 12 评论 0原文

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

I need to make a random list of permutations. The elements can be anything but assume that they are the integers 0 through x-1. I want to make y lists, each containing z elements. The rules are that no list may contain the same element twice and that over all the lists, the number of times each elements is used is the same (or as close as possible). For instance, if my elements are 0,1,2,3, y is 6, and z is 2, then one possible solution is:

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

Each row has only unique elements and no element has been used more than 3 times. If y were 7, then 2 elements would be used 4 times, the rest 3.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

凌乱心跳 2024-07-12 02:34:26

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

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

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

Based on new details in the comments, the solution may simply be an implementation of a standard random permutation generation algorithm. There is a lengthy discussion of random permutation generation algorithms here:

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

(From Google search: random permutation generation)

没企图 2024-07-12 02:34:26

这在 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

This works in 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

Sample usage:

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
青春如此纠结 2024-07-12 02:34:26

首先,你总是可以对列表进行随机排序,所以我们不用担心“随机排列”(很难); 只需担心 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。

First, you can always randomly sort the list in the end, so let's not worry about making "random permutations" (hard); and just worry about 1) making permutations (easy) and 2) randomizing them (easy).

If you want "truly" random groups, you have to accept that randomization by nature doesn't really allow for the constraint of "even distribution" of results -- you may get that or you may get a run of similar-looking ones. If you really want even distribution, first make the sets evenly distributed, and then randomize them as a group.

Do you have to use each element in the set x evenly? It's not clear from the rules that I couldn't just make the following interpretation:

Note the following: "over all the lists, the number of times each elements is used is the same (or as close as possible)"

Based on this criteria, and the rule that z < x*, I postulate that you can simply enumerate all the items over all the lists. So you automatically make y list of the items enumerated to position z. Your example doesn't fulfill the rule above as closely as my version will. Using your example of x={0,1,2,3} y=6 and z=2, I get:
0,1 0,1 0,1 0,1 0,1 0,1

Now I didn't use 2 or 3, but you didn't say I had to use them all. If I had to use them all and I don't care to be able to prove that I am "as close as possible" to even usage, I would just enumerate across all the items through the lists, like this:
0,1 2,3 0,1 2,3 0,1 2,3

Finally, suppose I really do have to use all the elements. To calculate how many times each element can repeat, I just take (y*z)/(count of x). That way, I don't have to sit and worry about how to divide up the items in the list. If there is a remainder, or the result is less than 1, then I know that I will not get an exact number of repeats, so in those cases, it doesn't much matter to try to waste computational energy to make it perfect. I contend that the fastest result is still to just enumerate as above, and use the calculation here to show why either a perfect result was or wasn't achieved. A fancy algorithm to extract from this calculation how many positions will be duplicates could be achieved, but "it's too long to fit here in the margin".

*Each list has the same z number of elements, so it will be impossible to make lists where z is greater than x and still fulfill the rule that no list may contain the same element twice. Therefore, this rule demands that z cannot be greater than x.

叹沉浮 2024-07-12 02:34:26

好的,一种近似方法:

1 - 打乱列表

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

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

5 - 如果没有足够的数字要完成列表,请重新整理原始列表并取出丢失的元素,确保不会重新取出数字。

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

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

Ok, one way to approximate that:

1 - shuffle your list

2 - take the y first elements to form the next row

4 - repeat (2) as long as you have numbers in the list

5 - if you don't have enough numbers to finish the list, reshuffle the original list and take the missing elements, making sure you don't retake numbers.

6 - Start over at step (2) as long as you need rows

I think this should be as random as you can make it and will for sure follow your criteria. Plus, you have very little tests for duplicate elements.

半世晨晓 2024-07-12 02:34:25

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

This could be improved, but it seems to do the job (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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文