2013-06-03 2 views
16

J'ai besoin de prendre une décision basée sur un ensemble assez grand de 8 conditions co-dépendantes.Comment implémenter la matrice de décision dans C#

  | A | B | C | D | E | F | G | H 
-----------+---+---+---+---+---+---+---+--- 
Decision01 | 0 | 1 | - | 1 | 0 | 1 | - | 1 
Decision02 | 1 | 0 | - | 0 | 0 | - | 1 | - 
    ... 
Decision11 | 1 | 0 | 1 | 1 | 1 | - | 1 | 1 

Chacune des conditions de A à H peuvent être vraies (1), false (0) ou non pertinent (-) pour la décision.

Donc, avec une entrée donnée de

A B C D E F G H 
1 0 1 0 0 1 1 1 

il doit évaluer à Decision02. Les décisions ne sont pas ambiguës et, à partir d'un ensemble donné de conditions d'entrée, il est clair que la décision doit être prise (et dans le cas où la matrice de décision ne couvre pas, une exception doit être levée).

Le développeur qui a travaillé avant moi sur ce projet a essayé d'implémenter ceci comme un béhémoth imbriqué de 500 lignes, qui bien sûr est bogué et ne peut pas être maintenu.

J'ai donc cherché la meilleure façon d'implémenter une telle logique et j'ai trouvé des tables de décision/tables de recherche/tables de contrôle.

que j'ai trouvé beaucoup de générateurs de table de décision, mais pas un seul morceau de code sur la façon de mettre en œuvre le processus de décision :(

Je peux faire la table de décision dans la base de données MSSQL sous-jacente, ou code, ou xml, ou tout ce qu'il faut. Je viens besoin de quelques conseils sur la façon de mettre en œuvre du tout.

Quelle est la meilleure pratique pour mettre en œuvre cette logique? Dictionnaire? ensemble Multidimensional? quelque chose de complètement différent

+0

Nullable booléen est où je commencerais ... bool? Peut être vrai faux ou nul – Sayse

+0

@Sayse By - il signifie que ce n'est pas important, par exemple il peut être 1 ou 0. –

Répondre

6

Vous pouvez le faire avec des tableaux de Func.

static Func<bool,bool> isTrue = delegate(bool b) { return b; }; 
static Func<bool,bool> isFalse = delegate(bool b) { return !b; }; 
static Func<bool,bool> isIrrelevant = delegate(bool b) { return true; }; 

Maintenant, vous pouvez mettre votre matrice dans un dictionnaire comme celui-ci:

Dictionary<string,Func<bool,bool>[]> decisionMatrix = new Dictionary<string,Func<bool,bool>[]>(); 
// 0 | 1 | - | 1 | 0 | 1 | - | 1 
matrix.Add("Decision01", new Func<bool,bool>{isFalse, isTrue, isIrrelevant, isTrue, isFalse, isTrue, isIrrelevant, isTrue}); 

Enfin pour chaque tableau d'entrée donné:

bool[] input = new bool[]{ false, true, false, true, false, true, false, true} 

string matchingRule = null; 
foreach(var pair in matrix) { 
    bool result = true; 
    for(int i = 0; i < input.Length; i++) { 
     // walk over the function array and call each function with the input value 
     result &= pair.Value[i](input[i]); 
    } 

    if (result) { // all functions returned true 
     // we got a winner 
     matchingRule = pair.Key; 
     break; 
    } 
} 

// matchingRule should now be "Decision01" 

Cela devrait probablement obtenir des contrôles supplémentaires (par exemple la vérification que le tableau d'entrée a la taille correcte) mais devrait vous donner une idée. L'utilisation de Funcs vous donne également plus de flexibilité dans le cas où vous obtenez un quatrième état.

+1

Vous avez nommé à la fois votre 'chaîne' et votre résultat' bool' " – jszigeti

+1

Merci, j'ai corrigé cela.Et il devrait être Func au lieu de Func , fixé cela aussi bien. –

+0

Cela fonctionne comme un charme! –

2

? I utiliser un tableau 2D (Dictionary<TKey, TValue> dans notre cas) de bool? - notez le ? pour Nullable<bool> qui permet 3 états: vrai, faux et nul. Votre nul pourrait représenter "aucun effet" ...

tableau défini:

var myArray = new Dictionary<char, Dictionary<int, bool?>>(); 

Ensuite, vous pouvez faire des choses comme:

bool result = false; 
foreach (var inputPair in input) 
{ 
    // Assuming inputPair is KeyValuePair<char, int> 
    result |= myArray[inputPair.Key][inputPair.Value]; 
} 

return result; 
+1

Démarrer le bool avec false, et faire '&' avec le faux vous obtenez toujours faux ... – Aristos

+0

Vous ' à droite, c'était simplement un exemple de ce qui pouvait être fait, pas de quoi faire verbatim. Je vais mettre à jour le code même à un OU à la place – Haney

+1

Ok, maintenant vous comprenez que le premier vrai, reste vrai pour le reste de la boucle, et vous êtes soit ce que vous avez gagné -default false, un vrai tout vrai- soit ne pas. Donc, le premier vrai, juste retourner vrai, ne pas continuer. – Aristos

0

Vous pouvez le faire en quelques lignes et créer une calculatrice binaire. Donc, dans un exemple ci-dessous, les résultats = 182 que la décision D (ou ce que tous les). Le ci-dessous, s'aligne avec vos décisions et résultats seront tous des totaux différents.

Voici un site Web qui va au-delà du binaire [http://electronicsclub.info/counting.htm] grâce à google.

Par exemple 10110110 en binaire est égal à 182 en décimal: valeur des chiffres: 128 64 32 16 8 4 2 1
nombre binaire: 1 0 1 1 0 1 1 0
valeur décimale: 128 + 0 + 32 + 16 + 0 + 4 + 2 + 0 = 182

1

Vous pouvez avoir une classe de décision représentée avec des champs de deux octets. Le premier octet désignera quelles conditions sont vraies ou fausses. Le deuxième octet désignera les conditions pertinentes. De plus, vous pouvez définir une fonction qui détermine si un octet d'entrée correspond à un objet. À partir de là, vous pouvez créer une classe de matrice qui enveloppe une liste de décisions, puis utilise LINQ pour rechercher dans la liste une décision correspondant à votre entrée.

Vous pouvez vous faire classe telle décision

class Decision 
{ 
    byte Conditions; 
    byte RelevantConditions; 

    bool IsMatch(byte input) 
    { 
     byte unmatchedBits = input^Conditions; //matching conditions are set to 0 
     unmatchedBits &= RelevantConditions; //Irrelevant conditions set to 0 
     return (unmatchedBits == 0); //if any bit is 1, then the input does not match the relevant conditions 
    } 
} 

Ainsi, l'objet pour Decision01 peut être défini comme

Decision decision01 = new Decision() 
{ 
    Conditions   = 0x55; //01010101 in binary 
    RelevantConditions = 0xdd; //11011101 in binary 
} 

Ensuite, votre classe décision Matrix peut être comme celui-ci

class DecisionMatrix 
{ 
    List<Decision> decisions; 

    Decision Find(byte input) 
    { 
     return decisions.Find(d => d.IsMatch(input)); 
    } 
} 

Il peut également être utile de créer une classe Input qui enveloppe un octet. Lorsque vous instanciez un objet Entrée avec les champs A-H, un octet est créé pour correspondre à ces champs.

2

Voici comment je le ferais, avec mon amour de LINQ.

Tout d'abord, vos matrices sont un IEnumerable<IEnumerable<bool?>> et true signifie 1, false, 0 et null indéterminée.

Ensuite, vous passez un IEnumerable<bool> que vous souhaitez vérifier. Voici la fonction:

public IEnumerable<bool?> DecisionMatrix(this IEnumerable<bool> source, IEnumerable<IEnumerable<bool?>> options) 
{ 
    IList<bool> sourceList = source.ToList(); 
    return options.Where(n => n.Count() == sourceList.Count) 
     .Select(n => n.Select((x, i) => new {Value = x, Index = i})) 
     .Where(x => 
      x.All(n => !(sourceList[n.Index]^n.Value ?? sourceList[n.Index]))) 
     .FirstOrDefault(); 
} 

(Il est une méthode d'extension, le mettre dans un static class :))

1

Vous pouvez mettre en œuvre la matrice de décision comme un dictionnaire comme indiqué ci-dessous et requête sur la matrice pour trouver une correspondance . J'ai utilisé string.join pour convertir le tableau en une chaîne. Ont également utilisé le '-' dans la matrice comme une regex [0 | 1].

Dictionary<string, char[]> myMatrix = new Dictionary<string, char[]>(); 
myMatrix.Add("Decision01", new char[] { '0', '1', '-', '1', '0', '1', '-', '1' }); 
myMatrix.Add("Decision02", new char[] { '1', '0', '-', '0', '0', '-', '1', '-' }); 
myMatrix.Add("Decision03", new char[] { '1', '1', '1', '0', '0', '1', '1', '1' }); 

char[] input = new char[] { '1', '0', '1', '0', '0', '1', '1', '1' }; 
var decision = (from match in myMatrix 
      where Regex.IsMatch(string.Join(string.Empty, input), 
       string.Join(string.Empty, match.Value).ToString().Replace("-", "[0|1]"), 
       RegexOptions.IgnoreCase) 
      select match.Key).FirstOrDefault(); 

Console.WriteLine(decision); 
Questions connexes