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.