我需要随机列出一些排列。元素可以是任何东西,但假设它们是整数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个。
答案 0 :(得分:2)
这可以改进,但它似乎完成了工作(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)
答案 1 :(得分:0)
好的,一种近似的方法:
1 - 随机播放列表
2 - 取y个第一个元素以形成下一行
4 - 只要列表中有数字
,就重复(2)5 - 如果您没有足够的号码来完成列表,请重新洗牌原始列表并删除缺失的元素,确保不重新编号。
6 - 只要你需要行
,就从步骤(2)开始我认为这应该是随机的,并且肯定会遵循您的标准。另外,您对重复元素的测试非常少。
答案 2 :(得分:0)
首先,你总是可以随意对列表进行排序,所以我们不要担心“随机排列”(硬);并且只是担心1)进行排列(简单)和2)随机化(简单)。
如果你想要“真正的”随机组,你必须接受本质上的随机化并不真正允许结果“均匀分布”的约束 - 你可能会得到这个或者你可能得到类似的 - 寻找的。如果你真的想要均匀分布,首先要均匀分布集合,然后将它们随机分组。
你必须均匀地使用集合x中的每个元素吗?从规则中不清楚我不能做出以下解释:
请注意以下内容:“在所有列表中,每个元素的使用次数相同(或尽可能接近)”
基于此标准,以及z&lt; x *,我假设您可以简单地枚举所有列表中的所有项目。因此,您会自动列出枚举到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。
答案 3 :(得分:0)
根据评论中的新细节,解决方案可能只是标准随机排列生成算法的实现。这里有一个关于随机置换生成算法的冗长讨论:
http://www.techuser.net/randpermgen.html
(来自Google搜索:随机排列生成)
答案 4 :(得分:0)
这适用于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
答案 5 :(得分:0)