2014-12-24 10 views
7

Ho una semplice relazione algebrica che utilizza tre variabili. Posso garantire che conosco due dei tre e ho bisogno di risolvere per il terzo, ma non necessariamente so quali delle due variabili saprò. Sto cercando un singolo metodo o algoritmo in grado di gestire uno dei casi senza un enorme lotto di condizionali. Questo potrebbe non essere possibile, ma vorrei implementarlo in un senso più generale piuttosto che in ogni relazione in termini di altre variabili. Ad esempio, se questo fosse il rapporto:Esiste un metodo generale per risolvere un singolo sconosciuto se la variabile sconosciuta cambia?

3x - 5y + z = 5 

Non voglio codificare questo:

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

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

E così via. Esiste un modo standard per gestire problemi di programmazione come questo? Forse usando matrici, parametrizzazione, ecc.?

risposta

6

Se si limita a voi stessi di quel tipo di funzioni lineari mostrate sopra, si potrebbe generalizzare la funzione come questa

3x - 5y + z = 5 

sarebbe diventato

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

con a = { 3, -5, 1 } e c = 5.

Vale a dire, hai bisogno di una lista (o array) di fattori costanti List<double> a; e un elenco di variabili List<double?> x; più il costante sul lato destro 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]; 
} 

Esempio:

3x - 5y + z = 5 

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

come si è visto nell'esempio la soluzione consiste nel sottrarre la somma di tutti i termini ad eccezione del termine sconosciuto dalla costante e quindi di dividere per il fattore dell'ignoto. Pertanto la mia soluzione memorizza l'indice dell'ignoto.


Si può generalizzare con poteri come questo, a patto che abbiate l'equazione

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

è necessario un ulteriore parametro IList<int> p e il risultato diventa

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

come x^(1/n) è pari a nth-root(x).


Se si utilizza raddoppia per i poteri, si sarà anche in grado di rappresentare le funzioni come

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

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

perché

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

e

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

Tuttavia, è non sarà in grado di trovare le radici di tr ue polinomi non lineari come x^2 - 5x = 7. L'algoritmo mostrato sopra, funziona solo se lo sconosciuto appare esattamente una volta nell'equazione.

+1

'+ 1' per generalizzare correttamente la mia idea. Non ho pensato di utilizzare un elenco come argomento – BradleyDotNET

+0

Se si utilizzano elenchi o array dipende dalla flessibilità richiesta. Dato che entrambi implementano 'IList ' Ho cambiato i parametri in 'IList' –

+0

'Suppongo che questo sia solo un semplice finder di base per le equazioni lineari, ma è generalizzabile a dimensioni superiori se necessario e non richiede alcuna ramificazione condizionale basata sulla conoscenza precedente di cui era l'ignoto.Penso che implementerò questo, grazie! – topher

1

Non sono sicuro di cosa stiate cercando, poiché la domanda è codificata ma il codice di esempio che avete sta producendo soluzioni numeriche, non simboliche.

Se si vuole trovare una soluzione numerica per un caso più generale, quindi definire una funzione

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

e dei mangimi ad un generale root-finding algorithm per trovare il valore del parametro sconosciuto (s) che produrrà una radice. La maggior parte delle implementazioni di ricerca delle radici consentono di bloccare determinati parametri di funzione su valori fissi prima di cercare una radice lungo le dimensioni sbloccate del problema.

2

Non esiste uno standard come risolvere questo problema.

Nel caso generale, matematica simbolica è un problema risolto da biblioteche appositamente costruito, Math.NET ha una libreria simbolica si potrebbe essere interessati a: http://symbolics.mathdotnet.com/

Ironia della sorte, un problema molto più dura, un sistema di di equazioni lineari, possono possono essere facilmente risolti da un computer calcolando una matrice inversa. È possibile impostare l'equazione fornita in questo modo, ma in .NET non sono presenti classi Matrix generiche incorporate.

Nella tua specifica caso, si potrebbe usare qualcosa di simile:

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

Ovviamente questo approccio diventa ingombrante per i grandi conteggi variabili, e non funziona se avete bisogno di un sacco di numeri dinamici o complessa operazioni, ma potrebbe essere generalizzato in una certa misura.

2

Sì, qui è una funzione:

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

Questo è l'approccio ovvio. Penso che questa domanda richieda qualcosa di più generale. – usr

+1

Sì, posso forzarlo come hai sopra, ma ho pensato che ci potrebbe essere un modo più generale (algoritmico o analitico) per risolvere un problema del genere. – topher

Problemi correlati