【问题标题】:How do I efficiently multiply a range of values of an array with a given number?如何有效地将数组的一系列值与给定数字相乘?
【发布时间】:2015-09-22 21:35:57
【问题描述】:

天真的方法是线性迭代范围并与范围中的每个数字相乘。

示例:数组:{1,2,3,4,5,6,7,8,9,10}; 将索引 3 与索引 8 相乘以 2。假设一个基于索引。

结果数组应为:{1,2,6,8,10,12,14,16,9,10};

我知道二进制索引树可用于“和”部分。如何有效地将给定范围与数字相乘?

【问题讨论】:

  • 在我看来线性方法是最有效的。:) 还是我不明白?
  • 到目前为止你尝试了什么?
  • 我尝试了遍历范围并将范围的每个数字与给定数字相乘的幼稚方法。但由于约束条件高,我猜效率不高。
  • 我同意来自莫斯科的 Vlad,但您可以使用某种 SIMD (en.wikipedia.org/wiki/SIMD)。
  • 这将取决于您稍后将如何检索这些数字(您的查询)。你能解释一下那部分吗?

标签: c++ arrays algorithm multiplication fenwick-tree


【解决方案1】:

如果你想实际修改数组,你不能比简单的线性算法做得更好:你必须迭代整个范围并相应地修改每个索引。

如果你的意思是,你有像你描述的更新操作和查询操作find the sum in the range from x to y,那么段树可以帮助这样。

对于每个更新操作left, right, value,对于每个具有包含在[left, right] 中的关联范围的节点,其总和将乘以value,因此相应地更新它并停止进行递归。这也适用于您不会递归的间隔,因此不是实际更新总和,而是在每个节点中存储其关联间隔乘以多少。

当从递归返回时,您可以根据此信息重新计算实际总和。

伪代码

Update(node, left, right, value):
  if [left, right] does not intersect node.associated_range:
    return     

  if [left, right] included in node.associated_range:
    node.multiplications *= value # 1 initially
    return

  Update(node.left, left, right, value)
  Update(node.right, left, right, value)

  node.sum = node.left.sum * node.left.multiplications +
             node.right.sum * node.right.multiplications

基本上,每个节点将通过仅考虑子段中的乘法来存储其总和。它的真实总和将在查询期间通过使用有关影响该间隔的乘法的信息来延迟计算。

然后执行求和查询几乎就像对段树的常规查询一样:只需确保将总和乘以它们或父区间乘以的数量。

伪代码

Query(node, multiplications = 1, left, right):
  if [left, right] does not intersect node.associated_range:
    return 0     

  if [left, right] included in node.associated_range:
    return node.sum * multiplications

  return Query(node.left, multiplications * node.multiplications, left, right) +
         Query(node.right, multiplications * node.multiplications, left, right)

最初构建树的函数留作练习(您可以做得比调用 update n 次更好)。

【讨论】:

    【解决方案2】:

    您还可以通过 BIT 更新范围。(Range update by BIT)。

    您还可以使用分段树、RMQ。您可以轻松地与给定的数字相乘。

    关于 ST 和 RMQ 的不错的教程。 RMQ TopcoderSegment Tree

    【讨论】:

    【解决方案3】:

    据我所知,唯一的方法是遍历向量,然后应用乘法。

    你可以像这样定义一个函数:

    void VecMultiply(std::vector<int>& pVector, int Multiplier);
    

    并像这样实现它:

    void VecMultiply(std::vector<int>& pVector, int Multiplier)
    {
        for (unsigned int i = 0; i < pVector.size(); i++)
        {
            *pVector[i] *= Multiplier;
        }
    }
    

    或者您甚至可以使用模板传递任何乘以任何值的向量:

    template<typename T>
    template<typename M>
    void VecMultiply(std::vector<T>& pVector, M Multiplier)
        {
            for (unsigned int i = 0; i < pVector.size(); i++)
            {
                *pVector[i] *= Multiplier;
            }
        }
    

    【讨论】:

    • 你可以做得更好,如果你传递了be引用,则不需要取消引用参数。
    【解决方案4】:

    您可以按照线性方法并将其编码为C++11 like

    std::array<int, 10> nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
    std::for_each(nums.begin() + 2, nums.begin() + 8, [](int & n) { n *= 2; });
    for (int & n : nums) std::cout << n << " ";
    

    输出

    1 2 6 8 10 12 14 16 9 10 
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-24
      • 2021-08-04
      • 2015-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-01-03
      • 2015-01-22
      相关资源
      最近更新 更多