【问题标题】:Algorithm - How to select one number from each column in an array so that their sum is as close as possible to a particular value算法 - 如何从数组中的每一列中选择一个数字,以使它们的总和尽可能接近特定值
【发布时间】:2014-09-13 22:41:17
【问题描述】:

我有一个 m x n 实数矩阵。我想从每一列中选择一个值,以便我选择的值的总和尽可能接近预先指定的总数。

我不是一个经验丰富的程序员(虽然我有一个经验丰富的朋友会提供帮助)。我想使用 Matlab、Mathematica 或 c++(必要时使用 MySQL)来实现这一点。 代码只需要运行几次,每隔几天运行一次——它不一定需要优化。我将有 16 列和大约 12 行。

【问题讨论】:

  • 取值范围是多少?特别是,数字可以是负数吗?
  • 如果这是作业,您可能会在课程材料中找到有关算法的提示。对我来说听起来像是一个优化问题。
  • @ooga 实数不是正数吗?我已经有一段时间没学数学了,所以我可能是错的。

标签: arrays algorithm select sum permutation


【解决方案1】:

通常我会建议使用动态编程,但这种情况的一些特征表明了另一种方法。一是性能要求轻;这个程序将只运行几次,听起来好像运行时间大约为几个小时不会有问题。其次,矩阵相当小。第三,矩阵包含实数,因此有必要四舍五入,然后进行一些复杂的搜索,以确保不会错过最佳可能性。

相反,我将建议采用以下半蛮力方法。 12**16 ~ 1.8e17,可能的选择总数太多了,但12**9 ~ 5.2e9 可以通过蛮力实现,而12**7 ~ 3.6e7 很适合记忆。计算前七列的所有可能选择。按总数对这些可能性进行排序。对于最后九列的每个可能选择,使用有效的搜索算法在前七列中找到最佳配对。 (如果你有很多内存,你可以试试 8 和 8。)

我将尝试使用 C++ 中的第一个实现,使用 <algorithm> 标准头文件中的 std::sortstd::lower_bound。测量它;如果速度太慢,请尝试使用内存中的 B+-tree(Boost 有吗?)。


我花了更多时间思考如何以最简单的方式实现我上面写的内容。这是一种适用于 12 x 16 矩阵的方法,适用于大约 4 GB 内存的 64 位机器。

前八列的选项数是12**8。每个选项由012**8 - 1 之间的一个4 字节整数表示。要解码选择索引i,第一列的行由i % 12 给出。更新i /= 12;。第二列的行现在由i % 12 等给出。

包含所有选项的向量大约需要 12**8 * 4 字节,或大约 1.6 GB。两个这样的向量需要 3.2 GB。为前八列准备一个,为后八列准备一个。按它们指示的条目的总和对它们进行排序。使用鞍背搜索找到最佳组合。 (将迭代器初始化为第一个向量,将反向迭代器初始化为第二个向量。当两个迭代器都未结束时,将当前组合与当前最佳值进行比较,并在必要时更新当前最佳值。如果当前组合总和大于目标,递增第一个迭代器。如果总和大于目标,则递增第二个迭代器。)

我估计这需要不到 50 行 C++。

【讨论】:

  • 由于蛮力部分在很大程度上控制了速度,如果将缓存的总和存储在磁盘上,您可能仍然没问题。 (特别是,如果算法以相同的值重复运行)。磁盘查找可能听起来很慢,但您知道所需的近似值。不需要 BTree,只需按所需值对文件进行排序。如果您还对其他列进行排序(按列中的值,然后按最小-最大差值降序排列),您将重复使用来自文件相同近似部分的值。
  • 第二种方法肯定更好。但是 4 GB 的内存并不是必需的。我们可以将所有需要的数据保存在 CPU 缓存中。诀窍是(而不是对 8 列的每个总和进行排序)只需按排序顺序生成这些总和:对前 4 列的每个总和进行排序;删除重复项;这是数组A;以相同的方式从接下来的 4 列中准备数组 B;将A[0] 添加到B 的每个元素中,并将结果推送到优先级队列中;删除 PQ 的顶部元素后,将其替换为 B 的相同元素加上 A 的下一个元素;在 PQ 不为空时重复。
【解决方案2】:

在不知道可能填充数组的值范围的情况下,像这样通用的东西怎么样:

  1. 将目标除以剩余的列数。
  2. 从该列中选择最接近该值的数字。
  3. 从 1 开始重复。直到选中每一列。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    相关资源
    最近更新 更多