【问题标题】:Algorithm to pick values from set to match target value?从集合中选择值以匹配目标值的算法?
【发布时间】:2011-02-23 19:13:55
【问题描述】:

我有一个大约 300 项长的固定整数值数组(集合 A)。该算法的目标是根据输入 R 从该数组中选择两个符合多个标准的数字(X 和 Y)。

正式要求:
从集合 A 中选取值 XY 使得表达式 X*Y/(X+Y) 为尽可能接近 R

仅此而已。我需要一个简单的算法来做到这一点。

其他信息:
集合 A 可以以任何方式排序或存储,最终将被硬编码。此外,通过一点数学,可以证明给定 X 的最佳 Y 是 Set A 中最接近的值表达式 X*R/(XR)。此外,XY 将始终大于 R

由此,我得到了一个可以正常工作的简单迭代算法:

int minX = 100000000;
int minY = 100000000;
foreach X in A
    if(X<=R)
        continue;
    else
        Y=X*R/(X-R)
        Y=FindNearestIn(A, Y);//do search to find closest useable Y value in A
        if( X*Y/(X+Y) < minX*minY/(minX+minY) )
        then
            minX = X;
            minY = Y;
        end
    end
end

我正在寻找一种比这种蛮力方法更优雅的方法。有什么建议吗?

【问题讨论】:

  • 你为什么称之为蛮力?蛮力将为所有X 尝试所有Y。你所拥有的是一个线性算法,它是最优的。好吧,也许你实际上可以进行二分搜索......
  • 算法不是线性的——示例代码只计算最优的Y值,而不在集合A中搜索与计算值最接近的值。使用二进制搜索添加此搜索使算法在 O(n*log(n)) 中运行。
  • 这听起来像是从一组可用电阻中找到两个电阻,以尽可能接近地匹配并联电路中的所需电阻。
  • @Daniel 实际上就是这样,我只是想将它简化为一个算法问题,以使其特定于编程。目标是找到一对接近所需电阻的并联电阻
  • 另一个有趣的问题是寻找电阻的最佳组合,而不是将组合限制为对,但允许任意基数。在这种情况下,如果 1/R1、1/R2、...1/RN(其中 R1...RN 是可用电阻)的(排序)值正在超增(en.wikipedia.org/wiki/Superincreasing_sequence),那么这个问题是微不足道的:这正是收银员会做的(假设无限硬币/纸币)给你一个指定的金额。这正是货币细分正在超级增长的原因! 1,2,5,10,20,50 等(而不是例如 1,2,3,5,8,12 等)

标签: algorithm search performance


【解决方案1】:

有关可能“更优雅”的解决方案,请参阅解决方案 2。


解决方案 1)

为什么不创建 R 的所有可能的 300*300/2 或 (300*299/2) 可能的 exact 值,将它们排序到数组 B 中,然后给出一个R,使用二分查找在B中找到最接近R的值,然后选择对应的X和Y。

我认为拥有数组 B(带有 X&Y 信息)不会占用大量内存,并且可以轻松进行硬编码(使用代码编写代码!:-))。

这将相当快:最坏的情况 ~ 17 次比较。


解决方案 2)

您还可以执行以下操作(未尝试证明,但似乎正确):

维护一个 1/X 值的数组,已排序。

现在给定一个 R,您尝试在 1/X 数组中找到最接近 1/R 的和。

为此,您需要维护两个指向 1/X 数组的指针,一个最小,一个最大,并不断增加一个并减少另一个以找到最接近 1/R 的一个。 (这是一道经典的面试题:求一个有序数组是否有两个数之和为 X)

在最坏的情况下,这将是 O(n) 的比较和加法。这也容易出现精度问题。不过,您可以通过维护一个反向排序的 X 数组来避免一些精度问题。

【讨论】:

  • 在我相信的大多数系统上,这将占用大约 2MB 的内存。
  • @CSharperWithJava:这不能接受吗?你总是可以把它放在磁盘支持的 B-Tree 中! ;-)
  • 但是单个磁盘访问可能会比原来的 O(nlogn) 算法慢! :)
  • @Dimitris:也许吧,但是做一百万个,并且使用操作系统缓存页面/良好的页面替换策略,基于磁盘的一个可能仍然会击败另一个。当然,如果使用 2MB 内存本身有问题,我怀疑磁盘也可能不可用(可能是某些嵌入式设备)。
  • 是的,RAM 就是他们所说的新磁盘 :)
【解决方案2】:

我想到了两个想法:

1) 由于集合 A 是常数,一些预处理可能会有所帮助。假设 A 的值范围不太大,可以创建一个大小为 N = max(A) 的数组。对于每个索引 i,您可以将 A 中的 最接近 值存储到 i。这样,您可以通过在恒定时间内找到最接近的值来改进您的算法,而不是使用二进制搜索。

2) 我看到你省略了 X2R 也不会产生任何解。所以要扫描的范围是R

【讨论】:

  • 关于范围限制的要点。这很有帮助。
【解决方案3】:

当输入的大小(大致)恒定时,O(n*log(n)) 解决方案可能比特定 O(n) 解决方案运行得更快。

我会从您最了解的解决方案开始,并在需要时从那里进行优化。

【讨论】:

  • 顺便说一下,如果n是一个常数,O(n*logn)就是O(1) :)
  • 是的,但是如果我在另一个循环中使用这个算法来计算许多 Rs 的许多 X 和 Y 值,O(1) 可能太长了。
  • @Dimitris - 这就是我的观点。由于输入大小是固定的,所有算法都是常数时间,所以你需要关注哪个常数较小,而不是作为n的函数,哪个具有更好的运行时间。
猜你喜欢
  • 2011-03-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-10-23
  • 2016-03-11
  • 2021-08-19
  • 2012-08-20
  • 1970-01-01
相关资源
最近更新 更多