在日常工作或生活中,我们经常面临抽奖的问题,比如从100个人中随机抽取4个不同的人,发送3万枚津巴布韦币。有一个专业术语叫不重复随机抽样,也就是说样本中的单位只能抽一次,抽中的单位不再放回整体。
举个例子。
如下图所示,a列是人名,现在需要动态抽取4个不重复的人,模拟结果见c列。
这样的问题,Excel解法有很多……很多……。打个响声,分享函数、PowerQuery、VBA、SQL等常用解法。
1.是函数解。
解决方法1:辅助列。
对大多数朋友来说,最简单的解决办法就是使用辅助列。
B2单元格输入以下公式,复制填充,获得一列随机值。
=RAND()
C2单元格输入以下公式,复制并填充到C2:C5区域。
=INDEX(A:A,MATCH(LARGE(B:B,ROW(A1),B:B,0)公式依次从b列获得第一、2、3、4个最大值的索引号,从a列获得人名,返回结果模拟如下。
解法2:数组公式
使用COUNTIF函数来判断A中人名是否存在结果区域,如果不存在,则返回相应的行号,使用SMALL函数随机取一个值,然后使用INDEX函数返回相应的结果。
By:Lf
公式写在C2单元格,蓝色部分是思路核心
=INDEX(A:A,SMALL(IF(COUNTIF(C$1:C1,A$2:A$28)=0,ROW(A$2:A$28)),RANDBETWEEN(1,27-ROW(A1)))
解法3:区域数组公式
使用RANDBETWEEN函数从2到999获得27个随机值,加权100倍后加上行号,然后使用SMALL函数依次取最小值,使用RIGHT函数取出行号,最后使用INDEX函数根据行号取得结果。
值得注意的是,这是一个区域数组公式,首先要选择C2:C5区域,然后在编辑栏中输入公示,同时按三个键完成公式输入。
蓝色部分是思路核心
=INDEX(A:A,RIGHT(SMALL(RANDBETWEEN(1^ROW(2:28),999)/1%+ROW(2:28),ROW(1:4)),2))
解法4:动态数组公式
365版本才可用
=INDEX(SORTBY(A2:A28,RANDARRAY(27)),SEQUENCE(4))
RANDARRAY(13)生成13个随机值,SORTBY据此将A2:A14区域的数据排序,最后再使用INDEX函数取前4个即为结果——思路其实等同于辅助列。
2,Power Query解
生成一列随机值,排序后取前四即可。
let
源=Excel.CurrentWorkbook(){[Name="表1"]}[Content],
随机值列=Table.Buffer(Table.AddColumn(源,"a",each Number.Random())),
结果 =Table.SelectColumns(Table.FirstN(Table.Sort(随机值列,"a"),4),"姓名")
in
结果
3,VBA解
使用了洗牌法,参考代码如下:
代码解析见注释
Sub ByVBA()
Dim r, i&, t, x&, n&
Randomize '随机种子初始化
r = Worksheets("数据源").Range("a2:a" & Worksheets("数据源").Cells(Rows.Count, 1).End(xlUp).Row) '将学生喊出教室,装入数组r
n = 4 '抽4个人
For i = 1 To n
x = Int(Rnd() * (UBound(r) - i + 1)) + i '依次从第1~2……位置起随机抽取
t = r(x, 1) '被抽中的出列
r(x, 1) = r(i, 1) '前列的学生去被抽中学生的位置
r(i, 1) = t '被抽中的学生站到前排去
Next
With Worksheets("vba")
.Select
.Columns(1).Clear
.Range("a1") = "结果"
.Range("a2").Resize(n, 1) = r '将队伍的前4名(抽取结果)放入抽奖区
End With
End Sub
我们来看下图这个例子,我想要筛选出区域为华南地区的数据:
4,SQL解
SELECT TOP 4 姓名
FROM [数据源$]
ORDER BY
RND(
ASC(姓名)-TIMER()