Is there general method to solve for a single unknown if the unknown variable changes?
Asked Answered
G

4

7

I have a simple algebraic relationship that uses three variables. I can guarantee that I know two of the three and need to solve for the third, but I don't necessarily know which two of the variables I will know. I'm looking for a single method or algorithm that can handle any of the cases without a huge batch of conditionals. This may not be possible, but I would like to implement it in a more general sense rather than code in every relationship in terms of the other variables. For example, if this were the relationship:

3x - 5y + z = 5

I don't want to code this:

function(int x, int y)
{
  return 5 - 3x + 5y;
}

function(int x, int z)
{
  return (5 - z - 3x)/(-5);
}

And so on. Is there a standard sort of way to handle programming problems like this? Maybe using matrices, parameterization, etc?

Gewgaw answered 24/12, 2014 at 19:19 Comment(0)
C
6

If you restrict yourself to the kind of linear functions shown above, you could generalize the function like this

3x - 5y + z = 5

would become

a[0]*x[0] + a[1]*x[1] + a[2]*x[2] = c

with a = { 3, -5, 1 } and c = 5.

I.e., you need a list (or array) of constant factors List<double> a; and a list of variables List<double?> x; plus the constant on the right side double c;

public double Solve(IList<double> a, IList<double?> x, double c)
{
    int unknowns = 0;
    int unkonwnIndex = 0; // Initialization required because the compiler is not smart
                          // enough to infer that unknownIndex will be initialized when
                          // our code reaches the return statement.
    double sum = 0.0;

    if (a.Count != x.Count) {
       throw new ArgumentException("a[] and x[] must have same length");
    }
    for (int i = 0; i < a.Count; i++) {
        if (x[i].HasValue) {
           sum += a[i] * x[i].Value;
        } else {
           unknowns++;
           unknownIndex = i;
        }
    }
    if (unknowns != 1) {
       throw new ArgumentException("Exactly one unknown expected");
    }
    return (c - sum) / a[unknownIndex];
}

Example:

3x - 5y + z = 5

    5 - (- 5y + z)
x = --------------
          3

As seen in the example the solution consists of subtracting the sum of all terms except the unknown term from the constant and then to divide by the factor of the unknown. Therefore my solution memorizes the index of the unknown.


You can generalize with powers like this, assuming that you have the equation

a[0]*x[0]^p[0] + a[1]*x[1]^p[1] + a[2]*x[2]^p[2] = c

you need an additional parameter IList<int> p and the result becomes

return Math.Pow((c - sum) / a[unknownIndex], 1.0 / p[unknownIndex]);

as x ^ (1/n) is equal to nth-root(x).


If you use doubles for the powers, you will even be able to represent functions like

         5
7*x^3 + --- + 4*sqrt(z) = 11
        y^2

a = { 7, 5, 4 },  p = { 3, -2, 0.5 },  c = 11

because

          1
x^(-n) = ---
         x^n

and

nth-root(x) = x^(1/n)

However, you will not be able to find the roots of true non-linear polynomials like x^2 - 5x = 7. The algorithm shown above, works only, if the unknown appears exactly once in the equation.

Corbeil answered 24/12, 2014 at 20:13 Comment(5)
+1 for correctly generalizing my idea. Didn't think to use a list as the argumentsLeitmotif
Whether you are using lists or arrays depends on the flexibility required. As both implement IList<T> I changed the parameters to IList<T>Corbeil
I suppose this is just a basic linear equation root finder, but it is generalizable to higher dimensions as necessary and doesn't require any conditional branching based on previous knowledge of which was the unknown. Think I'm going to implement this one, thanks!Gewgaw
I think the last if and the following return statement should not be inside the for loop.Fledgling
@NikontheThird: You are right of course. No idea how they got there.Corbeil
L
2

There is no standard way of solving such a problem.

In the general case, symbolic math is a problem solved by purpose built libraries, Math.NET has a symbolic library you might be interested in: http://symbolics.mathdotnet.com/

Ironically, a much tougher problem, a system of linear equations, can be easily solved by a computer by calculating an inverse matrix. You can set up the provided equation in this manner, but there are no built-in general purpose Matrix classes in .NET.

In your specific case, you could use something like this:

public int SolveForVar(int? x, int? y, int? z)
{
    int unknownCount = 0;
    int currentSum = 0;

    if (x.HasValue)
       currentSum += 3 * x.Value;
    else
       unknownCount++;

    if (y.HasValue)
       currentSum += -5 * y.Value;
    else
       unknownCount++;

    if (z.HasValue)
       currentSum += z.Value;
    else
       unknownCount++;

    if (unknownCount > 1)
       throw new ArgumentException("Too Many Unknowns");

    return 5 - currentSum;
}

int correctY = SolveForVar(10, null, 3);

Obviously that approach gets unwieldy for large variable counts, and doesn't work if you need lots of dynamic numbers or complex operations, but it could be generalized to a certain extent.

Leitmotif answered 24/12, 2014 at 19:37 Comment(0)
S
2

Yes, here is one function:

private double? ValueSolved (int? x, int? y, int? z)
    {
      if (y.HasValue && z.HasValue && !x.HasValue
         return (5 + (5 * y.Value) - z.Value) / 3;

      if (x.HasValue && z.HasValue && !y.HasValue
        return (5 - z.Value - (3 * x.Value)) / -5;

      if (x.HasValue && y.HasValue && !z.HasValue
        return 5 - (3 * x.Value) + (5 * y.Value);

      return null;
     }
Saltant answered 24/12, 2014 at 19:42 Comment(2)
That is the obvious approach. I think this question calls for something more general.Kerchief
Yeah, I can brute force it as you have above, but I thought there might be a more general (algorithmic or analytic) way of solving such a problem.Gewgaw
P
1

I'm not sure what you are looking for, since the question is tagged but the sample code you have is producing numerical solutions, not symbolic ones.

If you want to find a numerical solution for a more general case, then define a function

f(x, y, z) = 3x - 5y + z - 5

and feed it to a general root-finding algorithm to find the value of the unknown parameter(s) that will produce a root. Most root-finding implementations allow you to lock particular function parameters to fixed values before searching for a root along the unlocked dimensions of the problem.

Pleura answered 24/12, 2014 at 19:32 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.