ExcelHome技术论坛

 找回密码
 免费注册

QQ登录

只需一步,快速开始

快捷登录

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

[分享] 中西排名自定义函数

[复制链接]

TA的精华主题

TA的得分主题

发表于 2014-8-6 17:10 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
香川群子 发表于 2014-8-6 16:54
2楼代码及附件,是可以对任意对象(数值或文本)进行排序的通用代码。

初步感觉此时的做法和maditate的字典的思路有点类似了,但是我没有看到字典的使用……
先大胆猜下,空闲了定当倾力研究……

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-6 17:11 | 显示全部楼层
本帖最后由 香川群子 于 2014-8-6 17:42 编辑

10楼算法精妙之处:

1. 把数值转换为整数,然后以整数数值直接作为排序索引……【整数直接高效数组下标索引法自排序】
   ReDim a(m * 10 ^ d)
   ……
   t = r * 10 ^ d: a(t) = a(t) & "," & i


2. 使用Split Trim Join 函数嵌套,一次性计算得到【分档排序结果】
    b = Split(WorksheetFunction.Trim(Join(a)))


3. Do……Loop 循环区分解决排序对象处理方法 【Z-A排序和A-Z排序共用代码】
    If z = 1 Then i = -1: u = UBound(b) Else z = -1: i = UBound(b) + 1: u = 0

4. 使用k(2)数组,同时计算和记录3种排序结果【自然序列k(0)、西式排名k(1)、中式排名k(2)】
   k(1) = k(0) + 1: k(2) = k(2) + 1
   ……
   k(0) = k(0) + 1
   而且整理排序结果时,不再需要对比本行值和上一个值的大小……
    这样不仅代码简化、因为省去了If判断、速度效率又大为提高。


5. 利用p参数对应k(2)数组的列位置,直接输出对应的排序结果
    c(s(j), 1) = k(p)

以上达到了代码大部分通用的结果。

6. 输出结果为整体数组结果、或单个位置的排序结果
    If n > 0 Then Rnk2 = c(n) Else If n = 0 Then Rnk2 = WorksheetFunction.Transpose(c) Else Rnk2 = c并且考虑到了在工作表多行单列区域、或单行多列区域中以数组三键区域公式方式返回结果的3种处理方式。


呵呵。




评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-6 17:22 | 显示全部楼层
aoe1981 发表于 2014-8-6 17:10
初步感觉此时的做法和maditate的字典的思路有点类似了,但是我没有看到字典的使用……
先大胆猜下,空闲 ...

为什么需要用字典? 根本不需要的。


但是,把整数作为数组下标索引的【数组自排序法】有一定的使用风险。

如果数字最小值和最大值之间间隔范围很大,即数组密度很低时,
  显然定义一个巨大的数组会占用很多的内存空间,而且最后结果的整理也比较耗时……
    到一定程度就会得不偿失、还不如直接排序来得方便、快捷。


至于重复记录很多,导致数组内无法存储的问题基本可以无视。一般是足够的。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

发表于 2014-8-6 18:38 | 显示全部楼层
已收藏到我的代码助手默认仓库
的弄给我代码助手完善后
有空会把香川大神的经典代码都收入我仓库里了

点评

香川的代码都是精品,应该好好整理!  发表于 2014-8-12 07:36

TA的精华主题

TA的得分主题

发表于 2014-8-12 08:41 | 显示全部楼层
香川群子 发表于 2014-8-6 13:33
上代码、上附件:

  初步研究了下:
  1.     On Error GoTo IsArr
  2.     u = Rng_Arr.Count '针对区域
  3.     GoTo GetU
  4. IsArr:
  5.     u = UBound(Rng_Arr) '针对数组
  6. GetU:
  7.     ReDim ar(1 To u, 1 To 2) '1列存储原始数据(元素),2列记录原始次序(位置)
复制代码
  此处数组与区域的识别、防错,简洁巧妙,学习了……
  至于接下来,我可能得先了解下“希尔排序”的原理了……看不下去了……呵呵

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-12 09:31 | 显示全部楼层
本帖最后由 香川群子 于 2014-8-12 09:34 编辑
aoe1981 发表于 2014-8-12 08:41
  初步研究了下:  此处数组与区域的识别、防错,简洁巧妙,学习了……
  至于接下来,我可能得先 ...

呵呵,都是现想出来的办法……
用了Err处理、Err Goto……应该不是好方法。

其实,直接在自定义函数中指定参数会更简单。

因为在什么场合使用,写代码时是能分辨、区别的。

但要是能自动化、智能化,在不是很影响使用环境的时候,还是有好处的……

TA的精华主题

TA的得分主题

发表于 2014-8-12 09:42 | 显示全部楼层
[广告] VBA代码宝 - VBA编程加强工具 · VBA代码随查随用  · 内置多项VBA编程加强工具       ★ 免费下载 ★      ★使用手册
香川群子 发表于 2014-8-12 09:31
呵呵,都是现想出来的办法……
用了Err处理、Err Goto……应该不是好方法。

“希尔排序,也称递减增量排序算法,是插入排序的一种更高效的改进版本。希尔排序是非稳定排序算法。

希尔排序是基于插入排序的以下两点性质而提出改进方法的:

插入排序在对几乎已经排好序的数据操作时, 效率高, 即可以达到线性排序的效率
但插入排序一般来说是低效的, 因为插入排序每次只能将数据移动一位”

以上来自维基百科。
希尔排序看了一大堆,理论居多,应该有简单数据的分步模拟,就好理解了……
呵呵,再试着做做,大概比较的思路还是了解了一下……

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-12 11:02 | 显示全部楼层
希尔排序的实质是:
以步长为h(1<=h<m) 进行分组排序,
然后逐步改变步长(减小)后继续分组排序,
直至最后按步长h=1进行组内排序,完成全部排序。


…………
首先,介绍按步长h进行分组排序的函数过程:
Function InsertSort1(trr, L&, U&, Optional h& = 1) '一維
    Dim i&, j&, t
    For i = L + h To U
        t = trr(i)
        For j = i - h To L Step -h
            If trr(j) < t Then Exit For
            trr(j + h) = trr(j)
        Next
        trr(j + h) = t
    Next
End Function

此时,如果h=1,就是一个简单的插入排序:

步长h=1时
Function InsertSort1(trr, L&, U&) '一维数组trr L=LBound()、U=UBound()
    Dim i&, j&, t
    For i = L + 1 To U
        t = trr(i)
        For j = i - 1 To L Step -1
            If trr(j) < t Then Exit For
            trr(j + 1) = trr(j)
        Next
        trr(j + 1) = t
    Next
End Function

你先研究一下这个排序过程吧。

评分

1

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-12 11:10 | 显示全部楼层
本帖最后由 香川群子 于 2014-8-12 11:15 编辑

然后,下面就是完整的一个希尔排序算法代码例子
(其实同样的希尔排序算法代码,不同的人写法也不同,但基本循环结构、算法是一样的)

  1. Sub ShellSortTest() '调用希尔排序的代码测试实例
  2.       arr = Array(5, 4, 2, 1, 5, 8, 7, 2, 7, 9, 3, 6)
  3.       Call ShellSort5(arr)
  4.       Stop
  5. End Sub

  6. Sub ShellSort5(arr, Optional n& = 5) '简化希尔排序过程 序列参数可取 n=7,5,4 但我觉得=5较好
  7.     Dim h&, i&, j&, k&, L&, U&, t
  8.     L = LBound(arr): U = UBound(arr): h = U - L + 1
  9.     Do
  10.         h = (h \ n) * 2 + 1 '按此简化希尔序列计算方法,计算每次的步长h直至h=1
  11.         Call InsertSort1(arr, L, U, h) '根据计算得到的步长h,调用插入排序算法函数过程代码
  12.     Loop Until h = 1
  13. End Sub

  14. Sub InsertSort1(trr, L&, U&, Optional h& = 1) '一维数组按步长h进行插入排序
  15.     Dim i&, j&, t
  16.     For i = L + h To U
  17.         t = trr(i)
  18.         For j = i - h To L Step -h
  19.             If trr(j) < t Then Exit For
  20.             trr(j + h) = trr(j)
  21.         Next
  22.         trr(j + h) = t
  23.     Next
  24. End Sub
复制代码

评分

2

查看全部评分

TA的精华主题

TA的得分主题

 楼主| 发表于 2014-8-12 12:11 | 显示全部楼层
[广告] Excel易用宝 - 提升Excel的操作效率 · Excel / WPS表格插件       ★免费下载 ★       ★ 使用帮助
对希尔排序算法的进一步说明:

m个元素,按步长h 分为 m\h组,然后在每一个小组内进行快速比较、插入排序。
这样的结果,所有大数都会很快沉淀到最后几组。

缩小步长、反复数次以后,所有的数基本都已经按较小几行内差距较小、大数基本沉淀的结果。

于是,最后按h=1倒序检查每一个元素,只要检查到之前的值更小就可以退出
(由于前面部分已经排序完成,所以不会有更小的了)

这样事实上差不多间隔2-3行就能完成排序。(h=1之前的最小分组步长h=3)

呵呵。

详细可参考附图:



Shell.jpg

点评

是因为上一步步长是3,所以步长为1时只需交换2到3行就能交换到位吧……  发表于 2014-8-12 21:27
“是,最后按h=1倒序检查每一个元素,只要检查到之前的值更小就可以退出” 这一句是关键!  发表于 2014-8-12 14:48

评分

1

查看全部评分

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

本版积分规则

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

GMT+8, 2024-11-17 18:55 , Processed in 0.049786 second(s), 9 queries , Gzip On, MemCache On.

Powered by Discuz! X3.4

© 1999-2023 Wooffice Inc.

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

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

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