2013-06-03 1 views
16

Мне нужно принять решение на основе довольно большого набора из 8 зависимых друг от друга условий.Как реализовать матрицу решений в 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 

Каждое из условий от А до Н может быть истинным (1), ложь (0) или нерелевантными (-) для решения.

Так с данным входом

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

должно вычисляться Decision02.

Решения недвусмысленны, поэтому из любого заданного набора условий ввода ясно, какое решение необходимо принять (и в случае, когда эта матрица не охвачена, возникает исключение).

Разработчик, который работал до меня в этом проекте, попытался реализовать это как 500-строчный длинный вложенный, если бегемот, который, конечно, глючит, и не ремонтируется.

Итак, я искал лучший способ реализовать такую ​​логику, и я столкнулся с таблицами решений/таблицами поиска/таблицами управления.

Я нашел много столов решения генераторов, но ни один кусок кода, о том, как реализовать решение процесса :(

сделать я могу сделать таблицу решений в основной базе данных MSSQL, или в код, или XML, или все, что нужно. Мне просто нужно несколько советов о том, как реализовать это на всех.

Что лучшие практики для реализации этой логики? словарь? многомерный массив? Что-то совсем другое?

+0

Nullable boolean - это то, где я бы начал ... bool? Может быть true false или null – Sayse

+0

@Sayse By - он означает, что это не важно, например, оно может быть 1 или 0. –

ответ

6

Вы можете сделать это с массивами 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; }; 

Теперь вы можете поместить вашу матрицу в словарь, как это:

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

Наконец, для каждого заданного входного массива:

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" 

Это, вероятно, получить еще несколько проверок (например, проверка что входной массив имеет правильный размер), но должен дать вам некоторую идею. Использование Funcs также дает вам дополнительную гибкость, если вы получите четвертое состояние.

+1

Вы назвали ваш 'string' и ваш' bool' результат " – jszigeti

+1

Спасибо, я исправил это.И это должен быть Func вместо Func , исправлено это также. –

+0

Это работает как шарм! –

2

I 'd используйте 2D-массив (Dictionary<TKey, TValue> в нашем случае) bool? - обратите внимание на ? для Nullable<bool>, который допускает 3 состояния: true, false и null. Ваш нуль не может представлять "никакого эффекта" ...

Defined массив:

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

Тогда вы могли бы сделать что-то вроде:

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

return result; 
+1

Начните bool с false и сделайте '&' с ложью, вы всегда получаете false ... – Aristos

+0

You ' правильно, это был всего лишь пример того, что можно было сделать, а не ЧТО делать дословно. Я буду обновлять код до OR вместо – Haney

+1

Хорошо, теперь вы понимаете, что в первом истинном, остается верным для остальной части цикла, и вы либо это то, что вы выиграли --default false, один true all true - либо не. Итак, на первом истинном, просто верните истину, а не продолжайте. – Aristos

0

Вы можете сделать это в нескольких строках и создать двоичный калькулятор. Итак, в примере ниже, результаты = 182, чем решение D (или то, что каждый). Ниже приведен список ваших решений и результатов, которые будут разными.

Вот веб-сайт, который проходит через Binary [http://electronicsclub.info/counting.htm] благодаря Google.

Например 10110110 в двоичной системе равно 182 в десятичной системе: значение цифры: 128 64 32 16-4 2 1
Двоичный номер: 1 0 1 1 0 1 1 0
Десятичного значение: 128 + 0 + 32 + 16 + 0 + 4 + 2 + 0 = 182

1

У вас может быть класс принятия решений с двумя байтовыми полями. Первый байт будет определять, какие условия являются истинными или ложными. Второй байт будет определять, какие условия имеют значение. Кроме того, вы можете определить функцию, которая определяет, соответствует ли входной байт объекту.

Из этого вы можете создать класс матрицы, который обертывает список решений, а затем использует LINQ для поиска списка для решения, которое соответствует вашему вводу.

Вы можете иметь вас класс Решение, как этот

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

Таким образом, объект для Decision01 может быть определен как

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

Тогда ваш класс Матрица принятия решений может быть сделано как этот

class DecisionMatrix 
{ 
    List<Decision> decisions; 

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

Он также может помочь создать класс ввода, который обертывает байт. Когда вы создаете экземпляр объекта ввода с полями A-H, создается байт для соответствия этим полям.

2

Вот как бы я это сделал, с моей любовью к LINQ.

Во-первых, ваши матрицы являются IEnumerable<IEnumerable<bool?>> и true означает 1, false, 0 и null неопределенными.

Затем вы передаете IEnumerable<bool>, который вы хотите проверить. Вот функция:

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

(Это метод расширения, поместите его в static class :))

1

Вы можете реализовать матрицу решения в качестве словаря, как показано ниже, и запрос на матрицу, чтобы найти соответствие , Я использовал string.join для преобразования массива в строку. Также использовали '-' в матрице как регулярное выражение [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);