I want to improve the performance of my equation solver
So I have an expression:- 42a + 75b - 30c + 80d + 25e + 50f
, let's call it D
.
Variables a, b, c, d, e, f
are positive integers with values from 0 to 150.
I need to find and filter solutions to D = -(v + 1)
equation for each v
, where v
is an integer in the range from 0 to 3000. Solutions are filtered in a way where a variable is either 0 or at least 1. In other words, there are a total of 64 permutations for each v
. I'm searching for the solution with the lowest a + b + c + d + e + f
sum. Some permutations might not include any solutions, in which case it just returns Impossible
.
This is more or less a typical Diophantine equation but I couldn't find a solver that would handle it. hackmath.net has a solver, but it doesn't take in more than 4 variables with constraints and also doesn't seem to have any API so that didn't help.
That's why I decided to make my own solver. Since I'm limiting a, b, c, d, e, f
values making a brute-force algorithm didn't seem like such a bad idea, so I made one.
using System.Data;
const int c_maxVal = 150;
// valueSet[,] answerArray = new valueSet[3000 , 64];
// Lower size for testing
valueSet[,] answerArray = new valueSet[1 , 64];
for (int variation = 0; variation < answerArray.GetLength(0); variation++)
{
// FINDING SOLUTIONS
List<valueSet> results = new List<valueSet>();
Parallel.For(0, c_maxVal, a =>
{
for(int b = 0; b <= c_maxVal; b++)
{
for (int c = 0; c <= c_maxVal; c++)
{
for (int d = 0; d <= c_maxVal; d++)
{
for (int e = 0; e <= c_maxVal; e++)
{
for (int f = 0; f <= c_maxVal; f++)
{
if (variation - 42 * a + 75 * b - 30 * c + 80 * d + 25 * e + 50 * f == -1)
{
lock (results)
{
results.Add(new() { _a = a, _b = b, _c = c, _d = d, _e = e, _f = f });
}
}
}
}
}
}
}
});
// FILTERING VALUES
for (int s = 0; s < answerArray.GetLength(1); s++)
{
BitArray bArr = new BitArray(new int[] { s });
bool[] bits = new bool[bArr.Length];
bArr.CopyTo(bits, 0);
int[] aRange = new int[] { bits[0] ? 1 : 0, bits[0] ? c_maxVal : 0 };
int[] bRange = new int[] { bits[1] ? 1 : 0, bits[1] ? c_maxVal : 0 };
int[] cRange = new int[] { bits[2] ? 1 : 0, bits[2] ? c_maxVal : 0 };
int[] dRange = new int[] { bits[3] ? 1 : 0, bits[3] ? c_maxVal : 0 };
int[] eRange = new int[] { bits[4] ? 1 : 0, bits[4] ? c_maxVal : 0 };
int[] fRange = new int[] { bits[5] ? 1 : 0, bits[5] ? c_maxVal : 0 };
List<valueSet> finalList = results.Where(set =>
set._a >= aRange[0] && set._a <= aRange[1] &&
set._b >= bRange[0] && set._b <= bRange[1] &&
set._c >= cRange[0] && set._c <= cRange[1] &&
set._d >= dRange[0] && set._d <= dRange[1] &&
set._e >= eRange[0] && set._e <= eRange[1] &&
set._f >= fRange[0] && set._f <= fRange[1]
).ToList();
valueSet finalSet = finalList.Find(set => set.count == finalList.Min(set => set.count));
answerArray[variation, s] = finalSet;
}
}
// Console output for testing
Console.WriteLine();
for (int s = 0; s < answerArray.GetLength(1); s++)
{
Console.WriteLine($"Permutation #{s+1}");
Console.WriteLine(answerArray[0, s]);
}
struct valueSet
{
public int _a;
public int _b;
public int _c;
public int _d;
public int _e;
public int _f;
public int count { get { return _a + _b + _c + _d + _e + _f; } }
public override string ToString() => count > 0 ? $"a=[{_a}] b=[{_b}] c=[{_c}] d=[{_d}] e=[{_e}] f=[{_f}]" : "Impossible";
}
The main solution finder is what Parallel.For
loop is for and I have no complaints about that (my CPU spikes up to 90% load when the loop is running, but otherwise it does it's job and it does it fast).
Filtering through all the solutions to find each permutation with the lowest variable sum is what's significantly slowing the program. My approach is to use a for
loop and to convert it's iterator to a bit array on each step and then use each bit as a binary check for each variable. If the bit is set, then the range is from 1 to 100. If the bit is not set, then the range is from 0 to 0. My PC isn't that old, but it still takes a couple minutes to go through that search when testing just 1 variation
(v
from the prior explanation).
What I thought of doing:
- Removing certain unnecessary permutations.
Some of the permutations will never have a solution. For example if only
b
is used, then the expressionD
can only have positive answers even though it's supposed to stay negative.
Is there any way to significantly improve the performance of my filtering algorithm?
A couple things to note before answering:
- I know that
b, e, f
variables can substitute each other as they're all multiples of 25, but I still need them to be separate variables. - I plan on storing
answerArray
in a CSV file later and use it as a lookup table.
a
throughf
expected to stay the same whilev
varies, or do you expect a potentially differenta
throughf
whenv
varies? \$\endgroup\$v
I check each possible value ofa
throughf
and out of all the combinations I record those that are a solution to the initial equation.v
in this case is thevariation
variable and is a part of the top-mostfor
loop inside of which every other loop is located \$\endgroup\$