简体   繁体   中英

Finding solutions to a single linear equation in n variables, or determine that no solution exists

Suppose you have a linear equation in n variables. The goal is to either determine that no integer solution is possible, or determine the smallest coefficient vector, for an integer solution .

In other words, let ax=b where x is the vector you want to find, and a is a vector of coefficients. b is a scalar constant. Find x such that the sum of x1, ... ,xn is minimized, and all xi s are integers. Or, determine that no such x exists. From now on, I will say that |x| is the sum of the xi 's.

What is an efficient way to solve this? I feel like this is similar to the Knapsack problem, but I'm not entirely sure.

My Solution

The way I tried to solve this was doing a Breadth-First Search on the space of vectors, where the breadth would be the sum of the vector entries.

At first I did this naively, starting from |x| = 0 |x| = 0 , but when n is even moderately large, and the solution is non-trivial, the number of vectors generated is enormous ( n ^ |x| for each |x| you go through). Even worse, I was generating many duplicates. Even when I found a way to generate almost no duplicates, this way is too slow.

Next, I tried starting from a higher |x| from the beginning, by putting a lower bound on the optimal |x| . I sorted a to have it in decreasing order, then removed all ai > b . Then a lower bound on |x| is b / a[0] . However, from this point, I had difficulty quickly generating all the vectors of size |x| . From here, my code is mostly hacky.

In the code, b = distance , x = clubs , n = numClubs

Here is what it looks like:

short getNumStrokes (unsigned short distance, unsigned short numClubs, vector<unsigned short> clubs) {
    if (distance == 0)
        return 0;

    numClubs = pruneClubs(distance, &clubs, numClubs);
    //printClubs (clubs, numClubs);

    valarray<unsigned short> a(numClubs), b(numClubs);
    queue<valarray<unsigned short> > Q; 

    unsigned short floor = distance / clubs[0];

    if (numClubs > 1) {
        for (int i = 0; i < numClubs; i++) {
            a[i] = floor / numClubs;
        }

        Q.push (a);
    }

    // starter vectors
    for (int i = 0; i < numClubs; i++) {
        for (int j = 0; j < numClubs; j++) {
            if (i == j)
                a[j] = distance / clubs[0];
            else
                a[j] = 0;
        }

        if (dot_product (a, clubs) == distance)
            return count_strokes(a);

        // add N starter values
        Q.push (a);
    }

    bool sawZero = false;

    while (! Q.empty ()) {
        a = Q.front(); // take first element from Q
        Q.pop(); // apparently need to do this in 2 operations >_<

        sawZero = false;

        for (unsigned int i = 0; i < numClubs; i++) {
            // only add numbers past right-most non-zero digit
            //if (sawZero || (a[i] != 0 && (i + 1 == numClubs || a[i + 1] == 0))) {
            //    sawZero = true;

                b = a; // deep copy
                b[i] += 1;

                if (dot_product (b, clubs) == distance) {
                    return count_strokes(b);
                } else if (dot_product (b, clubs) < distance) {
                    //printValArray (b, clubs, numClubs);
                    Q.push (b);
                }
            //}
        }
    }

    return -1;
}

EDIT: I'm using valarray because my compiler isn't C++ 11 compliant, so I can't use array. Other code suggestions much appreciated.

Your problem is an equality constrained integer knapsack problem:

min |x|
s.t. ax = b
     x integer

If you have access, CPLEX or GUROBI can generally solve such problems quite easily.

Otherwise, consider some reductions of the constraint set

(eg, http://www.optimization-online.org/DB_FILE/2002/11/561.ps )

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM