程晓华
2025-6-21
我没事的时候喜欢编个小程序啥的,一是可以解决一些Excel等无法解决的实际业务问题,二是用于大脑放松,就当是打游戏玩了。
尽管我编程的水平很一般,但我个人认为,练习计算机编程最大的好处是能够训练自己的大脑,使你分析问题、解决问题能够更加条理化、逻辑化。
稍微复杂点的计算机程序基本上都绕不开一个算法问题。
一提到算法(Algorithm),大多数像我们这些非计算机专业的人肯定就会发懵,大家总认为算法都是些高大上的东西。
其实不然。
通俗地理解,算法就是做事情的方法,类似炒菜的菜谱,先干什么,后干什么,只要把菜炒出来即可。只是,计算机的算法要遵循一定的语法规则,而这个语法规则,类似汉语、英语、法语等,每个语种略有不同。譬如说,R的语法跟Python的就不完全一样。
我们下面通过一个简单的例子用R语言来说明一下这个算法问题。
我们先用R的正态分布随机函数产生一个n=50的数组R,其均值mean=100,标准差sd=30:
[1] 67 105 98 54 83 61 37 64 148 81 121 94 120 83 138 84 109 158 66
[20] 25 80 106 94 93 93 95 130 109 66 119 90 174 130 116 132 127 86 83
[39] 116 90 51 121 84 88 23 133 116 123 97 106
我们现在要解决的问题是:
把这个数组R中的50个数字从第一个数字开始,按顺序分成10组,每组5个数,每组数都不能重复。
在这里,我首先想到的是用for() 循环 + R[i] 解决这个问题,其中的R[i] 就是提取R这个数组中的第i个元素,同时利用i参与for循环,而i可以定义为1:(50/5)=1:10。
按照这个定义,取第1组数的解决方案是:
R[1:5]
取第2组数的解决方案是:
R[6:10]
取第3组数的解决方案是:
R[11:15]
……
但问题是,我们要取10组的数,这种一一取数的做法肯定是不行的,说白了,这种算法也太累人了。
那如何把R[1:5]、R[6:10]、R[11:15] 等做一般化处理,即把R[?:?]替换成换i的函数,从而让for循环自动完成这个任务呢?
这问题就是一个从特殊到一般的问题,也是我们要寻找的的算法要去解决的问题。
仔细观察R[1:5],当i=1的时候,我们可以将其替换为:R[i: (i+4)]。
但当i=2的时候,R[i:(i+4)]= R[2:6],这个结果显然不是我们想要的,因为我们希望此时的结果是R[6:10]。
那么,我们来继续观察研究这个R[6:10]的一般化问题,其实质就是如何把6、10两个数字用i、4的函数来代替的问题。
很明显:
6=2+4=i+4
10=2+4*2=i+4i
即:R[6:10]可以用 R[(i+4):(i+4i)]来一般化。
当i=3的时候,上边的这个一般化公式是否能够得到我们想要的结果呢?
我们试试:
R[(i+4):(i+4i)]= R[(3+4):(3+4*3)]= R[7:15]
而按照要求,我们想要的第3组结果应该是R[11:15]。
还差那么一点点!我们还需要对i+4进行适当的改造,譬如说在4前面增加个系数:(i-1)。
我们来试试 i+(i-1)*4:
当i=3的时候,i+(i-1)*4= 3+(3-1)*4=11,与我们的要求完全一致!
而:
当i=1的时候,i+(i-1)*4= 1+(1-1)*4=1
当i=2的时候,i+(i-1)*4= 2+(2-1)*4=6
也没有问题!
成了!
让实践去检验真理吧!
set.seed(10010)
R<-rnorm(n=50,mean=100,sd=30)
print(R,digits=1)
## [1] 67 105 98 54 83 61 37 64 148 81 121 94 120 83 138 84 109 158 66
## [20] 25 80 106 94 93 93 95 130 109 66 119 90 174 130 116 132 127 86 83
## [39] 116 90 51 121 84 88 23 133 116 123 97 106
list_groups<-list(0)
for (i in 1:10){
list_groups[[i]]<-R[(i+(i-1)*4):(i+4*i)]
}
names(list_groups)<-paste0("Group",1:10)
print(list_groups,digits=1)
## $Group1
## [1] 67 105 98 54 83
##
## $Group2
## [1] 61 37 64 148 81
##
## $Group3
## [1] 121 94 120 83 138
##
## $Group4
## [1] 84 109 158 66 25
##
## $Group5
## [1] 80 106 94 93 93
##
## $Group6
## [1] 95 130 109 66 119
##
## $Group7
## [1] 90 174 130 116 132
##
## $Group8
## [1] 127 86 83 116 90
##
## $Group9
## [1] 51 121 84 88 23
##
## $Group10
## [1] 133 116 123 97 106
以上我们设计、研究这个算法的过程就是一个从特殊(R[1:5]、R[6:10]、R[11:15])到一般(R[(i+(i-1)*4):(i+4*i)]),再从一般回到特殊(解决了i=4~10的所有问题)的过程。
当然,条条大路通罗马,你肯定还能找到其他的算法,譬如:
使用R语言中的seq( )设定R1=seq(1,50,by=5),R2=R1+4,再使用for循环提取数组R中的值:R[R1[i]:R2[i]];或者干脆用矩阵函数matrix(R, ncol=5,byrow=TRUE),这些方法都能实现分组的目的,也都可以称之为算法,最终是看你实现分组后的目的是什么。
感兴趣的读者还可以继续探索这个问题的新的算法。
为中国制造业之崛起而奋斗!供应链管理也能报国!
作者程晓华(John Cheng),全面库存管理(TIM)咨询独立顾问、制造业库存控制技术与策略课程创始人、讲师,《制造业库存控制技巧》《首席物料官》《决战库存》《制造业全面库存管理》《全面库存管理数学分析》著作者;1995年开始接触MRP,深耕制造业供应链管理近30年,一直聚焦于库存与交付问题的实践和咨询。
新书预告:《全面库存管理数学分析》第2版预计2025年底上市
全面库存管理(TIM)文章订阅号:ITOOTD
Mail: johnchengbj@126.com