【问题标题】:Solving the water jug problem解决水壶问题
【发布时间】:2010-10-13 05:44:40
【问题描述】:

在阅读一些关于初步数论的lecture notes 时,我遇到了解决方案 水壶问题(有两个水壶)总结如下:

利用两个数的 GCD 的性质,即 GCD(a,b) 是 a 和 b 的最小可能线性组合,因此某个量 Q 只能由 2 个水罐测量,当且当 Q 是一个*GCD (a,b),因为 Q=sA + tB,其中:

n = a positive integer
A = capacity of jug A
B=  capacity of jug B

然后,讨论解决方法

解决方案的另一个模型是将各种状态建模为人工智能中经常使用的状态空间搜索问题。

我的问题是:还有哪些其他已知方法可以为解决方案建模,以及如何建模?谷歌并没有吐太多。

【问题讨论】:

    标签: math computer-science theory


    【解决方案1】:

    这种类型的问题通常可以通过dynamic programming 技术解决。我经常看到这个特定问题被用作运筹学课程中的一个例子。一个很好的分步描述是here

    【讨论】:

    • 链接(最终)是死链接。
    【解决方案2】:

    通过barycentric coordinates(真的!),如始终出色的网站 Cut-the-Knot 所述:Barycentric coordinates: A Curious Application 是一种惊人而有趣的方法(用于 3 个水壶)。

    【讨论】:

      【解决方案3】:

      搜索空间方法是我所建议的。我编写了一个程序来使用 BFS 解决一般水壶问题。如果你愿意,可以发给你。

      【讨论】:

      • 是的,请在这里分享。
      【解决方案4】:

      只针对 2 罐问题

      Q = A * x + B * y
      

      Q = 您需要的加仑数。

      注意: Q 必须是 Gcd(A,B) 的倍数,否则无解。如果 Gcd(A,B) == 1,则 Any Q 有解。

      1) 方法一: Extended Euclid's Algorithm 将比任何图形算法更快地解决它。

      2) 方法二: 这是一个朴素的方法。(注意,这可能会抛出 2 个解决方案,您必须选择哪个更短)

      有问题的问题可以通过repeatedly从一个桶A填充到另一个桶B(顺序无关紧要)直到它填满你想要的数量......ofcoz,当一个桶填满时,你清空它并继续。

          A = 3, B = 4 and Q = 2
      

      反复填写A->B

          A B
         ######
          0 0
          4 0
          1 3
          1 0
          0 1
          4 1
          2 3 <-Solution
      

      让我们试着观察如果我们反过来会发生什么, 填写 B->A

      A  B
      #####
      0  0
      0  3
      3  0
      3  3
      4  2 <- Solution
      

      在这种情况下,填充 B->A 比 A->B 更快地为我们提供了目标状态

      通用 N 壶 这是一个有趣的paper

      【讨论】:

      • 决定先填充哪个:这 - “IF(Q is b/w A AND B) THEN max(A, B) ELSE min(A, B)” 真的有帮助吗?
      • @AMAR,我不这么认为。不能肯定地说。你有证据吗?
      • 不。这是一个问题。我曾尝试过使用较小的 5-6 对来解决问题。不确定它是否适用于所有人。
      • 是否有可能有这么大体积的水壶,这两种方法都需要相同数量的步骤:先装 A,先装 B??
      • @st0le 论文的链接(最后)是死链接。
      【解决方案5】:

      我在一项研究中遇到了这个问题。正如你和 st0le 在这里所说的那样,我发现扩展欧几里德算法作为问题的答案。但是这个答案并不让我满意,因为我认为这是一个定量的答案,而不是一个定性的答案(即算法并没有说要采取什么步骤才能达到结果)。

      我想我找到了一个不同的解决方案,它总是以最少的步骤达到结果。

      这里是:

      1. 检查问题的可行性:
        • Q 是 MCD(A,B) 的倍数;
        • Q 为
      2. 选择服务水壶(即您将用泵重新装满的水壶)。假设 A > B(你可以很容易地找到哪个水壶更大):

        if(Q is multiple of B)
            return B
        
        a_multiplier = 1
        b_multiplier = 1
        difference = A - B
        a_multiple = A
        b_multiple = B
        while(|difference| differs Q)
            if b_multiple < a_multiple
                b_multiple = b_multiplier + 1
                b_multiple = b_multiplier * B
            else
                a_multiple = a_multiplier + 1
                a_multiple = a_multiplier * A
        
            difference = a_multiple - b_multiple
        
        if(difference < 0)
            return B
        else
            return A
        
      3. 开始填充过程:

        • 用泵填充服务壶(如果空的话)

        • 使用服务一装满另一个水壶

        • 检查另一个水壶是否装满,以防万一,将其倒空

        • 当大壶里有Q时停止

      您可以在下面找到该算法在 C++ 中的一个非常简单的实现。随意重复使用它,或根据需要对其进行改进。

      #include <cstdio>
      #include <cstdlib>
      #include <cstring>
      
      unsigned int mcd(unsigned int a, unsigned int b) {
          // using the Euclide's algorithm to find MCD(a,b)
          unsigned int a_n = a;
          unsigned int b_n = b;
          while(b_n != 0) {
              unsigned int a_n1 = b_n;
              b_n = a_n % b_n; 
              a_n = a_n1;
          }
          return a_n;
      }
      
      unsigned int max(unsigned int a, unsigned int b) {
          return a < b ? b : a;
      }
      
      unsigned int min(unsigned int a, unsigned int b) {
          return a > b ? b : a;
      }
      
      void getServiceJugIndex(unsigned int capacities[2], unsigned int targetQty, unsigned int &index) {
          unsigned int biggerIndex = capacities[0] < capacities[1] ? 1 : 0;
          unsigned int smallerIndex = 1 - biggerIndex;
          if(targetQty % capacities[smallerIndex] == 0) {
              // targetQty is a multiple of the smaller jug, so it's convenient to use this one
              // as 'service' jug
              index = smallerIndex;
              return;
          }
      
          unsigned int multiples[2] = {capacities[0], capacities[1]};
          unsigned int multipliers[2] = {1, 1};
          int currentDifference = capacities[0] - capacities[1];
          while(abs(currentDifference) != targetQty) {
              if(multiples[smallerIndex] < multiples[biggerIndex])
                  multiples[smallerIndex] = capacities[smallerIndex] * ++multipliers[smallerIndex];
              else
                  multiples[biggerIndex] = capacities[biggerIndex] * ++multipliers[biggerIndex];
      
              currentDifference = multiples[biggerIndex] - multiples[smallerIndex];
          }
      
          index = currentDifference < 0 ? smallerIndex : biggerIndex;
      }
      
      void print_step(const char *message, unsigned int capacities[2], unsigned int fillings[2]) {
          printf("%s\n\n", message);
          for(unsigned int i = max(capacities[0], capacities[1]); i > 0; i--) {
              if(i <= capacities[0]) {
                  char filling[9];
                  if(i <= fillings[0])
                      strcpy(filling, "|=====| ");
                  else
                      strcpy(filling, "|     | ");
                  printf("%s", filling);
              } else {
                  printf("        ");
              }
              if(i <= capacities[1]) {
                  char filling[8];
                  if(i <= fillings[1])
                      strcpy(filling, "|=====|");
                  else
                      strcpy(filling, "|     |");
                  printf("%s", filling);
              } else {
                  printf("       ");
              }
              printf("\n");
          }
          printf("------- -------\n\n");
      }
      
      void twoJugsResolutor(unsigned int capacities[2], unsigned int targetQty) {
          if(capacities[0] == 0 && capacities[1] == 0) {
              printf("ERROR: Both jugs have 0 l capacity.\n");
              return;
          }
          // 1. check feasibility
          //  1.1. calculate MCD and verify targetQty is reachable
          unsigned int mcd = ::mcd(capacities[0], capacities[1]);
          if ( targetQty % mcd != 0 ||
          //  1.2. verify that targetQty is not more than max capacity of the biggest jug
                  targetQty > max(capacities[0], capacities[1])) {
              printf("The target quantity is not reachable with the available jugs\n");
              return;
          }
          // 2. choose 'service' jug
          unsigned int serviceJugIndex;
          getServiceJugIndex(capacities, targetQty, serviceJugIndex);
          unsigned int otherJugIndex = 1 - serviceJugIndex;
          unsigned int finalJugIndex = capacities[0] > capacities[1] ? 0 : 1;
          // 3. start fill process
          unsigned int currentFilling[2] = {0, 0};
          while(currentFilling[finalJugIndex] != targetQty) {
              // 3.1 fill with the pump the service jug (if needed)
              if(currentFilling[serviceJugIndex] == 0) {
                  currentFilling[serviceJugIndex] = capacities[serviceJugIndex];
                  print_step("Filling with the pump the service jug", capacities, currentFilling);
              }
      
              // 3.2 fill the other jug using the service one
              unsigned int thisTimeFill = min(currentFilling[serviceJugIndex], capacities[otherJugIndex] - currentFilling[otherJugIndex]);
              currentFilling[otherJugIndex] += thisTimeFill;
              currentFilling[serviceJugIndex] -= thisTimeFill;
              print_step("Filling the other jug using the service one", capacities, currentFilling);
              // 3.3 check fullness of the other jug and, in case, empty it
              if(currentFilling[otherJugIndex] == capacities[otherJugIndex]) {
                  currentFilling[otherJugIndex] = 0;
                  print_step("Empty the full jug", capacities, currentFilling);
              }
          }
          printf("Done\n");
      }
      
      int main (int argc, char** argv) {
          if(argc < 4)
              return -1;
          unsigned int jugs[] = {atoi(argv[1]), atoi(argv[2])};
          unsigned int qty = atoi(argv[3]);
      
          twoJugsResolutor(jugs, qty);
      }
      

      我不知道我所描述的过程背后是否有任何数学概念来选择正确的水壶以最大限度地减少所需步骤的数量,我将其用作启发式。

      希望对你有帮助。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-12-31
        • 1970-01-01
        • 2022-10-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多