往来款项的账龄划分是个很简单的问题。本文主要用途为给实习生作简单的培训,让其看完后能快速上手做底稿。
1. 假设
本文论述往来款余额账龄划分的方法,有如下假设:
上期账龄已知;
先进先出法。本期减少优先消耗上期末最长账龄的余额;
除最长账龄段之外,上期末任意账龄段的余额,在本期末的余额不会增加;
已经对本期增减金额作如下处理:本期增加金额如为负数,视同本期减少金额,本期减少如为负数,视同本期增加金额;
2. 公式推导
2.1 单个客商的账龄向量推导
首选考虑单个客商的账龄问题,设:
1.账龄共分为n段;
2.期初、本期增加、本期减少、期末金额分别为:S、D、C、E;
3.期初、本期增加、本期减少、期末金额的账龄向量分别为:\(\vec{S}=\begin{bmatrix}{0}\newline{s_1}\newline\cdots\newline{s_n+s_{n-1}}\end{bmatrix}\)、\(\vec{D}=\begin{bmatrix}{d}\newline{0}\newline\cdots\newline{0}\end{bmatrix}\)、\(\vec{C}=\begin{bmatrix}{0}\newline{0}\newline\cdots\newline{c}\end{bmatrix}\)、\(\vec{E}=\begin{bmatrix}{e_1}\newline{e_2}\newline\cdots\newline{e_n}\end{bmatrix}\);
期末余额与账龄向量之间有一个映射关系存在,易知\(E=sum(\vec{E}),S=sum(\vec{S}),D=sum(\vec{D}),C=sum(\vec{C})\);
\[\begin{aligned}
E&=S+D-C \newline
&\Downarrow\newline
\begin{bmatrix}
e_1\newline
e_2\newline
e_3\newline
\cdots\newline
e_n
\end{bmatrix}
&=
\begin{bmatrix}
0\newline
s_1\newline
s_2\newline
\cdots\newline
s_{n-1}+s_n
\end{bmatrix}
+
\begin{bmatrix}
d\newline
0\newline
0\newline
\cdots\newline
0
\end{bmatrix}
-
\begin{bmatrix}
0\newline
0\newline
0\newline
\cdots\newline
c
\end{bmatrix}
\end{aligned}
\]
其实\(e_j\)的结果只有三种可能,取决于\(c\)与\(\sum_{i=j}^{n}s_i\)、\(\sum_{i=j-1}^{n}s_i\)之间的大小关系:
\(c\leqslant\sum_{i=j}^{n}s_i\),此时上期长于\(j\)(落到本期为长于\(j-1\))账龄段的余额合计,减去本期减少金额,还有余额,故\(e_j=s_{j-1}\);
\(c\in(\sum_{i=j}^{n}s_i,\sum_{i=j-1}^{n}s_i)\),此时\(e_j=\sum_{i=j-1}^{n}s_i-c\);
\(\sum_{i=j-1}^{n}s_i\leqslant{c}\),此时\(e_j=0\);
特别地,当\(j=1\)时,\(e_1=max[0,d+min(0,\sum_{i=1}^{n}s_i-c)]\);
特别地,当\(j=n\)时,\(e_n=max(0,s_{n-1}+s_n-c)=max[0,s_{n-1}+min(0,s_n-c)]\);
结论:
\[\begin{aligned}
e_j&=
\begin{cases}
max[0,d+min(0,\sum_{i=1}^{n}s_i-c)], & j=1 \newline
max[0,s_{j-1}+min(0,\sum_{i=j}^{n}s_i-c)], & 1 \end{cases}\newline \newline \vec{E}&= \begin{bmatrix} max[0,d+min(0,\sum_{i=1}^{n}s_i-c)]\newline max[0,s_{1}+min(0,\sum_{i=2}^{n}s_i-c)]\newline \cdots\newline max[0,s_{n-2}+min(0,s_{n-1}+s_n-c)]\newline max[0,s_{n-1}+min(0,s_n-c)]\newline \end{bmatrix} \end{aligned} \] 2.2 多个客商的账龄矩阵推导 设有m个客商,账龄分为n段,于是有: \[\begin{aligned} E&=S+D-C \newline \begin{bmatrix} e_{11}&e_{12}&e_{13}\cdots{e_{1n}}\newline e_{21}&e_{22}&e_{23}\cdots{e_{2n}}\newline e_{31}&e_{32}&e_{33}\cdots{e_{3n}}\newline \cdots\newline e_{m1}&e_{m2}&e_{m3}\cdots{e_{mn}}\newline \end{bmatrix}_{(m\times{n})} \newline &= \begin{bmatrix} {0}&s_{11}&s_{12}\cdots{s_{1,n-1}+s_{1n}}\newline {0}&s_{21}&s_{22}\cdots{s_{2,n-1}+s_{2n}}\newline {0}&s_{31}&s_{32}\cdots{s_{3,n-1}+s_{3n}}\newline \cdots\newline {0}&s_{m1}&s_{m2}\cdots{s_{m,n-1}+s_{mn}}\newline \end{bmatrix}_{(m\times{n})} \newline &+ \begin{bmatrix} d_{1}&{0}&{0}\cdots{0}\newline d_{2}&{0}&{0}\cdots{0}\newline d_{3}&{0}&{0}\cdots{0}\newline \cdots\newline d_{m}&{0}&{0}\cdots{0}\newline \end{bmatrix}_{(m\times{n})} - \begin{bmatrix} {0}&{0}&{0}\cdots{c_1}\newline {0}&{0}&{0}\cdots{c_2}\newline {0}&{0}&{0}\cdots{c_3}\newline \cdots\newline {0}&{0}&{0}\cdots{c_m}\newline \end{bmatrix}_{(m\times{n})} \newline &= \begin{bmatrix} max[0,d_1+min(0,\sum_{i=1}^{n}s_{1i}-c_1)]& max[0,s_{11}+min(0,\sum_{i=2}^{n}s_{1i}-c_1)]& \cdots& max[0,s_{1,n-1}+min(0,\sum_{i=n}^{n}s_{1i}-c_1)]\newline max[0,d_2+min(0,\sum_{i=1}^{n}s_{2i}-c_2)]& max[0,s_{21}+min(0,\sum_{i=2}^{n}s_{2i}-c_2)]& \cdots& max[0,s_{2,n-1}+min(0,\sum_{i=n}^{n}s_{2i}-c_2)]\newline max[0,d_3+min(0,\sum_{i=1}^{n}s_{3i}-c_3)]& max[0,s_{31}+min(0,\sum_{i=2}^{n}s_{3i}-c_3)]& \cdots& max[0,s_{3,n-1}+min(0,\sum_{i=n}^{n}s_{3i}-c_3)]\newline \cdots\cdots&\newline max[0,d_m+min(0,\sum_{i=1}^{n}s_{mi}-c_m)]& max[0,s_{m1}+min(0,\sum_{i=2}^{n}s_{mi}-c_m)]& \cdots& max[0,s_{m,n-1}+min(0,\sum_{i=n}^{n}s_{mi}-c_m)]\newline \end{bmatrix}_{m\times{n}}\newline \begin{Bmatrix} e_{ij} \end{Bmatrix}_{m\times{n}} &= \begin{Bmatrix} \begin{cases} max[0,s_{i,j-1}+min(0,\sum_{\lambda=j}^{n}s_{i,\lambda}-c_i], &(1\leqslant{i}\leqslant{m},1<{j}\leqslant{n},i,j\in{N_+})\newline max[0,d_i+min(0,\sum_{\lambda=1}^{n}s_{i,\lambda}-c_i)], &(j=1,1\leqslant{i}\leqslant{m},i\in{N_+}) \end{cases} \end{Bmatrix}_{m\times{n}} \end{aligned} \] 3. 结论 经过上述推导可知,在一共划分n个账龄段,且上期账龄余额已知的情况下,本期第i个客商的第j个账龄段的余额为: \[\begin{aligned} \begin{Bmatrix} e_{ij} \end{Bmatrix}_{m\times{n}} &= \begin{Bmatrix} \begin{cases} max[0,s_{i,j-1}+min(0,\sum_{\lambda=j}^{n}s_{i,\lambda}-c_i], &(1\leqslant{i}\leqslant{m},1<{j}\leqslant{n},i,j\in{N_+})\newline max[0,d_i+min(0,\sum_{\lambda=1}^{n}s_{i,\lambda}-c_i)], &(j=1,1\leqslant{i}\leqslant{m},i\in{N_+}) \end{cases} \end{Bmatrix}_{m\times{n}} \end{aligned} \] 4. 算法实现 #[derive(Debug)] struct CompData{ name:String, start:Vec dr:f64, cr:f64, end:Vec } impl CompData{ pub fn new( na:String, _st:Vec _dr:f64, _cr:f64, )->Self{ let mut d=CompData{ name:na, start:_st, dr:_dr, cr:_cr, end:Vec::new(), }; d.age_split(); return d; } fn age_split( self:&mut Self )->(){ if self.start.is_empty(){ panic!(); } if self.start.len()<2{ panic!(); } self.end=self.start.clone(); self.end.insert(0,self.dr.clone()); let last_1=self.end.pop().unwrap(); let last_2=self.end.pop().unwrap(); self.end.push(last_1+last_2-self.cr.clone()); negative_tail_process(&mut self.end); } fn check_balance(self:&Self)->bool{ self.start.iter().sum:: } } fn if_negative_tail<'a>(v:&'a Vec let tail=v.get(v.len()-1); match tail{ Option::Some(tail)=>{ if tail <&0.0{ true }else{ false } }, Option::None=>{ panic!("tail is not a number!"); }, } } fn negative_tail_process<'a>( aged_split:&'a mut Vec )->(){ if aged_split.is_empty(){ panic!("check the input argument:{:?}",aged_split); } let age_len=aged_split.len(); while if_negative_tail(&aged_split){ if aged_split.len()==1{break;} let last_1=aged_split.pop(); let last_2=aged_split.pop(); match last_1{ Option::Some(last_1)=>{ match last_2{ Option::Some(last_2)=>{ aged_split.push(last_1+last_2); }, Option::None=>{ panic!("last_1 is fine, yet last_2 is None."); }, } }, Option::None=>{ panic!("last_1 is None;last_2 does not matter."); }, } } while aged_split.len() aged_split.push(0f64); } }