我有一些非常古老的遗留程序代码,它需要10个左右的枚举输入[i0,i1,i2,... i9]并生成170个奇数枚举的输出[r0,r1,... r168,r169]。通过列举,我的意思是每个单独的输入和输出都有其自己的一组不同的值集,例如[红色,绿色,黄色]或[是,否]等。
我要使用现有的代码将整个状态表放在一起,而不是想手动摸索它们,我想知道是否存在一种算法方法来确定合适的函数来从10个输入中获取每个结果。请注意,并非所有输入列都可用于确定单个输出列,即r124可能仅取决于i5,i6和i9。
这些不是连续的函数,我希望我最终会得到某种哈希函数方法,但是我想知道是否有人知道我应该使用更可重复的过程呢? (如果只有一些类似于Karnaugh映射的方法可用于多值非二进制函数;-))
答案 0 :(得分:3)
如果您愿意实际枚举所有可能的输入/输出序列,这是一种解决该问题的理论方法,应该相当有效。
首先,考虑输出的entropy。假设您有n
个可能的输入序列,并且x[i]
是获取i
作为输出的方法的数量。令p[i] = float(x[i])/float(n[i])
,则熵为- sum(p[i] * log(p[i]) for i in outputs)
。 (请注意,由于p[i] < 1
log(p[i])
是负数,因此熵是正的。还要注意,如果p[i] = 0
则我们假设p[i] * log(p[i])
也是零。)
可以将熵的量视为预测结果所需的信息量。
现在这是关键问题。相对于有关输入的信息,哪个变量能为我们提供有关输出的最多信息?
如果特定变量v
具有in[v]
可能的值,则指定v
的信息量为log(float(in[v]))
。我已经描述了如何计算整个输出集的熵。对于v
的每个可能值,我们可以为v
的值计算整个输出集的熵。通过了解v
得出的信息量是总集的熵减去v
各个值的熵的平均值。
选择变量v
,该变量将为您带来information_gained_from_v/information_to_specify_v
的最佳比率。您的算法将从切换该变量的值集开始。
然后为每个值重复执行此过程,以在条件发生时级联嵌套。
如果条件集中在输入变量上,这些条件将导致相当紧凑的级联嵌套,那么输入变量将尽可能快地告诉您,并尽可能减少分支的数量。
现在,假设您已经进行了全面的枚举。但是,如果不这样做怎么办?
答案是,我所描述的分析可以对可能的一组输入样本进行随机抽样。因此,如果您使用例如10,000个随机输入来运行代码,那么您将在第一个级别上得到相当不错的熵。在第二层上重复每个分支10,000个,同样如此。只要在计算上可行,就继续。
如果有好的模式可以找到,那么您会很快找到很多形式的模式,“如果您输入this和other,这就是您总是得到的输出。”如果有相当短的嵌套ifs集合可以提供正确的输出,则可能会找到它。之后,您需要确定是手动验证每个存储桶是否可靠,还是要相信,如果您无法通过10,000个随机输入找到任何异常,那么就找不到问题了。
棘手的验证方法。如果可以找到为您的语言编写的模糊测试软件,请运行该模糊测试软件,其目的是尝试找出找到的每个存储桶的所有可能的内部执行路径。如果模糊测试软件认为您无法通过上述方法获得与您认为最佳的答案不同的答案,那么您可能会相信它。
答案 1 :(得分:0)
算法非常简单。给定每个输入的可能值,我们可以生成所有可能的输入向量。然后,对于每个输出,我们可以消除与输入无关的这些输入。结果,对于每个输出,我们可以得到一个矩阵,该矩阵显示所有输入组合的输出值,但不包括与给定输出无关的输入。
示例输入格式(以下代码片段):
var schema = new ConvertionSchema()
{
InputPossibleValues = new object[][]
{
new object[] { 1, 2, 3, }, // input #0
new object[] { 'a', 'b', 'c' }, // input #1
new object[] { "foo", "bar" }, // input #2
},
Converters = new System.Func<object[], object>[]
{
input => input[0], // output #0
input => (int)input[0] + (int)(char)input[1], // output #1
input => (string)input[2] == "foo" ? 1 : 42, // output #2
input => input[2].ToString() + input[1].ToString(), // output #3
input => (int)input[0] % 2, // output #4
}
};
示例输出:
下面留下了向后转换的核心。此处以Linqpad代码段的形式提供了完整的代码:http://share.linqpad.net/cknrte.linq。
public void Reverse(ConvertionSchema schema)
{
// generate all possible input vectors and record the resul for each case
// then for each output we could figure out which inputs matters
object[][] inputs = schema.GenerateInputVectors();
// reversal path
for (int outputIdx = 0; outputIdx < schema.OutputsCount; outputIdx++)
{
List<int> inputsThatDoNotMatter = new List<int>();
for (int inputIdx = 0; inputIdx < schema.InputsCount; inputIdx++)
{
// find all groups for input vectors where all other inputs (excluding current) are the same
// if across these groups outputs are exactly the same, then it means that current input
// does not matter for given output
bool inputMatters = inputs.GroupBy(input => ExcudeByIndexes(input, new[] { inputIdx }), input => schema.Convert(input)[outputIdx], ObjectsByValuesComparer.Instance)
.Where(x => x.Distinct().Count() > 1)
.Any();
if (!inputMatters)
{
inputsThatDoNotMatter.Add(inputIdx);
Util.Metatext($"Input #{inputIdx} does not matter for output #{outputIdx}").Dump();
}
}
// mapping table (only inputs that matters)
var mapping = new List<dynamic>();
foreach (var inputGroup in inputs.GroupBy(input => ExcudeByIndexes(input, inputsThatDoNotMatter), ObjectsByValuesComparer.Instance))
{
dynamic record = new ExpandoObject();
object[] sampleInput = inputGroup.First();
object output = schema.Convert(sampleInput)[outputIdx];
for (int inputIdx = 0; inputIdx < schema.InputsCount; inputIdx++)
{
if (inputsThatDoNotMatter.Contains(inputIdx))
continue;
AddProperty(record, $"Input #{inputIdx}", sampleInput[inputIdx]);
}
AddProperty(record, $"Output #{outputIdx}", output);
mapping.Add(record);
}
// input x, ..., input y, output z form is needed
mapping.Dump();
}
}