解决水壶问题

时间:2009-03-13 18:21:48

标签: math computer-science theory

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

使用两个数字的GCD的属性,GCD(a,b)是a和b的最小可能线性组合,因此一定量Q仅可由2个jug测量,iff Q是* GCD (a,b),因为Q = sA + tB,其中:

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

然后讨论了解决方案的方法

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

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

5 个答案:

答案 0 :(得分:7)

严格针对2 Jug问题

Q = A * x + B * y

Q =你需要的加仑。

注意: Q必须是Gcd(A,B)的倍数,否则没有解决方案。如果Gcd(A,B)== 1,则有任何Q的解决方案。

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

2)方法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更快

Generic N Jugs 这是一个有趣的paper

答案 1 :(得分:4)

一个惊人而有趣的方法(适用于3个水壶)是通过 barycentric coordinates (真的!),如总是精彩的网站Cut-the-Knot所述:Barycentric coordinates: A Curious Application

答案 2 :(得分:1)

此类问题通常适用于dynamic programming技术。我已经看到这个特定的问题在运筹学课程中被用作一个例子。一个很好的分步描述是here

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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

这是:

  1. 检查问题的可行性:
    • Q是MCD(A,B)的倍数;
    • Q是&lt; = max(A,B)。
  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

    • 时停止
  4. 下面你会发现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);
    }
    

    我不知道在我描述的过程背后是否有任何数学概念来选择合适的水壶来减少所需步骤的数量,我将其用作启发式。

    我希望这可以帮到你。