【问题标题】:Dynamic programming to reach a threshold?动态规划达到阈值?
【发布时间】:2017-10-27 19:24:28
【问题描述】:

假设您正在使用弹性笔记本电脑。假设您有一台具有i 功率的液压机,其设置m(最大功率)会损坏笔记本电脑。如果笔记本电脑在电源i 时坏了,那么它也会因任何im 而坏掉。如果它在i 的电源上没有中断,那么0i 的任何东西都不会中断。


我认为,为了弄清楚i 是什么,将需要使用印刷机的次数降至最低的最佳方法是将其分成两半并不断重复。不破就走高,破了就走低。我认为它的复杂性是O(n/2),对吗?


现在假设我们只有n 笔记本电脑可用,如果所有n 在我们达到阈值之前中断,那么我们就失败了。如果我们有n 笔记本电脑并且印刷机有m 设置,我将如何获得动态编程算法,以便我们可以在最坏的情况下获得印刷机必须运行的最少次数?

我可以使用非限制方式的答案吗?子问题是什么?

【问题讨论】:

    标签: algorithm dynamic-programming


    【解决方案1】:

    这不是答案,但评论太长了。

    • 如果您只有一台笔记本电脑,最好线性搜索整个范围,这会产生 O(m) 复杂度。

    • 拥有 2 台笔记本电脑,您可以做得更好。

      sqrt(2m) 进行第一次尝试。如果笔记本电脑坏了,你需要sqrt(2m) - 1 尝试第二个。如果没有,请使用2*sqrt(2m) - 1 的第一个。通常,只要第一台笔记本电脑完好无损,请将p(k+1) 设置选为p(k) + sqrt(2m) - k。这保证了如果它发生故障,您仍然有 sqrt(2m) - k 尝试检查未知响铃,并通过总共 sqrt(2m) 尝试完成工作,无论它在哪里中断。

      如果第一台笔记本电脑从未损坏,您将再次尝试sqrt(2m) 到达m(我不想做数学,因为 SO 不支持 LaTeX,但它很简单,只需尝试)。

      因此,如果有 2 台笔记本电脑,复杂度为 O(sqrt(m))

    您拥有的笔记本电脑越多,分析就越困难。想法仍然存在:选择两种结果都需要相同数量的尝试才能完成的设置。可以证明只要n远小于log(m),复杂度就是O(m ** (1/n))

    【讨论】:

      【解决方案2】:

      第一个问题确实可以先尝试m/2,然后根据结果尝试m/43m/4 em>,...等。这使得问题 O(log(m)),而不是 O(m/2)(我想 n 是一个错字)。

      第二个版本也称为egg dropping problem

      递归函数可以帮助找到尝试次数。为了避免重复计算相同的配置两次,您确实可以使用动态规划。如果您有足够的笔记本电脑来执行二进制搜索(取中间压力,并根据结果取剩余压力的中间,......等等),停止递归并返回对数(以 2 为底)(不是一半!) 的剩余压力数。

      这是一个基本 JavaScript 的实现:

      function getLeastAttempts(numPressures, numLaptops) {
          var memo = {}; // Storage for known results (dynamic programming)
      
          function best(numPressures, numLaptops) {
              var result, numMinAttempts, numAttempts, pressure;
              // If no pressures available, we need no more attempts
              if (numPressures <= 0 || numLaptops <= 0) return { numAttempts: 0 };
              // We ccould apply each pressure from low to high to minimise damage;
              //   this represents an upper limit to the minimum number of attempts we need:
              result = {
                  numAttempts: numPressures,
                  firstAttemptAtPressure: 1
              };
              // If we cannot to afford losing another laptop without finding out the
              //    answer, then that is what we will have to do: from low to high:
              if (numLaptops == 1) return result;
              // Otherwise check if we have calculated this before
              if (memo[numLaptops] && memo[numLaptops][numPressures])
                  return memo[numLaptops][numPressures];
              // Otherwise if we have enough laptops for doing a binary search:
              if (2**numLaptops > numPressures) return {
                  numAttempts: Math.floor(Math.log2(numPressures)) + 1,
                  firstAttemptAtPressure: Math.floor(numPressures / 2)
              };
              // Otherwise, apply recursion to find out the least number of attempts
              for (pressure = 0; pressure < numPressures; pressure++) {
                  // Applying this pressure to the laptop can have 2 results, take the worst
                  //    of the two and add one for representing this attempt:
                  numAttempts = 1 + Math.max(
                      // 1. It breaks the laptop: try lower pressures with one less laptop
                      best(pressure, numLaptops-1).numAttempts,
                      // 2. The laptop is OK: try higher pressures with same laptops
                      best(numPressures - (pressure+1), numLaptops).numAttempts
                  );
                  // If applying this pressure resulted in fewer attempts to find out the 
                  //    truth, then remember this:
                  if (numAttempts <= result.numAttempts) {
                      result.numAttempts = numAttempts;
                      result.firstAttemptAtPressure = pressure;
                  }
              }
              // Store result in memory (dynamic programming)
              if (!memo[numLaptops]) memo[numLaptops] = [];
              memo[numLaptops][numPressures] = result;
              // ...and return it
              return result;
          }
          // Outermost call of recursive function
          return best(numPressures, numLaptops);
      }
      
      // I/O handling
      
      function processIO() {
          var result = getLeastAttempts(+inpPressures.value, +inpLaptops.value);
          outAttempts.textContent = result.numAttempts;
          outPressure.textContent = result.firstAttemptAtPressure;
      }
      
      inpPressures.oninput = processIO;
      inpLaptops.oninput = processIO;
      processIO(); // invoke calculation on page load
      Highest pressure (m): <input type="number" id="inpPressures" value=100><br>
      Number of laptops (n): <input type="number" id="inpLaptops" value=3><br>
      Number of attempts: <span id="outAttempts"></span><br>
      First pressure to attempt: <span id="outPressure"></span><br>

      更改 sn-p 中的输入以进行实验。

      此算法不包括Wikipedia article中提到的最终优化。

      【讨论】:

      • 分析不正确。众所周知,使用 2 台笔记本电脑可以实现 O(sqrt(m))。
      • 是掉蛋问题。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-21
      • 2015-05-04
      • 2019-01-14
      相关资源
      最近更新 更多