心理学实验通常要求您伪随机化试验顺序,以便试验显然是随机的,但您不会连续得到太多类似的试验(这可能发生在纯粹的随机排序中)。
假设每个试验的视觉显示都有颜色和大小:
display_list = []
colours = {0: 'red', 1: 'blue', 2: 'green', 3: 'yellow'}
sizes = [1] * 20 + [2] * 20 + [3] * 20 + [4] * 20 + [5] * 20 + [6] * 20
for i in range(120):
display_list.append({'colour': colours[i % 4], 'size': sizes[i]})
print(display_list)
我们可以使用此函数查看具有相同值的连续试验的最大数量:
def consecutive_properties(seq, field):
longest_run = 0
prev_value = None
current_run = 0
for d in seq:
if d[field] == prev_value:
current_run += 1
else:
current_run = 1
if current_run > longest_run:
longest_run = current_run
prev_value = d[field]
return longest_run
输出:
>>> print("Consecutive colours: ", consecutive_properties(display_list, 'colour')
('Consecutive colours: ', 1)
>>> print("Consecutive sizes: ", consecutive_properties(display_list, 'size'))
('Consecutive sizes: ', 20)
您是否知道有哪些算法可以最大限度地减少一个或两个属性的连续运行,或者至少将这些运行保持在指定长度以下?如果是后者,假设连续颜色或大小不超过4个。
我尝试了什么:
我现在的解决方案基本上是一个有点聪明的bogosort,它必须非常低效。基本上是:
display_list
分解为长度为24的块,则每个块的每种颜色都与每个大小配对。让我们假设试用列表总是可以分解为这些排列块,因为你知道实验设计的排列是什么。答案 0 :(得分:3)
问题:你知道的算法是否允许最小化 连续运行其中一个或两个属性,或至少保留这些属性 低于指定的长度?
是。有一个简单的算法可以通过简单地降低颜色或大小的选择概率(如果它已经在运行中发生)来实现。
from random import randrange
def choose(colors, numselections, maxrun):
'Repeatedly choose colors. Gradually reduce selection probability to avoid runs.'
colors = list(colors)
n = len(colors)
total = n * maxrun
current_run = 0
for _ in range(numselections):
i = randrange(total - current_run) // maxrun
yield colors[i]
colors[i], colors[-1] = colors[-1], colors[i]
current_run = current_run + 1 if i==n-1 else 1
if __name__ == '__main__':
colors = ['red', 'blue', 'green', 'yellow']
for color in choose(colors, 100, maxrun=4):
print color
注意,这种方法比使用重选技术避免运行的其他答案需要更少的工作量。另外,请注意,运行逐渐淡出,而不是像其他答案一样逐渐消失。
答案 1 :(得分:1)
你显然不关心真正的随机性,所以如果你定义一个距离度量,并随机绘制你的序列,你可以拒绝任何新的绘制,如果它的距离“太接近”上一次绘制,并且只是再画一次。
如果你是从一个有限的集合(比如一包卡片)中抽取,那么整个集合就可以 是一个绘制堆,你的排序将包括在关闭时交换两个元素 如果交换元素变得不可接受,则会找到对,但也拒绝交换伙伴,因此每个交换步骤都会使整个集合得到改进。
如果你的标准不太难以满足,这将很快终止。
答案 2 :(得分:1)
正如ddyer所说,你对随机而不是排序感兴趣。我的解决方案是:
一个工作片段:
from random import randint
from operator import itemgetter
from itertools import islice
def reshuffle(_items, max_consequent):
items = _items[:]
new_order = []
while items:
src_pos = randint(0, len(items)-1)
dest_pos = randint(0, len(new_order))
item = items[src_pos]
new_order.insert(dest_pos, item)
if is_new_order_fine(new_order, max_consequent):
items.pop(src_pos)
else:
new_order.pop(dest_pos)
return new_order
def is_new_order_fine(items, n_max):
return (
not has_consecutive_keys(items, n_max, key=itemgetter('colour')) and
not has_consecutive_keys(items, n_max, key=itemgetter('size')))
# can be optimised - don't check all items, just surrounding N
def has_consecutive_keys(items, n_max, key):
_has_n_keys = False
if len(items) >= n_max:
last_value = key(items[0])
n_consequent = 1
for item in items[1:]: # can optimize by using iterator
if key(item) == last_value:
n_consequent += 1
else:
last_value = key(item)
n_consequent = 1
if n_consequent >= n_max:
_has_n_keys = True
break
return _has_n_keys
请注意,您不需要每次检查目的地列表中的所有项目,在插入的新项目左侧和右侧K(未在代码段中实现)
修改强>:
has_consecutive_keys
中使用groupby(但不进行排序!)答案 3 :(得分:1)
如果连续元素的可能性不是很高(如你的例子),如果条件不满足,我会简单地重新洗牌。正如您所看到的,大多数时候您只需一次尝试即可逃脱,因此非常有效。
In [1]: from random import shuffle
In [2]: from itertools import groupby
In [3]: from collections import Counter
In [4]: def pseudo_shuffle(lst, limit, tries=1):
...: temp = list(lst)
...: shuffle(temp)
...: if max(sum(1 for x in g) for k, g in groupby(temp)) <= limit:
...: return tries #return temp
...: return pseudo_shuffle(lst, limit, tries=tries+1)
In [5]: colors = 30*['red', 'blue', 'green', 'yellow']
In [6]: sizes = [1] * 20 + [2] * 20 + [3] * 20 + [4] * 20 + [5] * 20 + [6] * 20
In [7]: Counter([pseudo_shuffle(colors, 4) for _ in range(1000)])
Out[7]: Counter({1: 751, 2: 200, 3: 38, 4: 10, 5: 1})
In [8]: Counter([pseudo_shuffle(sizes, 4) for _ in range(1000)])
Out[8]: Counter({1: 954, 2: 44, 3: 2})
答案 4 :(得分:0)
抱歉,这不是答案,但很难在评论中发布代码。这是编写consecutive_properties
函数
from operator import itemgetter
from itertools import groupby
def consecutive_properties(seq, field):
return max(sum(1 for x in g) for k,g in groupby(seq, key=itemgetter(field)))
当我理解你的问题时,我会尝试将其转化为答案:)