Excel·VBA数组分组问题

news2024/9/24 23:27:06

看到一个帖子《excel吧-数据分组问题》,对一组数据分成4组,使每组的和值相近
在这里插入图片描述

目录

    • 代码思路
    • 1,分组形式、可分组数
      • 代码1
      • 代码2
      • 代码2举例
    • 2,数组所有分组形式
      • 举例

  • 这个问题可以转化为2步:第1步,获取一组数据的所有分组形式;第2步,对所有分组形式计算其方差,方差最小的则是和值最相近的一组
  • 本文为第1步,获取一组数据的所有分组形式

代码思路

在这里插入图片描述

  • n个元素分成m组,每组元素个数最小值为1,最大值为n-m+1,可以通过组合获取所有分组形式
  • 所有元素进行分组,即组合问题,4组组合数相乘就是一种分组形式的分组数(注意:因为组合不区分顺序,因此当分组内组合的指数为1时,不管底数是多少,分组数都为1)。通过观察上图,可以发现9种元素分成4组,有6种分组形式共18480种分组
  • 有了分组形式和分组数,那就可以获取每种分组形式中的每个分组元素组成
  • 函数调用:以下代码调用了《Excel·VBA数组冒泡排序函数》bubble_sort函数,《Excel·VBA数组组合函数、组合求和》combin_arr1函数(如需使用代码需复制)

1,分组形式、可分组数

有2种代码及结果输出形式,主要使用第2种

代码1

Function 可分组数(ByVal n&, ByVal m&, Optional ByVal mode& = 1)
    '计算分组成不重复的组数,可选择最终返回组数,和每格内含元素个数的二维数组(从1开始计数)
    'n元素个数;m需要分成几组;mode为1时返回组数,为2时返回二维数组(组数行*m列)
    Dim arr, brr, crr, drr, x&, y&, i&, j&, t, tt, a, b, d, s, bb, k, krr, res
    ReDim arr(1 To n - m + 1), brr(1 To n - m + 1)  '组合法计算组数,最大值为n - m + 1
    x = n - m + 1: arr(1) = 1: brr(1) = m - 1  'arr元素个数,brr重复次数
    If m = 1 Then
        If mode = 1 Then
            可分组数 = 1: Exit Function
        ElseIf mode = 2 Then
            ReDim res(1 To 1, 1 To 1): res(1, 1) = n: 可分组数 = res: Exit Function
        End If
    End If
    For i = 2 To x  '每个数字各最多需要的数量
        arr(i) = i: t = n \ i: tt = n / i  '整除、除,判断是否相等
        If t = tt And t = m Then  '整除,且正好分配为m组
            brr(i) = t
        Else
            For j = t To 1 Step -1
                a = i * j + (m - j)  '数字i有j个,其余为1,判断和是否<=n
                If a <= n Then brr(i) = j: Exit For
            Next
        End If
    Next
    s = WorksheetFunction.Sum(brr): ReDim crr(1 To s)
    For i = x To 1 Step -1  '倒序、正序平均分组都在最后
        For j = 1 To brr(i)
            y = y + 1: crr(y) = arr(i)  '所有数字按个数写入一个数组
        Next
    Next
    '对数组crr选m个进行组合,获取和值为n,且组合形式唯一的所有组合
    Dim dict As Object: Set dict = CreateObject("scripting.dictionary"): x = 0
    drr = combin_arr1(crr, m)  '调用函数返回组合,一维嵌套数组
    For Each d In drr  '遍历组合,和值等于n;再降序排序,写入字典
        s = WorksheetFunction.Sum(d)
        If s = n Then b = bubble_sort(d, "-"): bb = Join(b, "+"): dict(bb) = ""
    Next
    '对符合条件的组合形式,计算分成m组的组数,以及每种组合形式的组数
    For Each k In dict.keys
        krr = Split(k, "+"): s = n: y = 1
        For i = 0 To m - 1   '分组中只有1个元素的无所谓顺序,排除
            If krr(i) > 1 Then y = y * Application.Combin(s, krr(i)): s = s - krr(i)
        Next
        dict(k) = y: x = x + y    'y每种组合形式的组数,x总组数
    Next
    If mode = 1 Then    '输出结果
        可分组数 = x
    ElseIf mode = 2 Then
        ReDim res(1 To x, 1 To m): i = 0
        For Each k In dict.keys
            krr = Split(k, "+")
            For y = 1 To dict(k)  '重复写入dict(k)行krr数组
                i = i + 1
                For j = 0 To m - 1
                    res(i, j + 1) = krr(j)
                Next
            Next
        Next
        可分组数 = res
    End If
End Function

代码2

Function 可分组数2(ByVal n&, ByVal m&, Optional ByVal mode& = 1)
    '计算分组成不重复的组数,可选择最终返回总组数,或每种组合形式的组数的二维数组(从1开始计数)
    'n元素个数;m需要分成几组;mode为1时返回组数,为2时返回二维数组,1列组合形式1列组数
    Dim arr, brr, crr, drr, x&, y&, i&, j&, t, tt, a, b, d, s, bb, k, res
    ReDim arr(1 To n - m + 1), brr(1 To n - m + 1)  '组合法计算组数,最大值为n - m + 1
    x = n - m + 1: arr(1) = 1: brr(1) = m - 1  'arr元素个数,brr重复次数
    If m = 1 Or n = m Then
        If mode = 1 Then
            可分组数2 = 1
        ElseIf mode = 2 Then
            ReDim res(1 To 1, 1 To 2): res(1, 2) = 1
            res(1, 1) = WorksheetFunction.Rept("1", m): 可分组数2 = res
        End If
        Exit Function
    End If
    For i = 2 To x  '每个数字各最多需要的数量
        arr(i) = i: t = n \ i: tt = n / i  '整除、除,判断是否相等
        If t = tt And t = m Then  '整除,且正好分配为m组
            brr(i) = t
        Else
            For j = t To 1 Step -1
                a = i * j + (m - j)  '数字i有j个,其余为1,判断和是否<=n
                If a <= n Then brr(i) = j: Exit For
            Next
        End If
    Next
    s = WorksheetFunction.Sum(brr): ReDim crr(1 To s)
    For i = x To 1 Step -1  '倒序、正序平均分组都在最后
        For j = 1 To brr(i)
            y = y + 1: crr(y) = arr(i)  '所有数字按个数写入一个数组
        Next
    Next
    '对数组crr选m个进行组合,获取和值为n,且组合形式唯一的所有组合
    Dim dict As Object: Set dict = CreateObject("scripting.dictionary"): x = 0
    drr = combin_arr1(crr, m)  '调用函数返回组合,一维嵌套数组
    For Each d In drr  '遍历组合,和值等于n;再降序排序,写入字典
        s = WorksheetFunction.Sum(d)
        If s = n Then b = bubble_sort(d, "-"): bb = Join(b, "+"): dict(bb) = ""
    Next
    '对符合条件的组合形式,计算分成m组的组数,以及每种组合形式的组数
    For Each k In dict.keys
        krr = Split(k, "+"): s = n: y = 1
        For i = 0 To m - 1   '分组中只有1个元素的无所谓顺序,排除
            If krr(i) > 1 Then y = y * Application.Combin(s, krr(i)): s = s - krr(i)
        Next
        dict(k) = y: x = x + y    'y每种组合形式的组数,x总组数
    Next
    If mode = 1 Then    '输出结果
        可分组数2 = x
    ElseIf mode = 2 Then
        ReDim res(1 To dict.Count, 1 To 2): i = 0
        For Each k In dict.keys
            i = i + 1: res(i, 1) = k: res(i, 2) = dict(k)
        Next
        可分组数2 = res
    End If
End Function

代码2举例

Sub 可分组数2举例()
    arr = 可分组数2(9, 4, 2)
    If IsArray(arr) Then
        [a1].Resize(UBound(arr), UBound(arr, 2)) = arr
    Else
        Debug.Print arr
    End If
End Sub

在这里插入图片描述
生成的分组形式和分组数都和手工计算一致
代码1的输出结果是上图A列每行按"+"号拆分成4列及重复对应B列数字行数,最终生成结果为18480行*4列

2,数组所有分组形式

  • 为方便后续计算方差,返回结果有分组和值和分组字符串2种形式。可以先调用函数获取和值计算方差及对应的行号,再调用函数获取字符串组成形式,输出行号对应的结果
  • 为减少计算量,last_row参数可以控制是计算所有分组形式,还是仅计算后x行分组形式。因为brr数组越后面元素分布越均匀,当需要计算方差的数组数值之间差异较小时,last_row较小则可以更快计算出结果;而如果数值差异较大的,可以适当增大last_row以便计算正确的结果;last_row等于0时,计算所有分组形式
Function 数组分组(ByVal data_arr, ByVal m&, Optional ByVal mode& = 1, Optional ByVal last_row& = 1)
    '对数组data_arr分为m组,结果返回二维数组(n行*m列),每列为和值/组成元素(数组从1开始计数)
    'data_arr元素数组;m需要分成几组;mode为1时返回和值,为2时返回字符串
    '为减少计算量,因为brr数组越后面元素分布越均匀,故last_row参数仅对brr数组的后last_row行进行分组
    Dim arr, brr, br, srr, sr, a, n&, i&, j&, x&, y&, r&, rr&, c&, t&, w&, res, trr, temp, s&
    ReDim arr(1 To 1000)
    If mode <> 1 And mode <> 2 Then Debug.Print "参数错误": Exit Function
    For Each a In data_arr  '多行多列的,按列从左往右读取,排除空值
        If Len(a) Then i = i + 1: arr(i) = a
    Next
    n = i: ReDim Preserve arr(1 To n): brr = 可分组数2(n, m, 2)
    If last_row > 0 And last_row < UBound(brr) Then  'last_row为2即仅计算brr数组后2行;为0则全部计算
        ReDim br(1 To last_row, 1 To 2)
        For i = 1 To last_row
            br(i, 1) = brr(i + UBound(brr) - last_row, 1): br(i, 2) = brr(i + UBound(brr) - last_row, 2)
        Next
        brr = br
    End If
    x = WorksheetFunction.Sum(Application.Index(brr, , 2))
    ReDim srr(1 To UBound(brr), 1 To m), sr(1 To UBound(brr), 1 To m)
    For i = 1 To UBound(brr)   'brr第1列转为数组
        temp = Split(brr(i, 1), "+"): t = brr(i, 2): s = n
        For j = 1 To m
            srr(i, j) = temp(j - 1)
        Next
        For j = 1 To m         '计算重复次数
            If srr(i, j) > 1 Then
                t = t \ Application.Combin(s, srr(i, j)): sr(i, j) = t: s = s - srr(i, j)
            Else
                sr(i, j) = 1
            End If
        Next
    Next
    i = 1: r = 0: c = 1: rr = 0: ReDim res(1 To x, 1 To m)
    Do
        Do While c = 1  '第1列赋值
            crr = combin_arr1(arr, srr(i, c)): t = sr(i, c)  '重复写入t次
            For Each a In crr
                For j = 1 To t
                    r = r + 1: res(r, c) = a
                Next
            Next
            If i < UBound(brr) Then i = i + 1 Else Exit Do
        Loop
        i = 1: r = 1: rr = 0: c = 2: ReDim temp(1 To n)  '除第1列的其他列,按列赋值
        Do
            ts = "": y = 0     'trr数组记录剩余元素,temp临时数组
            For j = 1 To c - 1
                ts = ts & "++" & Join(res(r, j), "++") & "++"
            Next
            For Each a In arr  '排除前一列已使用元素,且前后+号避免部分重复元素被找到
                aa = "+" & CStr(a) & "+"
                If InStr(ts, aa) = 0 Then
                    y = y + 1: temp(y) = a
                Else
                    ts = Replace(ts, aa, "", , 1)
                End If
            Next
            ReDim trr(1 To y)
            For j = 1 To y     'trr数组更新元素,且转换格式,否则导致求和错误
                trr(j) = CDbl(temp(j))
            Next
            If c <> m Then
                crr = combin_arr1(trr, srr(i, c)): w = 可分组数2(y, m - c + 1)
                If w = 1 Then  '只赋值第1个,避免c递增后出错
                    res(r, c) = crr(1): rr = rr + 1
                Else
                    t = sr(i, c): r = r - 1
                    For Each a In crr
                        For j = 1 To t
                            r = r + 1: res(r, c) = a: rr = rr + 1
                        Next
                    Next
                End If
            Else
                res(r, c) = trr: rr = rr + 1  '最后一列直接赋值,只有1组
            End If
            r = r + 1  '下一行
            If rr >= brr(i, 2) Then rr = 0: i = i + 1  'brr一行循环结束,进入下一轮
            If i > UBound(brr) Then i = 1: r = 1: c = c + 1
        Loop Until c > m
    Loop Until r = 1  '所有写入完成后,r=1
    If mode = 1 Then  '返回结果,求和模式
        For i = 1 To x
            For j = 1 To m
                res(i, j) = WorksheetFunction.Sum(res(i, j))
            Next
        Next
    Else              '字符串模式
        For i = 1 To x
            For j = 1 To m
                res(i, j) = Join(res(i, j), "+")
            Next
        Next
    End If
    数组分组 = res
End Function

举例

Sub 数组分组举例()
    tm = Timer
    arr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9): a = 数组分组(arr, 4, 1, 0)
    [a1].Resize(UBound(a), UBound(a, 2)) = a
    Debug.Print "累计用时" & Format(Timer - tm, "0.00")  '耗时
End Sub

mode参数为1,last_row参数为0,求和模式、输出所有分组形式(以下为部分截图)
在这里插入图片描述
mode参数为2,last_row参数为0,字符串模式、输出所有分组形式(以下为部分截图)
在这里插入图片描述

测试结果9个元素分成4组10个元素分成4组
总分组数1848088110
耗时秒数6.3426.57

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1545030.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【msyql】mysqldump: 未找到命令...

使用mysqldump备份数据库出现错误提示&#xff1a; mysqldump: 未找到命令... 执行的命令如下&#xff1a; mysqldump -uroot -proot --databases db_user > /home/backups/databackup.sql 解决方法 确认mysql是否安装 查看mysql版本 mysql --version 查找mysql安装路…

STM32学习笔记(6_5)- TIM定时器的输出捕获原理

无人问津也好&#xff0c;技不如人也罢&#xff0c;都应静下心来&#xff0c;去做该做的事。 最近在学STM32&#xff0c;所以也开贴记录一下主要内容&#xff0c;省的过目即忘。视频教程为江科大&#xff08;改名江协科技&#xff09;&#xff0c;网站jiangxiekeji.com 现在开…

在for循环加判断条件当条件都满足时,同时显现的解决方法

一、代码示例 function fu(s) {str ;ste ;console.log(s);let Things s;for (let i 0; i < Things.length; i) {if (Things[i].pid kk) {console.log(Things[i].pid);ste <div class"commodity_nei"><div class"zxc_pic"><div cl…

【Java初阶(五)】类和对象

❣博主主页: 33的博客❣ ▶文章专栏分类: Java从入门到精通◀ &#x1f69a;我的代码仓库: 33的代码仓库&#x1f69a; 目录 1. 前言2.面向对象的认识3.类的认识4. 类的实例化4.1什么是实例化4.2类和对象的说明 5.this引用6.对象初始化6.1 构造方法 7.static关键字8.代码块8.1 …

6.使用个人用户登录域控的成员服务器,如何防止个人用户账号的用户策略生效?

&#xff08;1&#xff09;需求&#xff1a; &#xff08;2&#xff09;实战配置步骤 第一步:创建新的策略-并编辑策略 第二步&#xff1a;将策略应用到服务器处在OU 第三步&#xff1a;测试 &#xff08;1&#xff09;需求&#xff1a; 比如域控&#xff0c;或者加入域的…

CUDA从入门到放弃(六):CUDA内存结构(Memory Hierarchy)

CUDA从入门到放弃&#xff08;六&#xff09;&#xff1a;CUDA内存结构&#xff08;Memory Hierarchy&#xff09; CUDA线程在执行过程中可以从多个内存空间访问数据。每个线程都有私有的局部内存。每个线程块具有共享内存&#xff0c;该内存对所有线程块内的线程可见&#xf…

磁盘文件系统实际操练,解释到bit

author: hjjdebug date: 2024年 03月 25日 星期一 17:50:02 CST description: 磁盘文件系统实际操练,解释到bit文章目录 0. 为什么需要磁盘文件系统.1. 磁盘文件系统的任务是什么?2. 空白磁盘是什么? 空白磁盘数据长什么样?3. 格式化磁盘都干了什么? 格式化后的磁盘长什么…

YoloV8改进策略:Neck改进|ECA-Net:用于深度卷积神经网络的高效通道注意力|多种改进方法|附结构图

摘要 本文使用ECA-Net注意力机制加入到YoloV8中。我尝试了多种改进方法&#xff0c;并附上改进结果&#xff0c;方便大家了解改进后的效果&#xff0c;为论文改进提供思路。 论文&#xff1a;《ECA-Net&#xff1a;用于深度卷积神经网络的高效通道注意力》 arxiv.org/pdf/19…

“一根盲杖,扫清前进道路”视障人士关爱行动中

近期&#xff0c;红枫林义警服务发展中心联合暨南街道社工站&#xff0c;面向暨南街道辖区内的视障人群&#xff0c;开展了一系列服务&#xff0c;送去了我们的关爱。 首先&#xff0c;我们成功为视障人群链接到了价值1万的爱心物资&#xff0c;捐赠仪式即为本次我们关爱行动的…

计算机组成原理 中断原理实验

一、实验目的 &#xff08;1&#xff09;从硬件&#xff0c;软件结合的角度&#xff0c;模拟单级中断和中断返回的过程 &#xff08;2&#xff09;通过简单的中断系统&#xff0c;掌握中断控制器、中断向量、中断屏蔽等概念 &#xff08;3&#xff09;了解微程序控制器与中断…

批量文本管理:一键合并与智能分隔,让文档处理更高效!

在信息爆炸的时代&#xff0c;我们每天都面临着海量的文本信息&#xff0c;从工作文件到个人笔记&#xff0c;从学术论文到社交媒体帖子&#xff0c;管理这些文本内容成为一项巨大的挑战。如何高效地合并、整理这些散乱的文本&#xff0c;使其有序且易于检索&#xff1f;今天&a…

后端常问面经之Java集合

HashMap底层原理 HashMap的数据结构&#xff1a; 底层使用hash表数据结构&#xff0c;即数组和链表或红黑树 当我们往HashMap中put元素时&#xff0c;利用key的hashCode重新hash计算出当前对象的元素在数组中的下标 存储时&#xff0c;如果出现hash值相同的key&#xff0c;此…

⨯ EPERM: operation not permitted, link ...

新增区块链相关包后&#xff0c;项目在部署的时候报错&#xff0c;报错内容如下&#xff1a; 报错信息&#xff1a; ⨯ EPERM: operation not permitted, link /Users/XXX/.cache/act/be662ca67b3f7553/hostexecutor/node_modules/bigint-buffer/build/node_gyp_bins/python…

【数据结构刷题专题】—— 二叉树

二叉树 二叉树刷题框架 二叉树的定义&#xff1a; struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(NULL), right(NULL); };1 二叉树的遍历方式 【1】前序遍历 class Solution { public:void traversal(TreeNode* node, vector&…

「Nginx」Nginx配置详解

「Nginx」Nginx配置详解 参考文章1、正向代理和方向代理2、指定域名允许跨域 参考文章 1、Nginx反向代理 2、nginx配置详解 3、Nginx服务器之负载均衡策略&#xff08;6种&#xff09; 1、正向代理和方向代理 2、指定域名允许跨域 map $http_origin $allow_cors {default 1;…

4D 毫米波雷达前景

目录 传统雷达检测流程 行业首先 存在问题 解决方案 雷达数据集 1&#xff09;3D检测 2&#xff09; 场景估计 4D毫米波雷达的未来发展趋势 4D毫米波雷达是指一种高级的雷达系统&#xff0c;它能够提供三维空间信息&#xff08;即长度、宽度、高度&#xff09;和第四维…

数据清洗(一)Excel

一、引言 线上出现问题之后的数据清洗是少不了的&#xff0c;有的可以直接通过接口或者mq补偿&#xff0c;有的写sql更新db就可以&#xff0c;但是在匹配关系比较复杂的时候就需要建立临时表做关联匹配&#xff0c;数据量不大可以直接用excel进行匹配。 二、Excel清洗数据 作者…

如何在VS Code上搭建 C/C++开发环境

顾得泉&#xff1a;个人主页 个人专栏&#xff1a;《Linux操作系统》 《C从入门到精通》 《LeedCode刷题》 键盘敲烂&#xff0c;年薪百万&#xff01; 一、什么是VScode VScode&#xff08;Visual Studio Code&#xff09;是一款由微软开发的免费开源的轻量级代码编辑器。它…

【Android】美团组件化路由框架WMRouter源码解析

前言 Android无论App开发还是SDK开发&#xff0c;都绕不开组件化&#xff0c;组件化要解决的最大的问题就是组件之间的通信&#xff0c;即路由框架。国内使用最多的两个路由框架一个是阿里的ARouter&#xff0c;另一个是美团的WMRouter。这两个路由框架功能都很强大&#xff0…

JavaScript 中内存泄漏的几种情况(非常详细)

文章目录 一、是什么二、垃圾回收机制标记清除引用计数小结 三、常见内存泄露情况参考文献 一、是什么 内存泄漏&#xff08;Memory leak&#xff09;是在计算机科学中&#xff0c;由于疏忽或错误造成程序未能释放已经不再使用的内存 并非指内存在物理上的消失&#xff0c;而…