ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

搜索
EH技术汇-专业的职场技能充电站 妙哉!函数段子手趣味讲函数 Excel服务器-会Excel,做管理系统 效率神器,一键搞定繁琐工作
HR薪酬管理数字化实战 Excel 2021函数公式学习大典 Excel数据透视表实战秘技 打造核心竞争力的职场宝典
让更多数据处理,一键完成 数据工作者的案头书 免费直播课集锦 ExcelHome出品 - VBA代码宝免费下载
用ChatGPT与VBA一键搞定Excel WPS表格从入门到精通 Excel VBA经典代码实践指南
查看: 32681|回复: 72

[分享] 求排列组合结果的自定义函数

[复制链接]

TA的精华主题

TA的得分主题

发表于 2011-6-17 14:04 | 显示全部楼层 |阅读模式
本帖已被收录到知识树中,索引项:其他结构和算法
从m个元素中取n个,产生各种不同的排列……

现在想知道所有排列结果中,第n个位置的具体排列结果,
而不需要事先排出所有结果,则可以使用下述自定义函数。
  1. Function PN(Rng As Range, n, k, Optional InsertWd = "") '给出第k个位置的排列结果
  2.     m = Rng.Count '元素数
  3.     PAM = Application.WorksheetFunction.Permut(m, n)
  4.     If k > PAM Then PN = "> " & PAM: Exit Function '检查k值如果大于可能排列结果,则终止并提示。
  5.    
  6.     Dim s As New Collection '把所有元素加入集合s中待用。
  7.     For i = 1 To m
  8.         s.Add Rng.Cells(i)
  9.     Next
  10.    
  11.     For i = 1 To n - 1 ’按顺序取n-1个值
  12.             j = Int((k - 1) / Application.WorksheetFunction.Permut(m - i, n - i)) Mod (m - i + 1) + 1
  13.             '求k位置序号,按排列值分割以后,再按待排列数,求余转换为序数值,即可从s集合中得到该元素
  14.             '例如:8取4时共1680个排列组合,
  15.        '首先,第1位的排列数应该是按照P(7,3)=210,即8个元素每一个有210种。210 x 8 =1680
  16.        '接下来,第2位的排列数应该是按照P(6,2)=30,即7个元素每一个有30种。30 x 7 =210
  17.             '接下来,第3位的排列数应该是按照P(5,1)=5,即6个元素每一个有5种。5 x 6 =30
  18.             '以此类推,直至倒数第2位结束,因为最后一位的算法不一样的。
  19.   
  20.             PN = PN & s(j) & InsertWd '逐个加入选定元素,并插入分割符号。(默认为空格)
  21.             s.Remove j '从集合s中去掉已经使用过的元素
  22.     Next
  23.     '最后一位,是k位置序号对于剩余数的余数,例如,8取4时取好3个剩最后一位时,还剩5个元素可选
  24.     PN = PN & s((k - 1) Mod (m - n + 1) + 1)
  25.    
  26. End Function
复制代码
请看附件。

任意序号位置排列结果.zip

8.94 KB, 下载次数: 575

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-6-17 14:13 | 显示全部楼层
有了上面这个函数,那么求m个元素取n个时的全部排列组合结果,
也可以简单地、通用性地,以下面这样的代码来求所有结果,
而不必考虑m、n值不同时,写循环代码的层数要增加了。

当然,如果使用递归迭代方式的话,也是可以的。
速度或许更快。
  1. Sub PLtest()
  2.     Dim Rng As Range
  3.     Set Rng = Selection
  4.     m = Rng.Count
  5.     n = m
  6.     InsertWd = "-"   
  7.    
  8.     PAM = Application.WorksheetFunction.Permut(m, n)
  9.     ReDim d(PAM, n)
  10.     d(0, 0) = "No"
  11.     d(0, 1) = "PL"
  12.    
  13.     For i = 1 To PAM
  14.         d(i, 0) = i
  15.         d(i, 1) = PN(Rng, n, i, InsertWd)
  16.     Next
  17.    
  18.     ActiveCell.Offset(5, 0).Resize(PAM + 1, 2) = d

  19. End Sub
复制代码
这里的代码,是把排列元素横向排在同一列中处理的。

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-6-23 08:00 | 显示全部楼层
求任意序号位置组合结果的自定义函数也已经编写好了:
  1. Function CN(Rng As Range, n, k, Optional InsertWd = "")
  2.     m = Rng.Count
  3.     CAM = Application.WorksheetFunction.Combin(m, n) '计算最大组合数
  4.     If k > CAM + 0.1 Then CN = "> " & CAM: Exit Function
  5.    
  6.     Dim s As New Collection
  7.     For i = 1 To m
  8.         s.Add Rng.Cells(i) '集合s中加入组合对象元素
  9.     Next
  10.    
  11.     For i = 1 To n - 1
  12.         For j = 1 To m - n + 1
  13.             t = Application.WorksheetFunction.Combin(m - i - p, m - n - p) '按组合公式排列
  14.             If k > t + 0.1 Then
  15.                 k = k - t
  16.                 p = p + 1
  17.             Else
  18.                 l = l + j
  19.                 Exit For
  20.             End If
  21.         Next
  22.         CN = CN & s(l) & InsertWd
  23.     Next
  24.     CN = CN & s((k - 1) Mod (m - l + 1) + l + 1) '最后一位的组合结果可以直接求余计算。
  25. End Function
复制代码

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2011-7-7 08:08 | 显示全部楼层
群子小姐,你好,有一程序想不明白,能否详加解释,谢谢
j = Int((k - 1) / Application.WorksheetFunction.Permut(m - i, n - i)) Mod (m - i + 1) + 1
这个看了半天,也没明白,为什么是这个位置呢?

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-7-7 12:16 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
前面已经有解释了呀。


'求k位置序号,按排列值分割以后,再按待排列数,求余转换为序数值,
即可从s集合中得到该元素

'例如:8取4时共1680个排列组合,

'首先,第1位的排列数应该是按照P(7,3)=210,即8个元素每一个有210种。
210 x 8 =1680

j指的是通过计算定下来的元素序号。
j = Int((k - 1) / Application.WorksheetFunction.Permut(m - i, n - i)) Mod (m - i + 1) + 1

在这里:
m是组合总元素数=8,n是排列抽取数=4,
i指的是排列抽取次数=1
Application.WorksheetFunction.Permut(m - i, n - i)是指用Exce内置公式求排列值。
即相当于计算 P(m-i,n-i) 的结果,P(8-1,4-1)=P(7,3)=210

接下来,这样理解:
把1680个组合结果按照P(7,3)=210,即210个序号一组分区,
则可以得到8个分区,分别对应于1到8个元素。


具体做法是,对于求值序号参数k,按(k-1) / 210求整数的结果,
得到0-7之间的值,对应第0-7号元素。

下面以此类推。


       '接下来,第2位的排列数应该是按照P(6,2)=30,即7个元素每一个有30种。30 x 7 =210

            '接下来,第3位的排列数应该是按照P(5,1)=5,即6个元素每一个有5种。5 x 6 =30


…………

TA的精华主题

TA的得分主题

发表于 2011-7-7 20:04 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
j = Int((k - 1) / Application.WorksheetFunction.Permut(m - i, n - i)) Mod (m - i + 1) + 1
这个前面的好理解,Int((k - 1) / Application.WorksheetFunction.Permut(m - i, n - i)) 就是你解释的意思,那后面加上这个求余数Mod (m - i + 1) + 1,要怎么解释呢?
谢谢,阿里亚多!

[ 本帖最后由 cumulonimbus 于 2011-7-7 20:05 编辑 ]

TA的精华主题

TA的得分主题

 楼主| 发表于 2011-7-7 22:13 | 显示全部楼层
后面的求余计算是因为,每计算完一次,剩余元素数量就减少一个。

所以,是m-i+1

通过求余的方法,得到相应的元素。

++++++++

其实,你只要运行一下宏代码,设置断点逐步运行,然后慢慢观察各个变量的变化过程就行了。

TA的精华主题

TA的得分主题

发表于 2011-7-7 23:37 | 显示全部楼层
群子小姐,你的意思是选8抽4的组合,第一位位置是int((k-1)/210),第二位位置是int((k-1)/30),第三位位置是int((k-1)/5)。接下去我就搞不明白了。
我这个人对算法比较笨。能否举一个实例作为说明。谢谢
为什么求余数就能转换为序数值呢?这一点没想明白。如果想通了这一点,一切都好理解了。能否劳烦再次解释一下否

[ 本帖最后由 cumulonimbus 于 2011-7-7 23:48 编辑 ]

TA的精华主题

TA的得分主题

发表于 2011-7-7 23:51 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
裙子老师好厉害~

TA的精华主题

TA的得分主题

发表于 2011-7-7 23:53 | 显示全部楼层
一个过程频繁调用工作表函数 不知道速度是不是会慢点了

Application.WorksheetFunction
您需要登录后才可以回帖 登录 | 免费注册

本版积分规则

手机版|关于我们|联系我们|ExcelHome

GMT+8, 2024-11-17 02:58 , Processed in 0.046119 second(s), 12 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

沪公网安备 31011702000001号 沪ICP备11019229号-2

本论坛言论纯属发表者个人意见,任何违反国家相关法律的言论,本站将协助国家相关部门追究发言者责任!     本站特聘法律顾问:李志群律师

快速回复 返回顶部 返回列表