Excel·VBA数组分组问题

看到一个帖子《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, resReDim arr(1 To n - m + 1), brr(1 To n - m + 1)  '组合法计算组数,最大值为n - m + 1x = n - m + 1: arr(1) = 1: brr(1) = m - 1  'arr元素个数,brr重复次数If m = 1 ThenIf mode = 1 Then可分组数 = 1: Exit FunctionElseIf mode = 2 ThenReDim res(1 To 1, 1 To 1): res(1, 1) = n: 可分组数 = res: Exit FunctionEnd IfEnd IfFor i = 2 To x  '每个数字各最多需要的数量arr(i) = i: t = n \ i: tt = n / i  '整除、除,判断是否相等If t = tt And t = m Then  '整除,且正好分配为m组brr(i) = tElseFor j = t To 1 Step -1a = i * j + (m - j)  '数字i有j个,其余为1,判断和是否<=nIf a <= n Then brr(i) = j: Exit ForNextEnd IfNexts = 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)  '所有数字按个数写入一个数组NextNext'对数组crr选m个进行组合,获取和值为n,且组合形式唯一的所有组合Dim dict As Object: Set dict = CreateObject("scripting.dictionary"): x = 0drr = 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.keyskrr = Split(k, "+"): s = n: y = 1For i = 0 To m - 1   '分组中只有1个元素的无所谓顺序,排除If krr(i) > 1 Then y = y * Application.Combin(s, krr(i)): s = s - krr(i)Nextdict(k) = y: x = x + y    'y每种组合形式的组数,x总组数NextIf mode = 1 Then    '输出结果可分组数 = xElseIf mode = 2 ThenReDim res(1 To x, 1 To m): i = 0For Each k In dict.keyskrr = Split(k, "+")For y = 1 To dict(k)  '重复写入dict(k)行krr数组i = i + 1For j = 0 To m - 1res(i, j + 1) = krr(j)NextNextNext可分组数 = resEnd 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, resReDim arr(1 To n - m + 1), brr(1 To n - m + 1)  '组合法计算组数,最大值为n - m + 1x = n - m + 1: arr(1) = 1: brr(1) = m - 1  'arr元素个数,brr重复次数If m = 1 Or n = m ThenIf mode = 1 Then可分组数2 = 1ElseIf mode = 2 ThenReDim res(1 To 1, 1 To 2): res(1, 2) = 1res(1, 1) = WorksheetFunction.Rept("1", m): 可分组数2 = resEnd IfExit FunctionEnd IfFor i = 2 To x  '每个数字各最多需要的数量arr(i) = i: t = n \ i: tt = n / i  '整除、除,判断是否相等If t = tt And t = m Then  '整除,且正好分配为m组brr(i) = tElseFor j = t To 1 Step -1a = i * j + (m - j)  '数字i有j个,其余为1,判断和是否<=nIf a <= n Then brr(i) = j: Exit ForNextEnd IfNexts = 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)  '所有数字按个数写入一个数组NextNext'对数组crr选m个进行组合,获取和值为n,且组合形式唯一的所有组合Dim dict As Object: Set dict = CreateObject("scripting.dictionary"): x = 0drr = 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.keyskrr = Split(k, "+"): s = n: y = 1For i = 0 To m - 1   '分组中只有1个元素的无所谓顺序,排除If krr(i) > 1 Then y = y * Application.Combin(s, krr(i)): s = s - krr(i)Nextdict(k) = y: x = x + y    'y每种组合形式的组数,x总组数NextIf mode = 1 Then    '输出结果可分组数2 = xElseIf mode = 2 ThenReDim res(1 To dict.Count, 1 To 2): i = 0For Each k In dict.keysi = i + 1: res(i, 1) = k: res(i, 2) = dict(k)Next可分组数2 = resEnd If
End Function

代码2举例

Sub 可分组数2举例()arr = 可分组数2(9, 4, 2)If IsArray(arr) Then[a1].Resize(UBound(arr), UBound(arr, 2)) = arrElseDebug.Print arrEnd 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 FunctionFor Each a In data_arr  '多行多列的,按列从左往右读取,排除空值If Len(a) Then i = i + 1: arr(i) = aNextn = 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_rowbr(i, 1) = brr(i + UBound(brr) - last_row, 1): br(i, 2) = brr(i + UBound(brr) - last_row, 2)Nextbrr = brEnd Ifx = 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 = nFor j = 1 To msrr(i, j) = temp(j - 1)NextFor j = 1 To m         '计算重复次数If srr(i, j) > 1 Thent = t \ Application.Combin(s, srr(i, j)): sr(i, j) = t: s = s - srr(i, j)Elsesr(i, j) = 1End IfNextNexti = 1: r = 0: c = 1: rr = 0: ReDim res(1 To x, 1 To m)DoDo While c = 1  '第1列赋值crr = combin_arr1(arr, srr(i, c)): t = sr(i, c)  '重复写入t次For Each a In crrFor j = 1 To tr = r + 1: res(r, c) = aNextNextIf i < UBound(brr) Then i = i + 1 Else Exit DoLoopi = 1: r = 1: rr = 0: c = 2: ReDim temp(1 To n)  '除第1列的其他列,按列赋值Dots = "": y = 0     'trr数组记录剩余元素,temp临时数组For j = 1 To c - 1ts = ts & "++" & Join(res(r, j), "++") & "++"NextFor Each a In arr  '排除前一列已使用元素,且前后+号避免部分重复元素被找到aa = "+" & CStr(a) & "+"If InStr(ts, aa) = 0 Theny = y + 1: temp(y) = aElsets = Replace(ts, aa, "", , 1)End IfNextReDim trr(1 To y)For j = 1 To y     'trr数组更新元素,且转换格式,否则导致求和错误trr(j) = CDbl(temp(j))NextIf c <> m Thencrr = 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 + 1Elset = sr(i, c): r = r - 1For Each a In crrFor j = 1 To tr = r + 1: res(r, c) = a: rr = rr + 1NextNextEnd IfElseres(r, c) = trr: rr = rr + 1  '最后一列直接赋值,只有1组End Ifr = 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 + 1Loop Until c > mLoop Until r = 1  '所有写入完成后,r=1If mode = 1 Then  '返回结果,求和模式For i = 1 To xFor j = 1 To mres(i, j) = WorksheetFunction.Sum(res(i, j))NextNextElse              '字符串模式For i = 1 To xFor j = 1 To mres(i, j) = Join(res(i, j), "+")NextNextEnd If数组分组 = res
End Function

举例

Sub 数组分组举例()tm = Timerarr = Array(1, 2, 3, 4, 5, 6, 7, 8, 9): a = 数组分组(arr, 4, 1, 0)[a1].Resize(UBound(a), UBound(a, 2)) = aDebug.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://xiahunao.cn/news/2905484.html

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

相关文章

线程的状态:操作系统层面和JVM层面

在操作系统层面&#xff0c;线程有五种状态 初始状态&#xff1a;线程被创建&#xff0c;操作系统为其分配资源。 可运行状态(就绪状态)&#xff1a;线程被创建完成&#xff0c;进入就绪队列&#xff0c;参与CPU执行权的争夺。或因为一些原因&#xff0c;从阻塞状态唤醒的线程…

车道线检测项目 | 基于lanenet实现的实时车道线检测

项目应用场景 面向自动驾驶场景的车道线检测场景&#xff0c;项目的特点是能够达到实时的车道线检测 项目效果&#xff1a; 项目细节 > 具体参见项目 README.md (1) 安装依赖 pip3 install -r requirements.txt (2) 测试图片 python tools/test_lanenet.py --weights_pat…

QT QInputDialog弹出消息框用法

使用QInputDialog类的静态方法来弹出对话框获取用户输入&#xff0c;缺点是不能自定义按钮的文字&#xff0c;默认为OK和Cancel&#xff1a; int main(int argc, char *argv[]) {QApplication a(argc, argv);bool isOK;QString text QInputDialog::getText(NULL, "Input …

酷开科技通过酷开系统,将场景精细划分

OTT行业发展至今&#xff0c;伴随着消费者内容消费习惯的不断演进以及商业营销基建的持续完善&#xff0c;整个OTT大屏营销环境也发生了新的变化。作为家庭场景中的第一媒介的OTT大屏&#xff0c;成为了当下红利效应的营销阵地&#xff0c;正开启新一轮的价值释放。 在目前阶段…

excel 提取数字字符混合文本中的数字(快捷键ctrl+e)

首先&#xff0c;已知A列数据&#xff0c;在B1单元格输入A列中的数据&#xff0c;如3*4*6 第二部&#xff1a;全选对应的B列&#xff0c;然后&#xff1a; ctrld 批量复制 CTRLE 智能复制 由此可见&#xff0c;智能提取汉字与数字混合中的数字方法 。若想分别提取3个数字&am…

独立游戏《星尘异变》UE5 C++程序开发日志3——UEC++特供的数据类型

本篇日志将介绍FString&#xff0c;FText、FName的用法和相互转换&#xff0c;以及容器TMap&#xff0c;TArray的增删查改 一、字符串相关数据类型&#xff1a;FString、FText、FName FString是最接近std::string的类型&#xff0c;字符串本身可以看做一个存储char型的动态数…

计算机网络-RIP动态路由协议简介

一、概述 前面我们学习了动态路由协议按照工作机制及算法划分可以分为&#xff1a;距离矢量路由协议DV型和链路状态路由协议LS型。RIP就是典型的距离矢量路由协议&#xff0c;但是实际工作中用得已经比较少了。 距离矢量路由协议DV: RIP 链路状态路由协议LS: OSPF IS-IS 二、RI…

四数之和【双指针】

三数之和 举一反三 class Solution { public:vector<vector<int>> fourSum(vector<int> &nums, int target){sort(nums.begin(), nums.end());vector<vector<int>> ret;int first 0, n nums.size();while (first < n){int second firs…

康耐视visionpro-CogBlobTool工具详细说明

CogBlobTool功能说明: 通过设置灰度值提取感兴趣区域,并分析所提取区域的面积、长宽等参数。 CogBlobTool操作说明: ①.打开工具栏,双击或点击鼠标拖拽添加CogBlobTool工具 ②.添加输入图像:单击鼠标右键“链接到”或以连线拖拽的方式选择相应输入源 ③.极性:“白底黑点…

宝宝洗澡时间:精心呵护,打造温馨时刻

引言&#xff1a; 给新生儿洗澡是每位父母的日常之一&#xff0c;而选择适当的洗澡时间对宝宝的健康和舒适度至关重要。在本文中&#xff0c;我们将探讨宝宝洗澡时间的注意事项&#xff0c;为您提供正确的洗澡时间和洗澡技巧&#xff0c;让宝宝在洗澡过程中感受到温暖舒适的体验…

Python基本运算

1.逻辑运算符 第四行会有黄色的下划线是因为这个不是系统推荐的写法&#xff0c;系统推荐的是第五行的链式比较&#xff1b; 2.短路求值 对于and而言&#xff0c;左边的语句是false&#xff0c;那么整体一定是false,右边的表达式就不会进行计算&#xff1b; 对于or而言&…

STM32 PWM通过RC低通滤波转双极性SPWM测试

STM32 PWM通过RC低通滤波转双极性SPWM测试 &#x1f4cd;参考内容《利用是stm32cubemx实现双极性spwm调制 基于stm32f407vet6》&#x1f4fa;相关视频链接&#xff1a;https://www.bilibili.com/video/BV16S4y147hB/?spm_id_from333.788 双极性SPWM调制讲解以及基于stm32的代码…

基于TensorFlow的花卉识别(算能杯)%%%

Anaconda Prompt 激活 TensorFlow CPU版本 conda activate tensorflow_cpu //配合PyCharm环境 直接使用TensorFlow1.数据分析 此次设计的主题为花卉识别&#xff0c;数据为TensorFlow的官方数据集flower_photos&#xff0c;包括5种花卉&#xff08;雏菊、蒲公英、玫瑰、向日葵…

基于Echarts的超市销售可视化分析系统(数据+程序+论文)

本论文旨在研究Python技术和ECharts可视化技术在超市销售数据分析系统中的应用。本系统通过对超市销售数据进行分析和可视化展示&#xff0c;帮助决策层更好地了解销售情况和趋势&#xff0c;进而做出更有针对性的决策。本系统主要包括数据处理、数据可视化和系统测试三个模块。…

进阶了解C++(6)——二叉树OJ题

Leetcode.606.根据二叉树创建字符串&#xff1a; 606. 根据二叉树创建字符串 - 力扣&#xff08;LeetCode&#xff09; 难度不大&#xff0c;根据题目的描述&#xff0c;首先对二叉树进行一次前序遍历&#xff0c;即&#xff1a; class Solution { public:string tree2str(Tr…

php 快速入门(七)

一、操作数据库 1.1 操作MySQL的步骤 第一步&#xff1a;登录MySQL服务器 第二步&#xff1a;选择当前数据库 第三步&#xff1a;设置请求数据的字符集 第四步&#xff1a;执行SQL语句 1.2 连接MySQL 函数1&#xff1a;mysql_connect() 功能&#xff1a;连接&#xff08;登录…

Linux根据时间删除文件或目录

《liunx根据时间删除文件》和 《Linux 根据时间删除文件或者目录》已经讲述了根据时间删除文件或目录的方法。 下面我做一些补充&#xff0c;讲述一个具体例子。以删除/home目录下的文件为例。 首先通过命令&#xff1a; ls -l --time-style"%Y-%m-%d %H:%M:%S"…

详解:JS的四种异步解决方案之分布/订阅,及其利弊。

上期讲了详解&#xff1a;JS异步解决方案之分布/订阅&#xff0c;及其弊端&#xff0c;原文链接在文章后面&#xff0c;分布/订阅是异步的一种方式而已&#xff0c;本期讲解第六个方案。 一、什么是分布/订阅 分布/订阅&#xff08;Publish/Subscribe&#xff09;是一种软件架…

YOLOv9改进策略:注意力机制 | FocalNet焦点调制注意力取代自注意力

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文改进内容&#xff1a;由于自注意力二次的计算复杂度效率较低&#xff0c;尤其是对于高分辨率输入。因此&#xff0c;作者提出了focal modulation network&#xff08;FocalNet&#xff09;使用焦点调制模块来取代自注意力。 改进结…

黑群晖基于docker配置frp内网穿透

前言 我的黑群晖需要设置一下内网穿透来外地访问&#xff0c;虽然zerotier的p2p组网已经很不错了&#xff0c;但是这个毕竟有一定的局限性&#xff0c;比如我是ios的国区id就下载不了zerotier的app&#xff0c;组网不了 1.下载镜像 选择第一个镜像 2.映射文件 配置frpc.ini&a…