Автоматически генерация вариантов контрольных работ и ответов к ним


Построение алгоритмов для генерирования условий и ответов
Задача 1Для генерации схожих вариантов заданий очень удобно использовать функции, которые дают последовательность произвольной длины псевдослучайных чисел, которые распределены равномерно.
Среди этих функций стоит отметить:
RandomReal[{Subscript[x, min], Subscript[x, max]}, {n, m}] (генерация массива размера nxm псевдослучайных чисел, которые лежат на отрезке [Subscript[x, min], Subscript[x, max]]);
RandomInteger[{Subscript[n, min], Subscript[n, max]}, {n, m}] (генерация массива размера nЧm псевдослучайных целых чисел, которые лежат на отрезке [Subscript[n, min], Subscript[n, max]]);
RandomChoice[list, {n, m}] (генерация массива размера nЧm псевдослучайных чисел (или других объектов), которые являются элементами списка list);
RandomSample[list, n] (генерация списка длины n из неповторяющихся элементов списка list, взятых случайным образом).
Теперь можно приступить к генерации условия для задачи 1. Нам нужны два трехмерных вектора. При этом, так как данное задание в целом простое с вычислительной точки зрения, то можно просто сгенерировать два вектора, координаты которых были бы, скажем, целыми числами из диапазона [-5, 5]:
task[1]:=RandomInteger[{-5,5},{2,3}]Так как генерация происходит случайно, то может случиться так, что векторы получатся одинаковыми, чтобы избежать этого немного модифицируем функцию task[1]:
task[1]:=Block[{vectors,vectorsGenerator},vectorsGenerator:=RandomInteger[{-5,5},{2,3}];vectors=vectorsGenerator;While[SameQ@@vectors,vectors=vectorsGenerator];vectors]Теперь создадим функцию, которая будет генерировать ответ к заданию. Для этого нам потребуются функции: Dot (часто употребляется в короткой форме a.b) для вычисления скалярного произведения векторов, Cross для вычисления векторного произведения и VectorAngle для вычисления наименьшего угла между векторами.
answer[1][task_]:={Dot@@task,Cross@@task,VectorAngle@@task}Сгенерировать задание и получить ответ, используя команды task1 и answer[1][%].
Задача 2С помощью функции RandomInteger сгенерируем основную матрицу системы и вектор свободных членов. При этом пусть коэффициенты при неизвестных будут целыми числами в интервале [-3, 3], а свободные члены — [-5, 5]:
A:=RandomInteger[{-3,3},{3,4}]B:=RandomInteger[{-5,5},3]Ввиду случайной генерации основной матрицы системы, возможна ситуация, когда некоторая строка (или строки) или некоторый столбец (столбцы) будут состоять только из нулей или в матрице будет несколько одинаковых строк. Также нас не устроит, если все элементы столбца свободных членов будут нулями. Такие ситуации нас не устраивают, поэтому исключим такую возможность подобно тому, как мы делали это выше:
A:=Block[{A,AGenerator},AGenerator:=RandomInteger[{-3,3},{3,4}];A=AGenerator;While[Length[DeleteDuplicates[A]]<Length[A]||Or@@((DeleteCases[#,0]=={})&/@A)||Or@@((DeleteCases[#,0]=={})&/@Transpose[A]),A=AGenerator];A]B:=Block[{B,BGenerator},BGenerator:=RandomInteger[{-5,5},3];B=BGenerator;While[DeleteCases[B,0]=={},B=BGenerator];B]Теперь построим, собственно, систему линейных уравнений:
system:=Thread[A.{x[1],x[2],x[3],x[4]}==B]Здесь была использована функция Thread, которая позволяет "распространить" функцию над всеми списками, которые входят в ее тело в качестве аргументов, пример:
{Thread[f[{a,b}]],Thread[f[{a,b},{1,2}]]}Теперь попробуем найти решение системы, это можно сделать, например, с помощью функции Reduce:
systemReduce[%,{x[1],x[2],x[3],x[4]}]Создадим функцию, которая будет выдавать задание в привычном виде:
task[2][system_]:=TraditionalForm[RawBoxes[RowBox[{"",GridBox[Transpose[{ToBoxes/@(system/.x[i_]→xi)}]]}]]]task[2][system]Во введенной функции были использованы следующие встроенные выражения: функция TraditionalForm, позволяющая представить выражение в привычной математической нотации и функции RawBoxes, RowBox, GridBox, ToBoxes с помощью которых осуществляется низкоуровневое форматирование выражений. Вообще говоря, в конечном счете (на самом низком уровне), любая конструкция в Mathematica является набором некоторых блоков (boxes), которые представляются функциями которые содержат всегда в своем названии слово Box.
Создадим функцию, которая будет выдавать ответ в привычной форме:
answer[2][system_]:=TraditionalForm[Reduce[system,{x[1],x[2],x[3],x[4]}]/.x[i_]→xi]Сгенерируем задание и получим ответ:
systemtask[2][%]answer[2][%%] Задача 3С помощью функции RandomInteger сгенерируем определитель третьего порядка с элементами, которые являются положительными целыми числами в интервале [1, 10]:
det:=RandomInteger[{1,10},{3,3}]Наложим на генерируемую матрицу схожие ограничения, что мы вводили выше для основной матрицы системы, добавим еще одно условие — чтобы не было одинаковых столбцов (условия относительно 0 нам не нужны, так как числа берутся в интервале [1, 10]):
det:=Block[{det,detGenerator},detGenerator:=RandomInteger[{1,10},{3,3}];det=detGenerator;While[Length[DeleteDuplicates[A]]<Length[det]||Length[DeleteDuplicates[Transpose[A]]]<Length[Transpose[A]],det=detGenerator];det]Создадим функцию, которая будет выдавать задание в привычном виде (функция Grid позволяет сформировать таблицу):
task[3][det_]:=TraditionalForm[Abs[Grid[det]]]task[3][det]Наконец, сделаем функцию, которая будет вычислять ответ (функция Det позволяет вычислить определить произвольной квадратной матрицы, как символьно, так и численно) и проверим работоспособность наших функций на конкретном примере:
answer[3][det_]:=Det[det]dettask[3][%]answer[3][%%]Задача 4Из линейной алгебры известно, что матрицы A и BAB-1 имеют одинаковые собственные числа и собственные векторы. Также известно, что если матрица A диагональная, то на ее диагонали стоят ее собственные числа. Используем эти факты для удобной и простой генерации матриц 3x3, которые будут иметь заданные "красивые" целые собственные числа в интервале [-5, 5] без нуля. Воспользуемся функцией RandomSample для генерации списка из трех собственных, различных между собой, чисел (функция Range[a, b, c] генерирует список чисел от a до b с шагом c):
eigenvalues:=RandomSample[Range[-5,-1]~Join~Range[1,5],3]eigenvaluesЗдесь использована инфиксная форма работы с функцией Join, позволяющей объединять элементы двух и более списков в один. Вообще, инфиксная форма весьма удобна, она работает так:
x~f~y ~ f[x,y]
Вот несколько примеров:
Sin[x]~Plot~{x,0,2Pi}(Cos[x^y])~D~{{x,y}}Range[20]~Sort~(Cos[#1]>Sin[#2]&)Теперь, построим матрицу, на диагонали которой будут стоять числа из данного списка, все элементы которой нули, это можно осуществить с помощью функции DiagonalMatrix:
matrix:=DiagonalMatrix[eigenvalues]matrixMatrixForm[%]Для создания условия задания нам понадобится невырожденная матрица, размера 3x3 и обратная к ней, которую можно найти с помощью функции Inverse. Создадим функцию, которая бы выдавала список из матрицы B и обратной к ней:
invertibleMatrix≔Block[{invertibleMatrix,invertibleMatrixGenerator},invertibleMatrixGenerator≔RandomInteger-5,5,3,3;invertibleMatrix=invertibleMatrixGenerator;WhileDetinvertibleMatrix==0,invertibleMatrix=invertibleMatrixGenerator;{invertibleMatrix,Inverse[invertibleMatrix]}]invertibleMatrixИмеем список из матрицы B и B-1. Попробуем найти матрицу BAB-1, которая будут иметь те же собственные числа и векторы, что и матрица A:
matrixinvertibleMatrix#1.%%.#2&@@%Ясно, что матрица BAB-1 может быть в общем случае весьма "некрасивой" и неудобной в работе. Сделаем так, чтобы на выходе мы получали матрицу только из целых чисел, примем сумма их модулей не превышала бы 9*20=180 (т. е. чтобы каждое число было по модулю не более 20) и среди них не было бы 0:
taskMatrix:=Block[{evalues,iMatrix,taskMatrix},evalues=matrix;iMatrix=invertibleMatrix;taskMatrix=(iMatrix[[1]].evalues.iMatrix[[2]]);While[Length[Cases[taskMatrix,_Integer,Infinity]]<9||Total[Abs[Flatten[taskMatrix]]]>180||Not[FreeQ[taskMatrix,0]],evalues=matrix;iMatrix=invertibleMatrix;taskMatrix=(iMatrix[[1]].evalues.iMatrix[[2]]);];taskMatrix]taskMatrixТеперь создадим функцию, которая будет выдавать задание в привычном виде:
task[4][matrix_]:=TraditionalForm[MatrixForm[matrix]]task[4][taskMatrix]Сделаем функцию, которая будет вычислять ответ (функция Eigensystem выдает список собственных чисел и векторов):
answer[4][matrix_]:=Transpose[Eigensystem[matrix]]taskMatrixtask[4][%]answer4%% Построение одного варианта контрольной работыИспользуя созданные функции в предыдущем пункте мы можем теперь создать один вариант контрольной работы:
variant≔{{#,answer[1][#]}&[task[1]],{task[2][#],answer[2][#]}&[system],{task[3][#],answer[3][#]}&[det],{task[4][#],answer[4][#]}&[taskMatrix]}Variant Генерация произвольного числа вариантов контрольной работыСоздадим столько разных вариантов, сколько нам нужно, например 5:
variants[n_]:=Block[{variants,variantsGenerator},variantsGenerator:=Table[variant,{n}];variants=variantsGenerator;While[Total[Length[DeleteDuplicates[#]]&/@Transpose[variants[[;;,;;,1]]]]!=4n,variants=variantsGenerator];variants]variants[5] Формирование готового списка задач для учащихся и ответов для преподавателяКаждый вариант состоит из списков по два элемента — условия и ответа. Разобьем список вариантов на два списка, условий и ответов:
controlWorkTasksAndAnswers[n_]≔Block[{Variants},Variants=Transpose[variants[n]];{Transpose[Variants[[;;,;;,1]]],Transpose[Variants[[;;,;;,2]]]}]controlWorkTasksAndAnswers[5]Представим варианты и ответы к ним в красивом, отформатированном виде. Для начала сгенерируем некоторое количество вариантов, например 15:
controlWork=controlWorkTasksAndAnswers[15];Подготовим варианты и ответы к ним:
table["task"]=Grid[Table[{i}~Join~controlWork[[1,i]],{i,1,Length[controlWork[[1]]]}],Frame→All,ItemSize→{{1,6,12,5,6}},Alignment→{Center,Center},ItemStyle→{Bold,14}]table["answer"]=Grid[Table[{i,Column[controlWork[[2,i]],Dividers→Center]},{i,1,Length[controlWork[[2]]]}],Frame→{False,{{1}}},ItemSize→{{1,25},8},Alignment→{Left,Center},ItemStyle→{Bold,14}]Можно экспортировать полученные варианты и ответы к ним, например, в TIFF и распечатать. Это можно сделать с помощью функции Export (функция NotebookDirectory дает адрес директории в которой сохранен текущий документ Mathematica, так что соответствующие файлы будут созданы там же):
Export[NotebookDirectory[]<>"linear_algebra_kw_tasks.tiff",table["task"]]Export[NotebookDirectory[]<>"linear_algebra_kw_answers.tiff",table["answer"]]После генерации, в данном случае картинок, остается их распечатать и разрезать на варианты. Возможно, генерировать произвольное число качественных вариантов вместе с ответами к ним, при этом как все задачи, так и все ответы будут корректны.

Рис. 1. Экспорт результатов в формат tiff

Приложенные файлы

  • docx Generator
    Размер файла: 317 kB Загрузок: 3