如果未知变量发生变化,是否有一般方法来解决单个未知变量的问题?

7

我有一个简单的代数关系,涉及三个变量。我可以保证我知道其中两个变量,并需要解决第三个变量,但我不一定知道哪两个变量。我正在寻找一种单一的方法或算法,可以处理任何情况,而不需要大量的条件语句。这可能是不可能的,但我想以更普遍的方式实现它,而不是根据其他变量编写每个关系的代码。 例如,如果这是关系:

3x - 5y + z = 5

我不想编写这个代码:

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

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

等等。有没有一种标准的方式来处理这样的编程问题?也许可以使用矩阵、参数化等方法吗?

4个回答

6
如果你限制自己只使用上面展示的线性函数,你可以像这样推广该函数。
3x - 5y + z = 5

将会变成

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

使用 a = { 3, -5, 1 }c = 5

即,您需要一个常数因子列表(或数组)List<double> a; 和一个变量列表 List<double?> x;,以及右侧的常数 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];
}

例子:

3x - 5y + z = 5

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

从这个例子中可以看出,解决方案包括从常数中减去除未知项以外的所有项的总和,然后除以未知项的系数。因此,我的解决方案记住了未知项的索引。


您可以像这样使用幂进行泛化,假设您有方程

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

你需要添加一个额外的参数 IList<int> p,结果会变成

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

当使用 x ^ (1/n) 时,它等同于 nth-root(x)


如果您对指数使用 doubles,则甚至可以表示像

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

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

因为

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

并且

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

然而,您将无法找到真正的非线性多项式的根,例如x^2 - 5x = 7。上面展示的算法仅在未知数在方程中出现一次时才有效。


1
如果您能正确地概括我的想法,我会给您一个“+1”。我没有想到可以使用列表作为参数。 - BradleyDotNET
使用列表或数组取决于所需的灵活性。由于两者都实现了 IList<T>,因此我将参数更改为 IList<T> - Olivier Jacot-Descombes
我猜这只是一个基本的线性方程根查找器,但它可以推广到更高维度,如有需要,并且不需要根据先前了解的未知数进行任何条件分支。我打算实现这个,谢谢! - topher
我认为最后的 if 和接下来的 return 语句不应该在 for 循环内部。 - Nikon the Third
@NikontheThird:你当然是对的。不知道它们是怎么出现的。 - Olivier Jacot-Descombes

2

解决这种问题没有标准方法。符号数学通常由专用库解决,Math.NET具有一个符号库,可能会对您感兴趣:http://symbolics.mathdotnet.com/

具有讽刺意味的是,一个更棘手的问题,即线性方程组,可以通过计算逆矩阵轻松地被计算机解决。您可以用这种方式设置提供的方程,但在.NET中没有内置的通用矩阵类。

对于您的特定情况,您可以使用类似以下的东西:

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);

显然,这种方法对于大量变量计数变得笨重,并且如果需要许多动态数字或复杂操作,则无法使用,但在一定程度上可以推广。

2

是的,这里有一个函数:

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;
     }

那是显而易见的方法。我认为这个问题需要更一般性的解决方案。 - usr
1
是的,我可以像你上面做的那样使用暴力方法,但我认为可能有一种更通用(算法或分析)的解决这类问题的方法。 - topher

1

我不确定你在寻找什么,因为问题标记为,但你提供的示例代码生成的是数值解而不是符号解。

如果你想要找到一个更一般的情况的数值解,那么需要定义一个函数。

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

将其传递给通用的根查找算法,以找到会产生根的未知参数的值。大多数根查找实现都允许您在沿问题的未锁定维度搜索根之前将特定函数参数锁定为固定值。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接