我参与了一项研究,受试者从4个类别(1,2,3,4)输入,每个类别有4个单词(a,b,c,d)。每个受试者的单词顺序需要随机化,但连续的单词应该来自不同的类别。我是一个相当新的编码,并一直在努力的算法,可以做到这一点,但它是越来越长和复杂,我觉得应该有一个更简单的方法来做,是不容易出错。任何帮助将不胜感激!
nhjlsmyf1#
下面是我在评论中建议的“蛮力”方法的实现。
source = ["1a","1b","1c","1d","2a","2b","2c","2d","3a","3b","3c","3d","4a","4b","4c","4d"] valid = [] num_valid = 100 until valid.length >= num_valid do candidate = source.shuffle if candidate.each_cons(2).all? {|a,b| a[0] != b[0] } valid |= [candidate] end end
运行此命令后,valid将包含100个不同的有效列表。
valid
qq24tv8q2#
分解问题(以一种方式;还有其他的):
[a, b, a, ...]
[c, b, d, a, c, b, d, a, ...]
[c, b, d, a, b, a, c, d, ...]
cats.length * words.length
详细但清楚地说:这是一个类别shuffle的平面图,每个words.length一个,如果需要,可以重新shuffle (如果需要),直到shuffle的第一个条目不同于前一个shuffle的最后一个条目。将其分成words.length块,并将每个类别与混洗单词列表中的单词相关联。很可能是一个简单的实现,但相当清晰,还有十几行或两行代码,没有试图变得聪明(我绝对没有:rofl:)。换句话说:它不需要非常复杂就能满足需求--也许值得尝试以不同的方式来考虑它。
words.length
tquggr8v3#
这里有一个方法,它与@user513951的方法略有不同,但可能在数组较大时更有效。与前面的方法一样,它会循环,直到随机选择具有所需的属性。这两种方法都会产生统计随机样本(在软件中产生真正的随机值时受到限制)。前面的方法提取块{ |a,b| a[0] != b[0] }中两个字符串的类别。有不超过9个类别。如果类别可能由两个或更多的数字组成,数字将不得不从字符串的前面剥离。可能有一个前-处理步骤,使a和b在前面提到的块两个元素数组中(这将需要在最后进行一些整理)。这将是对早期方法的简单更改。如果我们被赋予
{ |a,b| a[0] != b[0] }
a
b
a = ["1a", "1b", "1c", "1d", "20a", "20b", "20c", "20d", "3a", "3b", "3c", "3d", "41a", "41b", "41c", "41d"]
我们可以从计算1开始
arr = a.map { |s| s.split(/(?<=\d)(?=\D)/) } #=> [["1", "a"], ["1", "b"], ["1", "c"], ["1", "d"], # ["20", "a"], ["20", "b"], ["20", "c"], ["20", "d"], # ["3", "a"], ["3", "b"], ["3", "c"], ["3", "d"], # ["41", "a"], ["41", "b"], ["41", "c"], ["41", "d"]]
与前面的方法相比,我建议的唯一变化是首先获得满足所需属性的类别随机序列,一旦找到,我将把它转换为一个返回值,该返回值仍然是一个无偏样本选择。实际上,我将块{ |a,b| a[0] != b[0] }简化为{ |x,y| x != y },其中x和y是类别。首先创建一个哈希,将类别Map到它们的单词数组。
{ |x,y| x != y }
x
y
h = arr.each_with_object(Hash.new { |h,k| h[k] = [] }) do |(cat,word),h| h[cat] << word end #=> {"1"=>["a", "b", "c", "d"], "20"=>["a", "b", "c", "d"], # "3"=>["a", "b", "c", "d"], "41"=>["a", "b", "c", "d"]}
接下来,打乱每个值的元素以在以后保持随机性。
h.transform_values!(&:shuffle) #=> {"1"=>["b", "d", "a", "c"], "20"=>["b", "c", "a", "d"], # "3"=>["d", "b", "c", "a"], "41"=>["a", "d", "c", "b"]}
然后选择按其值的大小复制的键(对于所有键,不需要完全相同)。
keys = h.each_with_object([]) { |(k,v),a| a.concat([k]*v.size) } #=> ["1", "1", "1", "1", "20", "20", "20", "20", # "3", "3", "3", "3", "41", "41", "41", "41"]
现在找到一个满足所需属性的类别序列。
loop do n += 1 keys.shuffle! break keys if keys.each_cons(2).all? { |x,y| x != y } end #=> ["1", "41", "3", "20", "41", "20", "1", "20", "3", "41", "1", "3", "1", "20", "41", "3"]
(Upon多次执行此操作,总是需要70次迭代才能找到有效序列。最后,为每个类别添加随机化的单词,以生成一个满足所需属性的随机数组。
keys.map { |k| "#{k}#{h[k].shift}" } #=> ["1b", "41a", "3d", "20b", "41d", "20c", "1d", "20a", # "3b", "41c", "1a", "3c", "1c", "20d", "41b", "3a"]
(?<=\d)
(?=\D)
3条答案
按热度按时间nhjlsmyf1#
下面是我在评论中建议的“蛮力”方法的实现。
运行此命令后,
valid
将包含100个不同的有效列表。qq24tv8q2#
分解问题(以一种方式;还有其他的):
[a, b, a, ...]
怎么样[c, b, d, a, c, b, d, a, ...]
(每个单词组相同的猫)或[c, b, d, a, b, a, c, d, ...]
(不同的猫)cats.length * words.length
(组合数)详细但清楚地说:这是一个类别shuffle的平面图,每个
words.length
一个,如果需要,可以重新shuffle (如果需要),直到shuffle的第一个条目不同于前一个shuffle的最后一个条目。将其分成
words.length
块,并将每个类别与混洗单词列表中的单词相关联。很可能是一个简单的实现,但相当清晰,还有十几行或两行代码,没有试图变得聪明(我绝对没有:rofl:)。换句话说:它不需要非常复杂就能满足需求--也许值得尝试以不同的方式来考虑它。
tquggr8v3#
这里有一个方法,它与@user513951的方法略有不同,但可能在数组较大时更有效。与前面的方法一样,它会循环,直到随机选择具有所需的属性。这两种方法都会产生统计随机样本(在软件中产生真正的随机值时受到限制)。
前面的方法提取块
{ |a,b| a[0] != b[0] }
中两个字符串的类别。有不超过9个类别。如果类别可能由两个或更多的数字组成,数字将不得不从字符串的前面剥离。可能有一个前-处理步骤,使a
和b
在前面提到的块两个元素数组中(这将需要在最后进行一些整理)。这将是对早期方法的简单更改。如果我们被赋予
我们可以从计算1开始
与前面的方法相比,我建议的唯一变化是首先获得满足所需属性的类别随机序列,一旦找到,我将把它转换为一个返回值,该返回值仍然是一个无偏样本选择。
实际上,我将块
{ |a,b| a[0] != b[0] }
简化为{ |x,y| x != y }
,其中x
和y
是类别。首先创建一个哈希,将类别Map到它们的单词数组。
接下来,打乱每个值的元素以在以后保持随机性。
然后选择按其值的大小复制的键(对于所有键,不需要完全相同)。
现在找到一个满足所需属性的类别序列。
(Upon多次执行此操作,总是需要70次迭代才能找到有效序列。
最后,为每个类别添加随机化的单词,以生成一个满足所需属性的随机数组。
(?<=\d)
是一个 * 正向后查找 *,它Assert匹配的前面是一个数字。(?=\D)
是一个 * 正向前查找 *,它Assert匹配的后面是一个字符而不是一个数字。因此,这个正则表达式匹配字符串中最后一个数字和第一个非数字之间的零宽度位置。