ruby – 基于不同权重随机混洗数组的算法
我有一组我想随机随机播放的元素,但每个元素都有不同的优先级或权重.因此,具有更大权重的元素必须具有更多的概率才能在结果的顶部.
我有这个数组: elements = [ { :id => "ID_1",:weight => 1 },{ :id => "ID_2",:weight => 2 },{ :id => "ID_3",:weight => 6 } ] 我想要改变它,因此id为“ID_3”的元素首先比元素“ID_1”多6倍,概率比元素“ID_2”高3倍. 更新 澄清:一旦你选择了第一个位置,其他元素将使用相同的逻辑争取休息位置. 解决方法
我可以想到两种方法来解决它,虽然我的直觉告诉我应该修改
Fisher-Yates以实现它更好:
O(n * W)解决方案:(简单编程) 第一种方法,根据权重创建重复项(与您的方法相同),并填充新列表.现在在这个列表上运行一个标准的shuffle(fisher-yates).迭代列表并丢弃所有重复项,并仅保留每个元素的第一次出现.这在O(n * W)中运行,其中n是列表中元素的数量,W是平均权重(伪多项式解). O(nlogn)解决方案:(难以编程) 第二种方法是创建元素权重总和列表: sum[i] = weight[0] + ... + weight[i] 现在,绘制一个介于0和sum [n]之间的数字,并选择其总和大于/等于此随机数的第一个元素. 这在O(n ^ 2 * logn)中运行 可以通过创建二叉树而不是列表来进一步增强它,其中每个节点还存储整个子树的权重值. 构造和使用二叉树的说明. 假设你有元素= [a,b,c,d,e,f,g,h,i,j,k,l,m],权重= [1,2,3,1,1] 首先构造一个几乎完整的二叉树,并填充其中的元素.请注意,树不是二进制搜索树,只是常规树,因此元素的顺序无关紧要 – 我们以后不需要维护它. 您将获得类似以下树的内容: 图例:w – 该节点的权重,整个子树的权重的sw和. 接下来,计算每个子树的权重总和.从叶子开始,计算s.w = w.对于每个其他节点,计算s.w = left-> s.w right-> s.w,从下向上填充树(post order traversal). 构建树,填充树并计算s.w.每个节点都在O(n)中完成. 现在,你需要迭代地选择0到权重之和的随机数(根的s.w.值,在我们的例子中为25).设这个数字为r,并为每个这样的数字找到匹配节点. if `r< root.left.sw`: go to left son,and repeat. else if `r<root.left.sw + root.w`: the node you are seeking is the root,choose it. else: go to `root.right` with `r= r-root.left.sw - root.w` 例如,选择r = 10: Is r<root.left.sw? Yes. Recursively invoke with r=10,root=B (left child) Is r<root.left.sw No. Is r < root.left.sw + root.w? No. Recursively invoke with r=10-6-2=2,and root=E (right chile) Is r<root.left.sw? No. Is r < root.left.sw + root.w? Yes. Choose E as next node. 这是在每次迭代中以O(h)= O(logn)完成的. 现在,您需要删除该节点,并重置树的权重. 第一个开关: 然后重新计算: 请注意,只需要重新计算两条路径,每条路径的深度最多为O(logn)(图中的橙色节点为橙色),因此删除和重新计算也是O(logn). 现在,你得到了一个新的二叉树,修改了权重,你就可以选择下一个候选者,直到树用完为止. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |