Как реализовать матрицу решений в c#

мне нужно принять решение, основанное на довольно большом наборе из 8 взаимозависимых условий.

           | 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

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

Итак, с заданным входом

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

он должен оценивать Решение02.

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

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

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

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

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

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

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"

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


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

массива:

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;

вот как я бы это сделал, с моей любовью к 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 :))


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

из этого вы можете создать класс matrix, который обертывает список решений, а затем использует 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 создается байт, соответствующий этим полям.


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

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

вот веб-сайт, который идет через двоичный файл [http://electronicsclub.info/counting.htm] спасибо google.

например 10110110 в двоичном коде равно 182 в десятичном: Цифровое значение: 128 64 32 16 8 4 2 1
Двоичное число: 1 0 1 1 0 1 1 0
Десятичное значение: 128 + 0 + 32 + 16 + 0 + 4 + 2 + 0 = 182