【问题标题】:Partition the array with minimal difference以最小的差异对数组进行分区
【发布时间】:2019-05-06 06:27:38
【问题描述】:

给定一个数组 A N 整数。我需要找到X,以使以下两个值(A[1] * A[2] * ... * A[X])(A[X+1] * A[X+2] * ... * A[N]) 之间的差异最小,即我需要最小化| (A[1] * A[2] * ... * A[X]) - (A[X+1] * A[X+2] * ... * A[N]) |,如果有多个这样的X 值,打印最小的一个。

约束:-

  • 1 N

  • 1 A[i]

我无法找到有效解决此问题的方法。 解决此问题的最佳方法应该是什么。有没有什么特殊的算法可以让大量的数字相乘。

【问题讨论】:

  • 从概念上讲,将数组分为三部分:leftmiddleright。最初,middle 是整个数组,leftright 是空的。您还需要一个浮点值,它代表左侧产品到右侧产品的ratio。最初,ratio 为 1。如果ratio >= 1middle 的最后一个元素移动到right,并将ratio 除以该元素。否则将middle 的第一个元素移动到left,并将ratio 乘以该元素。继续直到middle 只有一个元素。答案是将最后一个元素移动到leftright
  • @krpra 你对此有什么解决方案?

标签: algorithm math data-structures


【解决方案1】:

这个想法是使用前缀和后缀产品的形式。

让:

  1. pre[i] = A[1] * A[2] * ... A[i]
  2. suf[i] = A[i] * A[i + 1] * ... A[N]

您可以在 O(n) 时间内计算这些数组,如下所示:

  • pre[i] = A[i] * pre[i - 1]pre[1] = A[i]

  • suf[i] = A[i] * suf[i + 1]suf[N] = A[n]

然后,从 i = 1 迭代到 N 并计算最大值:

abs(pre[i] - suf[i + 1])

观察pre[i] - suf[i + 1] 与:

(A[1] * A[2] * ... * A[i]) - (A[i + 1] * A[i + 2] ... * A[N])

这正是您想要计算的。

【讨论】:

  • 感谢@Rishav 的回复。是的,这可以在O(n) 时间完成,但是如何存储 10^17 和 10^18 范围内的 100,000 个数组元素的乘积。是否可以获得所有这些数组元素的乘积??跨度>
  • 是的,我没有考虑到这一点。让我考虑一下。
  • @krpra 您可以按照其他答案的建议使用对数。乘法变成对数的加法,你需要检查最接近 0 的对数的差。
  • @Rishav 你确定对数值足够长吗?这听起来像是一个类似竞赛的问题。如果这是正确的,测试数据将被专门设计为强制使用全精度,例如在某些时候差异将出现在 10^17 范围内的几个单位中。取对数会降低必须处理的绝对值,但不会会降低必要的精度。并且 OP 没有指定编程环境和可用的数值(浮点)类型。
  • @CiaPan 是的,这是一个类似竞赛的问题。我怎么解决这个问题?有没有其他不考虑对数的算法?
【解决方案2】:

你可以在 O(n) 中做到这一点:第一次 - 得到数组 (P) 的所有元素的乘积,第二次 - 假设在开始时左边部分是一个,第二个是 P,在每一步 i在 X[i] 上左乘并在 X[i] 上右除。继续这个过程,直到左边小于右边。

由于您有大量数字,因此您需要一些大数乘法。所以,也许你最好转移到 A[i]、LA[i] 的对数数组并转移到新的标准。

编辑:

正如@CiaPan 所说,标准 64 位十进制的精度不足以在这里进行日志操作(因为值可能高达 10^18)。

所以要解决这个问题,你应该首先将源数组的值拆分成对,这样:

s[2*i]   = a[i].toDouble / (10.0^9)
s[2*i+1] = a[i]/s[2*i]  

数组 s 比源数组 a 长两倍,但它的值不超过 10^9,所以应用 log 操作是安全的,然后为数组 s 找到所需的 sX 并将其除以 2 得到数组 a 的 X .

不需要超精度对数逻辑。

【讨论】:

  • 您能否更清楚地阐述您的方法。如果我没记错的话,整个数组的乘积是不可能的。假设如果数组中有 10^5 个元素,并且所有数组元素都在 10^17 到 10^18 范围内,那么我们如何获得所有数组元素的乘积。
  • @krpra 通过使用一些BigInteger 库,也许...?
  • @krpra 您还可以为大于标准整数类型的整数实现自己的算法,但不能任意大 - 例如,最大。 14 个字节。主要算法很简单,所有的困难(和效率问题)都转移到乘法和除法例程上。
  • 你不必持有这么大的数字,你可以移动到我提到的对数。因此,您首先创建数组 la[i] = log(a[i])。标准移至 |sum[0-k] (la[i]) - sum[(k+1)-n] (la[i])| -> 分钟(k)。
  • @Nyavro 你确定它会有所帮助吗?区分 10^(30) 和 10^(30)+1 需要多少位整数表示?区分 log(10^(30)) 和 log(10^(30)+1) 需要多少位浮点表示?在这两种情况下实际上有多少位可用? (另见this comment。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多