如何在c#中实现决策矩阵
Posted
技术标签:
【中文标题】如何在c#中实现决策矩阵【英文标题】:How to implement decision matrix in c# 【发布时间】:2013-05-29 20:27:33 【问题描述】:我需要根据一组相当大的 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 或其他任何方式中制作决策表。我只需要一些关于如何实现这一点的指示。
实现此逻辑的最佳做法是什么?字典?多维数组?完全不同的东西?
【问题讨论】:
Nullable boolean 是我要开始的地方... bool?可以是真假或空 @Sayse By - 他的意思是它不重要,例如它可以是 1 或 0。 【参考方案1】:你可以用 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 还可以为您提供更多的灵活性,以防您获得第四种状态。
【讨论】:
您将string
和bool
都命名为“结果”
谢谢,我已经解决了。它应该是 Func我会使用bool?
的二维数组(在我们的例子中是Dictionary<TKey, TValue>
) - 注意 ?对于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;
【讨论】:
你是对的,这只是一个可以做的例子,而不是逐字记录的例子。我会将相同的代码更新为 OR 好的,现在你明白了,在第一个为真时,在循环的其余部分保持真,你要么是你赢得的——默认为假,一个真为真——要么不是。所以在第一个 true 上,只返回 true,而不是继续。 是的,这只是理论上可以做的一个例子。我不知道,也不想知道他的具体逻辑制作。我只是展示如何通过索引器语法使用嵌套字典。【参考方案3】:我就是这样做的,因为我对 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
:) )
【讨论】:
【参考方案4】:您可以用几行代码完成并创建一个二进制计算器。因此,在下面的示例中,结果 = 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
【讨论】:
【参考方案5】:您可以有一个用两个字节字段表示的决策类。第一个字节将指定哪些条件是真或假。第二个字节将指定哪些条件是相关的。此外,您可以定义一个函数来确定输入字节是否与对象匹配。
由此,您可以创建一个包含决策列表的矩阵类,然后使用 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 类也可能会有所帮助。当您使用 A-H 字段实例化 Input 对象时,会创建一个字节来匹配这些字段。
【讨论】:
【参考方案6】:您可以将决策矩阵实现为字典,如下所示,并在矩阵上查询以找到匹配项。我使用 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);
【讨论】:
以上是关于如何在c#中实现决策矩阵的主要内容,如果未能解决你的问题,请参考以下文章