Если вы действительно хотите перечислить все возможные последовательности ввода / вывода, вот теоретический подход к решению этой проблемы, который должен быть довольно эффективным.
Сначала рассмотрим энтропию выходных данных. Предположим, что у вас есть 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 каждой из ваших веток на втором уровне, и то же самое произойдет. Продолжайте, пока это возможно в вычислительном отношении.
Если есть хорошие шаблоны для поиска, вы быстро найдете множество шаблонов в форме: «Если вы добавите то и другое, вот результат, который вы всегда получаете». Если есть достаточно короткий набор вложенных if, которые дают правильный вывод, вы, вероятно, найдете его. После этого у вас возникает вопрос о том, нужно ли на самом деле вручную проверять надежность каждого сегмента или полагать, что если вы не смогли найти каких-либо исключений с 10 000 случайных входов, то их не найти.
Хитрый подход к валидации. Если вы можете найти программное обеспечение для фаззинга, написанное для вашего языка, запустите программное обеспечение для фаззинга, чтобы попытаться выявить все возможные внутренние пути выполнения для каждого сегмента, который вы найдете. Если программное обеспечение fuzzing решит, что вы не можете получить ответы, отличные от того, который, по вашему мнению, лучше всего подходит для вышеуказанного подхода, то вы, вероятно, можете ему доверять.