【问题标题】:How to multiply an integer by a fraction如何将整数乘以分数
【发布时间】:2017-04-09 08:24:40
【问题描述】:

所以我的目标是创建一个签名如下所示的函数:

template<typename int_t, int_t numerator, int_t denominator>
int_t Multiply(int_t x);

类型为整型,既是单参数类型,又是返回类型。

另外两个模板参数是分数的分子和分母。

此函数的目标是将数字“x”乘以两个模板值中的任意分数。一般来说,答案应该是:

floor(x*n/d) mod (int_t_max+1)

最简单的做法是先将“x”乘以分子,然后再除。

看一个具体的案例,假设int_t=uint8_t,“x”是 30,分子和分母分别是 119 和 255。

走这条天真的路线失败了,因为 (30*119)mod 256 = 242,除以 255 然后取底为 0。真正的答案应该是 14。

下一步将只为中间值使用更大的整数大小。因此,不是在 mod 256 中进行 30*119 计算,而是在 mod 65536 中进行。这在一定程度上确实有效,但是当我们尝试在 Multiply 函数中使用最大整数大小时它会失败。

下一步就是使用一些BigInt 类型来保存值,这样它不会溢出。这也是可行的,但是使用模板参数的全部原因是这样可以非常快,并且使用 BigInt 可能会破坏该目的。

那么问题来了:

是否有一种算法只涉及移位、乘法、除法、加法、减法和余数运算符,可以执行此数学函数,而不会导致溢出问题?

【问题讨论】:

  • 回答这里提出的问题:是的,有一种算法只涉及移位、乘法、除法、加法、减法和余数运算符,可以执行此数学函数,而不会导致溢出问题。哦,至于它是什么,请注意,例如,一个32位的数字可以表示为a*65536+b,其中ab都是16位数字;因此,对 32 位数字的此类操作可以表示为对 16 位数字的等效操作,并以数学方式组合结果。您只需要自己弄清楚这些操作是什么。
  • @sam 但它比使用更大的 int 更快吗?
  • 我认为没有一种算法可以避免对任意操作数进行全精度计算。 (如果输入和分母碰巧有 gcd > 1,你可以以此作为初始步骤。)这意味着计算完整的产品,然后使用多个窄类型实例进行划分。这将是丑陋的,并且总是比单个硬件在更大宽度上的乘法和除法要慢。建议您重新指定模板以包含中间产品的类型。
  • 从 486DX 开始,每个 Intel CPU 都带有一个内置的数学协处理器。除非您没有列出其他一些要求(例如,可接受的精度损失)或为极其关键的部分编写代码,否则我怀疑这是否真的需要按照您描述的方式进行优化
  • 假设输入是 8 位值并且数学是在 16 位中完成的,那么为什么 max_int 值 255 是个问题? 255*255 = 65025,适合 65536。

标签: c++ algorithm templates optimization


【解决方案1】:

对于 Windows 平台,我强烈建议您查看large integers 上的这篇文章,该文章目前支持最多 128 位整数值。您可以根据 int_t 的 bit-with 对模板进行专门化,以充当这些操作系统功能的代理。

为乘法实现“移位和加法”可能提供了一个足够好的替代方案,但除法肯定会抵消您所希望的任何性能提升。

然后有一些“捷径”,例如尝试查看 numeratordenominator 是否可以通过分数缩减来简化,例如乘以 35/49 与乘以 5/7 相同。

想到的另一种选择是“逐渐”乘以“分数”。不过,这需要一些解释:

假设你乘以1234567/89012。我将使用十进制表示法来提高可读性,但同样适用于(自然)二进制数学。

所以我们有一个值x,需要乘以那个分数。由于我们正在处理整数算术,让我们重新打包这个分数:

1234567/89012 = A + B/10 + C/100 + D/1000...
 = 1157156/89012 + ((71210*10)/89012)/10 + ((5341*100)/89012)/100 + ((801*1000)/89012)/1000...
 = 13 + 8/10 + 6/100 + 9/1000...

事实上,此时您的主要问题是“我的计算要精确到什么程度?”。根据该问题的答案,您将拥有该长序列的适当数量的成员。

这将为您提供所需的精度并提供一种通用的“无溢出”方法来计算乘积,但计算成本是多少?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多