在Excel催化剂的自定义函数中,有规划求解的函数,用于在一些凑数的场景,某财务工作网友向我提出的需求,例如用于凑发票额使用。

一般开发票的场景是多次采购合在一起开具,即多个订单产生后开,同时发票一般有限额不是想开多少就开多少,而且发票的张数每月都是有限的,也不是随便可以一个零头开一张发票。

对这些凑数的场景,有个算法叫背包算法,是规范求解方面的,当然笔者也没有深入研究过,只是在我师傅的帮助下,找到了Google有一个开源库专门干这些事,性能也是棒棒的,甩开原生Excel的规范求解几个月球距离。

因为这个Google库比较大,而且好像是C++内核的,有区分32位和64位,所以最终没有直接放到ExcelDna项目中,而是采用WebService的方式来部署这个功能,放到服务器上,避开32位、64位问题,同时也不必让客户端发布文件时携带那么大的类库。

关于WebService的问题,可自行百度学习,现只是给出此类库和用这个类库实现了凑数的场景。

 
Google.OrTools类库

WebSevice源码如下:

    public List<object> GetGroupIdsByKnapsacks(long[] values, long[] capacities,int scaleNum)
{
KnapsackSolver solver = new KnapsackSolver(
KnapsackSolver.KNAPSACK_DYNAMIC_PROGRAMMING_SOLVER, "test"); long[,] weights = new long[1, values.Length]; for (int i = 0; i < values.Length; i++)
{
weights[0, i] = values[i];
} Dictionary<int, string> dicResult = new Dictionary<int, string>();
int iLoop = 0;
long computedProfit;
do
{
long capacity = capacities[iLoop];
solver.Init(values, weights, new long[] { capacity }); computedProfit = solver.Solve();
//因为有0值的存在,所有一定会有解,只是目标值为0
if (computedProfit == 0)
{
break;
}
for (int i = 0; i < values.Length; i++)
{
if (solver.BestSolutionContains(i))
{
if (!dicResult.ContainsKey(i))
{ dicResult.Add(i, $"{(iLoop + 1).ToString("00")}_{capacity*1.0/Math.Pow(10,scaleNum)}_{capacity * 1.0 / Math.Pow(10, scaleNum) - computedProfit * 1.0 / Math.Pow(10, scaleNum)}");//存入序号和组大小、组差异等信息
values[i] = 0;
weights[0, i] = 0;
} }
} //Console.WriteLine(computedProfit); iLoop++;
} while (iLoop < capacities.Length); List<object> groupIds = new List<object>(); for (int i = 0; i < values.Length; i++)
{
if (dicResult.ContainsKey(i))
{
groupIds.Add(dicResult[i]);
}
else
{
groupIds.Add(null);
}
}
return groupIds;
}

在ExcelDna上再进行封装

 [ExcelFunction(Category = "规划求解类", Description = "分组凑数,从源数据列中,抽取出指定的项目组合,使其求和数最大限度接近分组的大小。Excel催化剂出品,必属精品!")]
public static object CouShuWithGroupFromOrTools(
[ExcelArgument(Description = "需要分组的原始数据单元格区域,精度为最多4位小数点,多于4位将截断")] object[] srcRange,
[ExcelArgument(Description = "限定组的上限的单元格区域,可选多个单元格代表分多个组,组的大小可不相同,尽量较难组合的放最上面优先对其组合")] object[] groupeRange
) { int scaleNum = GetScaleNum(srcRange); KnapsacksService.KnapsacksServiceSoapClient client = new KnapsacksService.KnapsacksServiceSoapClient(); KnapsacksService.ArrayOfLong values = new KnapsacksService.ArrayOfLong(); values.AddRange(srcRange.Select(s => Convert.ToDouble(s)).Select(t => Convert.ToInt64(t * Math.Pow(10, scaleNum)))); KnapsacksService.ArrayOfLong capacities = new KnapsacksService.ArrayOfLong();
capacities.AddRange(groupeRange.Where(s => s != ExcelEmpty.Value).Select(t => Convert.ToDouble(t)).Select(r => Convert.ToInt64(r * Math.Pow(10, scaleNum)))); KnapsacksService.ArrayOfAnyType results = client.GetGroupIdsByKnapsacks(values, capacities,scaleNum); return Common.ReturnDataArray(results.Select(s => s).ToArray(), "L"); }

结语

此篇介绍的Google.OrTools类库,远不止用于一个简单的凑数功能,若对其他功能有兴趣,可自行去查阅文档学习,此处仅仅作引路,这些著名的类库,通常来说,帮助文档、示例代码都是十分详细的。

再一次见证了VSTO借助外部的轮子力量,给Excel提供了源源不断地能力,让Excel用户在Excel环境可以完成许多不可思议的事情。

同样此篇也开拓了思路,一些复杂的功能需求,不止是借助外部WebAPI的接口调用,甚至自己也可以封装一些API接口供自己调用,在服务器上开发功能,不必考虑客户端的复杂环境,更为稳定地进行开发,而不必考虑兼容性等问题。

技术交流QQ群

QQ群名:Excel催化剂开源讨论群, QQ群号:788145319

 
Excel催化剂开源讨论群二维码

关于Excel催化剂

Excel催化剂先是一微信公众号的名称,后来顺其名称,正式推出了Excel插件,插件将持续性地更新,更新的周期视本人的时间而定争取一周能够上线一个大功能模块。Excel催化剂插件承诺个人用户永久性免费使用!

Excel催化剂插件使用最新的布署技术,实现一次安装,日后所有更新自动更新完成,无需重复关注更新动态,手动下载安装包重新安装,只需一次安装即可随时保持最新版本!

Excel催化剂插件下载链接:https://pan.baidu.com/s/1Iz2_NZJ8v7C9eqhNjdnP3Q

 
联系作者
 
公众号

取名催化剂,因Excel本身的强大,并非所有人能够立马享受到,大部分人还是在被Excel软件所虐的阶段,就是头脑里很清晰想达到的效果,而且高手们也已经实现出来,就是自己怎么弄都弄不出来,或者更糟的是还不知道Excel能够做什么而停留在不断地重复、机械、手工地在做着数据,耗费着无数的青春年华岁月。所以催生了是否可以作为一种媒介,让广大的Excel用户们可以瞬间点燃Excel的爆点,无需苦苦地挣扎地没日没夜的技巧学习、高级复杂函数的烧脑,最终走向了从入门到放弃的道路。

最后Excel功能强大,其实还需树立一个观点,不是所有事情都要交给Excel去完成,也不是所有事情Excel都是十分胜任的,外面的世界仍然是一个广阔的世界,Excel只是其中一枚耀眼的明星,还有其他更多同样精彩强大的技术、工具等。*Excel催化剂也将借力这些其他技术,让Excel能够发挥更强大的爆发!

关于Excel催化剂作者

姓名:李伟坚,从事数据分析工作多年(BI方向),一名同样在路上的学习者。
服务过行业:零售特别是鞋服类的零售行业,电商(淘宝、天猫、京东、唯品会)

技术路线从一名普通用户,通过Excel软件的学习,从此走向数据世界,非科班IT专业人士。
历经重重难关,终于在数据的道路上达到技术平原期,学习众多的知识不再太吃力,同时也形成了自己的一套数据解决方案(数据采集、数据加工清洗、数据多维建模、数据报表展示等)。

擅长技术领域:Excel等Office家族软件、VBA&VSTO的二次开发、Sqlserver数据库技术、Sqlserver的商业智能BI技术、Powerbi技术、云服务器布署技术等等。

2018年开始职业生涯作了重大调整,从原来的正职工作,转为自由职业者,暂无固定收入,暂对前面道路不太明朗,苦重新回到正职工作,对Excel催化剂的运营和开发必定受到很大的影响(正职工作时间内不可能维护也不可能随便把工作时间内的成果公布于外,工作外的时间也十分有限,因已而立之年,家庭责任重大)。

和广大拥护者一同期盼:Excel催化剂一直能运行下去,我所惠及的群体们能够给予支持(多留言鼓励下、转发下朋友圈推荐、小额打赏下和最重点的可以和所在公司及同行推荐推荐,让我的技术可以在贵司发挥价值,实现双赢(初步设想可以数据顾问的方式或一些小型项目开发的方式合作)。

Excel催化剂开源第28波-调用Google规划求解库的更多相关文章

  1. Excel催化剂开源第22波-VSTO的帮助文档在哪里?

    Excel催化剂开源第22波-VSTO的帮助文档在哪里? Excel催化剂   2019.01.12 14:10 字数 2930 阅读 55评论 0喜欢 0 编辑文章 对于专业程序猿来说,查找文档不是 ...

  2. Excel催化剂开源第23波-VSTO开发辅助录入功能关键技术

    Excel催化剂开源第23波-VSTO开发辅助录入功能关键技术 Excel催化剂   2019.01.12 14:10* 字数 2948 阅读 41评论 0喜欢 0 编辑文章 在Excel催化剂的几大 ...

  3. Excel催化剂开源第34波-SM.MS图床API调用(用POST上传multipart/form-data内容)

    日常做网抓数据,都是以GET请求为主,偶尔遇到需要POST请求的,一般POST的参数只是一串字符串就可以了,通过构造字符串也很容易完成,但此次SM.MS的API接口要求是Content-Type: m ...

  4. Excel催化剂开源第32波-VSTO开发的插件让WPS顺利调用的方法-注册表增加注册信息

    VSTO插件开发完成后,鉴于现在WPS用户也不少,很多时候用户没办法用OFFICE软件,只能在WPS环境下办公,VSTO开发的插件,只需增加一句注册表信息,即可让WPS识别到并调用VSTO开发的功能, ...

  5. Excel催化剂开源第25波-Excel调用百度AI,返回AI结果

    现成的这些轮子,无需调用网页,直接本地离线即可生成). 当然在AI时代,少不了各种AI接口的使用场景,普通开发者只需聚焦在自己的业务场景上,这些AI底层技术,只需类似水煤电一般去BAT这些大厂那里去消 ...

  6. Excel催化剂开源第8波-VSTO开发之异步调用方法

    在VSTO开发过程中,因其和普通的Winform开发有点差别,具体细节笔者也说不清楚,大概是VSTO的插件是寄生在Excel中,不属于独立的进程之类的,其异步方法调用时,未能如Winform那样直接用 ...

  7. Excel催化剂开源第3波-修复ExcelCom加载项失效问题及WPS可调用Com加载项的方法

    为了还原一个干净无侵扰的网络世界,本文将不进行大规模地分发,若您觉得此文有用,不妨小范围地分享到真正有需要的人手中 功能概述 修复ExcelCom加载项常见问题,如每次需重新勾选COM加载项或COM加 ...

  8. Excel催化剂开源第36波-图片Exif信息提取,速度超快,信息超全

    Excel催化剂在文件处理方面,功能做到极致,但其实很大功劳都是引用一些开源社区的轮子库,不敢独占好处,此篇给大家分享下抓取图片的Exif信息的好用的轮子. 此篇对应的Excel催化剂功能实现:第83 ...

  9. Excel催化剂开源第51波-Excel催化剂遍历单元格操作性能保障

    在Excel催化剂推出的这一年多时间里,经常性听到一种声音,大概意思是真正会写代码的人,都不会看上Excel催化剂写出来的功能,自己造一个更舒服贴心,仿佛会一点VBA就可以天下无敌一般,也好像Exce ...

随机推荐

  1. MySQL操作详解

    创建并使用数据库 查看服务器上的数据库:SHOW DATABASES; 创建数据库:CREATE DATABASE <数据库名>; 指明使用何数据库:USE <数据库名> 创建 ...

  2. XPath概述

    1.  XPath 具体示例可参考网址: http://www.zvon.org/xxl/XPathTutorial/General/examples.html 1.1 概述 * 现节点下所有元素 * ...

  3. kafka笔记4(2)

    提交和偏移量 每次调用poll 方法,总是返回生产者写入Kafka但还没有被消费者读取过的记录我们因此可以追踪到哪些记录时被群组里的哪个消费者读取过的. 我们把更新分区当前位置的操作叫做提交. 那么消 ...

  4. Django之分页器组件

    class Pagination(object): def __init__(self,current_page,all_count,per_page_num=2,pager_count=11): & ...

  5. 跟我学SpringCloud | 第二篇:注册中心Eureka

    Eureka是Netflix开源的一款提供服务注册和发现的产品,它提供了完整的Service Registry和Service Discovery实现.也是springcloud体系中最重要最核心的组 ...

  6. Scala 学习之路(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

  7. C语言版数据结构笔记

    现在把以前学的数据结构知识再理一遍,上机测试.首先最重要的是链表.在我看来,链表其实就是由一个个结构体连接而成的,创建一个链表有多种方式,头插法,尾插法等,这里采用的是尾插法.表述有不对的地方,欢迎更 ...

  8. 1.谈谈对Java平台的理解

    1.谈谈你对Java平台的理解 Java 本身是一种面向对象的语言,最显著的特性有两个方面,一是所谓的“一次编译,到处运行”(Compile once,run anywhere),能够非常容易地获取跨 ...

  9. CSS3背景与渐变

    一.CSS3 背景图像区域 background-clip(指定背景绘制区域) ackground-clip: border-box / padding-box / content-box; /*没有 ...

  10. spring源码深度解析— IOC 之 属性填充

    doCreateBean() 主要用于完成 bean 的创建和初始化工作,我们可以将其分为四个过程: createBeanInstance() 实例化 bean populateBean() 属性填充 ...