17

我需要根据一组相当大的 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

它应该评估为Decision02。

决策是明确的,因此从任何给定的输入条件集中,必须做出哪个决策是很清楚的(并且在决策矩阵未涵盖的情况下,应抛出异常)。

在我之前从事这个项目的开发人员试图将其实现为一个 500 行长的嵌套 if 庞然大物,这当然是错误的,而且不可维护。

所以我寻找了实现这样一个逻辑的最佳方法,我遇到了决策表/查找表/控制表。

我发现了很多决策表生成器,但没有找到关于如何实现决策过程的代码:(

我可以在底层 MSSQL 数据库、代码、xml 或其他任何方式中制作决策表。我只需要一些关于如何实现这一点的指示。

实现此逻辑的最佳实践是什么?字典?多维数组?完全不同的东西?

4

6 回答 6

8

你可以用 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 还为您提供了更多的灵活性,以防您获得第四种状态。

于 2013-06-03T16:41:51.160 回答
2

我会使用 2D 数组(Dictionary<TKey, TValue>在我们的例子中)bool?- 注意 ? 其中允许Nullable<bool>3 种状态:真、假和空。您的 null 可能代表“无效”...

定义数组:

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;
于 2013-06-03T16:35:18.567 回答
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:) )

于 2013-06-03T17:44:58.023 回答
1

您可以用几行代码完成并创建一个二进制计算器。因此,在下面的示例中,结果 = 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

于 2013-06-03T17:22:01.987 回答
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));
    }
}

制作一个包装字节的 Input 类也可能会有所帮助。当您使用 AH 字段实例化 Input 对象时,会创建一个字节来匹配这些字段。

于 2013-06-03T17:26:12.547 回答
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);
于 2013-06-03T18:14:26.227 回答