一个插值相关的简单的整除问题

近段时间在写动作捕获数据插值相关的代码,在插值的过程中遇到了一点关于整除的问题。

该问题来源于以下场景:

对一条动作捕获数据的轨迹 \(D = \left\{ s_0, s_1, s_2, ..., s_n \right\}\), 数据的采样间隔为 \(t_m\) ,现在需要通过插值来将这条动作轨迹的采样间隔降低为 \(t\)\(t_m > t\))。现已有两个数据点之间的均匀插值算法 \(I\)(具体实现与该问题无关): \(\left\{ s'_0, s'_1, ..., s'_{m - 1}, s'_{m} \right\} = I\left(s_{i}, s_{i + 1}, m\right)\)。 问如何给定上述插值算法的参数 \(m\) ,使得插值后,两点之间的 \(m + 1\) 个点能够完整覆盖原先的采样间隔?

对上述问题进行抽象,记数据采样间隔为 \(N = t_m\),要求采样间隔为 \(n = t\),插值算法参数为 \(m\)。则上述问题转化为:给定两个数 \(N\)\(n\),它们满足关系 \(N >> n\),求一个整数 \(m\),使得:

\[ \begin{align} n\cdot m &\geq N \\ n\cdot (m-1) &< N \end{align} \]

对这个问题,第一反应大概会是这个表达式:

\[ m = \left[ \frac{N}{n} \right] \]

对于能够整除的情况,如 \(N=0.25\)\(n=0.05\)。则根据上述的算法, \(m = 5\),符合需求。但是当不能整除时,如 \(N=0.25\)\(n=0.04\),则计算得到 \(m = 5\),采样得到的点的数量并不能覆盖整个采样间隔。类似的问题存在与下面的两个表达式:

\[ m = \left[ \frac{N}{n} \right] + 1 \]

\[ m = \left[ \frac{N}{n} + 0.5 \right] \]

所以似乎常见的整除相关的表达式都不能完美地解决这个问题。下面根据取整符号的性质来推导出正确的表达式。

对于取整符号 \(\left[\right]\) ,有以下的性质

\[ \text{For}\, m'=\left[ \frac{N'}{n'} \right],\, m' \, \text{satisfies}\\ m'\cdot n' \leq N' < (m'+1)\cdot n' \tag{1} \]

而我们期望的结果为:

\[ n\cdot (m-1) < N \leq n\cdot m \tag{2} \]

可以观察到目标结果和取整符号之间存在一定的差异,所以对期望结果进行等效变换如下

\[ \begin{align} n\cdot (m-1) < &N \leq n\cdot m \notag\\ -n\cdot (m-1) > &-N \geq -n\cdot m \notag\\ n\cdot (-m+1) > &-N \geq n\cdot (-m) \notag\\ n\cdot ((-m)+1) > &(-N) \geq n\cdot (-m) \notag\\ n\cdot (-m) \leq &(-N) < n\cdot ((-m)+1) \tag{3} \end{align} \]

对比等式(1) 和 (3) ,将 \(m'\) 等效为 \(-m\)\(N'\) 等效为 \(-N\)\(n'\) 等效为 \(n\)

则能够得到正确的等式如下:

\[ m = - \left[-\frac{N}{n}\right] \tag{*} \]

需要注意的是,Python 中的 int() 函数并不等价于数学上的取整函数。其对于负数的取整是向上取整,而非向下取整。一个可靠的替代为 math.floor()