Автоматическая генерация вариантов контрольных работ и ответов к ним в Mathematica
Общее количество использованных в посте встроенных функций или символов: 84
Список имен используемых встроенных функций и символов в порядке их появления в коде:
RandomReal | List ({...}) | RandomInteger | RandomChoice | Power (^) | Sin | Cos | BesselJ | E (e) | Times (*, ×) | Thread | Rule (->, ->) | RandomSample | SetDelayed (:=) | Block | CompoundExpression (;) | Set (=) | While | Apply (@@) | SameQ (===) | Pattern (:) | Blank (_) | Dot (.) | Cross (×) | VectorAngle | Out (%) | Or (||, ∨) | Less (<) | Length | DeleteDuplicates | Map (/@) | Function (&) | Equal (==) | DeleteCases | Slot (#) | Transpose | Reduce | TraditionalForm | RawBoxes | RowBox | Piecewise | GridBox | ToBoxes | ReplaceAll (/.) | Subscript | Abs | Grid | Det | Join | Range | Plot | Pi (π) | D (∂) | Sort | Greater (>) | DiagonalMatrix | MatrixForm | Inverse | Part ([[…]]) | Cases | Integer | Infinity | Total | Flatten | Not (!, ¬) | FreeQ | Eigensystem | Table | Unequal (!=, ≠) | Span (;;) | All | Frame | ItemSize | Alignment | Center | ItemStyle | Bold | Column | Dividers | False | Left | Export | StringJoin (<>) | NotebookDirectory
Список имен используемых встроенных функций и символов в порядке их появления в коде:
RandomReal | List ({...}) | RandomInteger | RandomChoice | Power (^) | Sin | Cos | BesselJ | E (e) | Times (*, ×) | Thread | Rule (->, ->) | RandomSample | SetDelayed (:=) | Block | CompoundExpression (;) | Set (=) | While | Apply (@@) | SameQ (===) | Pattern (:) | Blank (_) | Dot (.) | Cross (×) | VectorAngle | Out (%) | Or (||, ∨) | Less (<) | Length | DeleteDuplicates | Map (/@) | Function (&) | Equal (==) | DeleteCases | Slot (#) | Transpose | Reduce | TraditionalForm | RawBoxes | RowBox | Piecewise | GridBox | ToBoxes | ReplaceAll (/.) | Subscript | Abs | Grid | Det | Join | Range | Plot | Pi (π) | D (∂) | Sort | Greater (>) | DiagonalMatrix | MatrixForm | Inverse | Part ([[…]]) | Cases | Integer | Infinity | Total | Flatten | Not (!, ¬) | FreeQ | Eigensystem | Table | Unequal (!=, ≠) | Span (;;) | All | Frame | ItemSize | Alignment | Center | ItemStyle | Bold | Column | Dividers | False | Left | Export | StringJoin (<>) | NotebookDirectory
Любой учитель в школе или преподаватель в ВУЗе сталкивается с проблемой проверки знаний учащихся по своему предмету. Стандартной проверкой знаний учащегося обычно служит некоторая контрольная работа или тест. Если вы работаете в старших классах школы или в вузе, то создание большого количества схожих вариантов одной контрольной работы потребует огромного количества времени, особенно если вы хотите снабдить каждого учащегося уникальным вариантом. Скажем, в обычной группе студентов ВУЗа около 25 человек, даже если один вариант контрольной работы будет содержать всего 4 задания, то потребуется создать уже 100 задач.
К тому же нужно помнить о том, что мы живем в век цифровых технологий... а значит вся информация, в том числе и варианты контрольных работ, быстро распространятся в интернете и следующие группы студентов (классы школьников) будут уже знать заранее все, что будет в контрольной, если, особенно, существует, скажем, всего 4 варианта некоторой контрольной работы, которые даются из года в год учащимся.
С помощью Mathematica вы можете решить описанные проблемы, генерируя качественные задания вместе с ответами к ним в нужном количестве. При этом вы будете уверены в том, что все задачи будут корректны, а ответы будут абсолютно точно верны.
В этом посте я продемонстрирую то, как можно создать произвольное количество вариантов некоторой контрольной работы, вместе с ответами к ней. Надеюсь, что пост окажется полезен для вас.
Генерация вариантов контрольной работы по линейной алгебре
Создадим вариант контрольной работы по линейной алгебре, который будет содержать 4 задачи:
1) для двух трехмерных векторов вычислить их скалярное и векторное произведения, а также найти угол между ними;
2) методом Гаусса решить систему 3-х линейных уравнений с 4 неизвестными;
3) вычислить определитель 3-го порядка;
4) найти собственные числа и векторы матрицы 3x3.
Построение алгоритмов для генерирования условий и ответов
Задача 1. Для двух векторов вычислить их скалярное и векторное произведения, а также найти угол между ними
Для генерации схожих вариантов заданий очень удобно использовать функции, которые дают последовательность произвольной длины псевдослучайных чисел, которые распределены равномерно.
Среди этих функций стоит отметить:
Приведу несколько примеров работы с данными функциями.
Генерация матрицы 3x4 из случайных десятичных дробей:
In[1]:=
RandomReal[{-100, 1000}, {3, 4}]
Out[1]=
Генерация последовательности из 10 точек с целочисленными координатами:
In[2]:=
RandomInteger[{12, 100}, {10, 2}]
Out[2]=
Случайная последовательность из 20 функций:
In[3]:=
RandomChoice[{x, x^2, Sin[x], Cos[x], BesselJ[0, x], E^x, 1/x}, 20]
Out[3]=
Случайные правила замены, описывающие распределение работ между людьми:
In[4]:=
Thread[RandomSample[{"Иванов", "Петров", "Сидоров", "Манухина", "Смирнова", "Пуненкова"}, 6] ->RandomSample[{"Помыть пол", "Стереть с доски", "Подмести пол", "Полить цветы", "Вынести мусор", "Сварить борщ"}, 6]]
Out[4]=
Теперь можно приступить к генерации условия для задачи 1.
Нам нужны два трехмерных вектора. При этом, так как данное задание в целом простое с вычислительной точки зрения, то можно просто сгенерировать два вектора, координаты которых были бы, скажем, целыми числами из диапазона [-5, 5]:
In[5]:=
task[1] := RandomInteger[{-5, 5}, {2, 3}]
In[6]:=
task[1]
Out[6]=
Так как генерация происходит случайно, то может случиться так, что векторы получатся одинаковыми, чтобы избежать этого немного модифицируем функцию task[1]:
In[7]:=
task[1] := Block[{vectors, vectorsGenerator}, vectorsGenerator := RandomInteger[{-5, 5}, {2, 3}] ; vectors = vectorsGenerator ; While[SameQ @@ vectors, vectors = vectorsGenerator] ; vectors]
In[8]:=
task[1]
Out[8]=
Здесь были использованы функции Apply в виде @@, которая изменяет головную часть выражения:
А также функция обычного условного цикла While и функция SameQ, которая проверяет идентичность двух выражений, обычно употребляется в виде ==, т. е. SameQ[x, y] тоже что и x==y.
Теперь создадим функцию, которая будет генерировать ответ к заданию. Для этого нам потребуются функции: Dot (часто употребляется в короткой форме a.b) для вычисления скалярного произведения векторов, Cross для вычисления векторного произведения и VectorAngle для вычисления наименьшего угла между векторами.
In[9]:=
answer[1][task_] := {Dot @@ task, Cross @@ task, VectorAngle @@ task}
Что ж, попробуем сгенерировать задание и получить ответ:
In[10]:=
task[1]
Out[10]=
In[11]:=
answer[1][%]
Out[11]=
Задача 2. Методом Гаусса решить систему 3-х линейных уравнений с 4 неизвестными.
С помощью функции RandomInteger сгенерируем основную матрицу системы и вектор свободных членов. При этом пусть коэффициенты при неизвестных будут целыми числами в интервале [-3, 3], а свободные члены — [-5, 5]:
In[12]:=
A := RandomInteger[{-3, 3}, {3, 4}]
In[13]:=
B := RandomInteger[{-5, 5}, 3]
In[14]:=
{A, B}
Out[14]=
Ввиду случайной генерации основной матрицы системы, возможна ситуация, когда некоторая строка (или строки) или некоторый столбец (столбцы) будут состоять только из нулей или в матрице будет несколько одинаковых строк. Также нас не устроит, если все элементы столбца свободных членов будут нулями. Такие ситуации нас не устраивают, поэтому исключим такую возможность подобно тому, как мы делали это выше:
In[15]:=
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]
In[16]:=
B := Block[{B, BGenerator}, BGenerator := RandomInteger[{-5, 5}, 3] ; B = BGenerator ; While[DeleteCases[B, 0] == {}, B = BGenerator] ; B]
In[17]:=
{A, B}
Out[17]=
Здесь были использованы функции:
Теперь построим, собственно, систему линейных уравнений:
In[18]:=
system := Thread[A . {x[1], x[2], x[3], x[4]} == B]
In[19]:=
system
Out[19]=
Здесь была использована функция Thread, которая позволяет “распространить” функцию над всеми списками, которые входят в ее тело в качестве аргументов, пример:
In[20]:=
{Thread[f[{a, b}]], Thread[f[{a, b}, {1, 2}]]}
Out[20]=
Теперь попробуем найти решение системы, это можно сделать, например, с помощью функции Reduce:
In[21]:=
system
Out[21]=
In[22]:=
Reduce[%, {x[1], x[2], x[3], x[4]}]
Out[22]=
Создадим функцию, которая будет выдавать задание в привычном виде:
In[23]:=
task[2][system_] := TraditionalForm[RawBoxes[RowBox[{"", GridBox[Transpose[{ToBoxes/@(system/.x[i_] ->x_i)}]]}]]]
In[24]:=
task[2][system]
Out[24]//TraditionalForm=
Во введенной функции были использованы следующие встроенные выражения: функция TraditionalForm, позволяющая представить выражение в привычной математической нотации и функции RawBoxes, RowBox, GridBox, ToBoxes с помощью которых осуществляется низкоуровневое форматирование выражений. Вообще говоря, в конечном счете (на самом низком уровне), любая конструкция в Mathematica является набором некоторых блоков (boxes), которые представляются функциями которые содержат всегда в своем названии слово Box.
Что ж, теперь сделаем последний шаг — создадим функцию, которая будет выдавать ответ в привычной форме:
In[25]:=
answer[2][system_] := TraditionalForm[Reduce[system, {x[1], x[2], x[3], x[4]}]/.x[i_] ->x_i]
Попробуем сгенерировать задание и получить ответ:
In[26]:=
system
Out[26]=
In[27]:=
task[2][%]
Out[27]//TraditionalForm=
In[28]:=
answer[2][%%]
Out[28]//TraditionalForm=
Задача 3. Вычислить определитель 3-го порядка.
С помощью функции RandomInteger сгенерируем определитель третьего порядка с элементами, которые являются положительными целыми числами в интервале [1, 10]:
In[29]:=
det := RandomInteger[{1, 10}, {3, 3}]
In[30]:=
det
Out[30]=
Наложим на генерируемую матрицу схожие ограничения, что мы вводили выше для основной матрицы системы, добавим еще одно условие — чтобы не было одинаковых столбцов (условия относительно 0 нам не нужны, так как числа берутся в интервале [1, 10]):
In[31]:=
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]
In[32]:=
det
Out[32]=
Создадим функцию, которая будет выдавать задание в привычном виде (функция Grid позволяет сформировать таблицу):
In[33]:=
task[3][det_] := TraditionalForm[Abs[Grid[det]]]
In[34]:=
task[3][det]
Out[34]//TraditionalForm=
Наконец, сделаем функцию, которая будет вычислять ответ (функция Det позволяет вычислить определить произвольной квадратной матрицы, как символьно, так и численно) и провем работоспособность наших функций на конкретном примере:
In[35]:=
answer[3][det_] := Det[det]
In[36]:=
det
Out[36]=
In[37]:=
task[3][%]
Out[37]//TraditionalForm=
In[38]:=
answer[3][%%]
Out[38]=
Задача 4. Найти собственные числа и векторы матрицы 3x3.
Из линейной алгебры известно, что матрицы A и BAB-1 имеют одинаковые собственные числа и собственные векторы. Также известно, что если матрица A диагональная, то на ее диагонали стоят ее собственные числа. Используем эти факты для удобной и простой генерации матриц 3x3, которые будут иметь заданные “красивые” целые собственные числа в интервале [-5, 5] без нуля. Воспользуемся функцией RandomSample для генерации списка из трех собственных, различных между собой, чисел (функция Range[a, b, c] генерирует список чисел от a до b с шагом c):
In[39]:=
eigenvalues := RandomSample[Range[-5, -1] ~ Join ~ Range[1, 5], 3]
In[40]:=
eigenvalues
Out[40]=
Здесь использована инфиксная форма работы с функцией Join, позволяющей объединять элементы двух и более списков в один. Вообще, инфиксная форма весьма удобна, она работает так:
x ~ f ~ y == f[x, y]
Вот несколько интересных примеров:
In[41]:=
Sin[x] ~ Plot ~ {x, 0, 2Pi}
Out[41]=
In[42]:=
(Cos[x ^y]) ~ D ~ {{x, y}}
Out[42]=
In[43]:=
Range[20] ~ Sort ~ (Cos[#1] >Sin[#2] &)
Out[43]=
Теперь, построим матрицу, на диагонали которой будут стоять числа из данного списка, все элементы которой нули, это можно осуществить с помощью функции DiagonalMatrix:
In[44]:=
matrix := DiagonalMatrix[eigenvalues]
In[45]:=
matrix
Out[45]=
In[46]:=
MatrixForm[%]
Out[46]//MatrixForm=
Для создания условия задания нам понадобится невырожденная матрица, размера 3x3 и обратная к ней, которую можно найти с помощью функции Inverse. Создадим функцию, которая бы выдавала список из матрицы B и обратной к ней:
In[47]:=
invertibleMatrix := Block[{invertibleMatrix, invertibleMatrixGenerator}, invertibleMatrixGenerator := RandomInteger[{-5, 5}, {3, 3}] ; invertibleMatrix = invertibleMatrixGenerator ; While[Det[invertibleMatrix] == 0, invertibleMatrix = invertibleMatrixGenerator] ; {invertibleMatrix, Inverse[invertibleMatrix]}]
In[48]:=
invertibleMatrix
Out[48]=
Теперь мы имеем список из матрицы B и B-1) .
Попробуем найти матрицу BAB-1), которая будут иметь те же собственные числа и векторы, что и матрица A:
In[49]:=
matrix
Out[49]=
In[50]:=
invertibleMatrix
Out[50]=
In[51]:=
#1 . %% . #2& @@ %
Out[51]=
Ясно, что матрица BAB-1 может быть в общем случае весьма “некрасивой” и неудобной в работе. Сделаем так, чтобы на выходе мы получали матрицу только из целых чисел, причем сумма их модулей не превышала бы 9*20=180 (т. е. чтобы каждое число было по модулю не более 20) и среди них не было бы 0:
In[52]:=
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]
In[53]:=
taskMatrix
Out[53]=
Здесь были использованы следующие функции:
Теперь создадим функцию, которая будет выдавать задание в привычном виде:
In[54]:=
task[4][matrix_] := TraditionalForm[MatrixForm[matrix]]
In[55]:=
task[4][taskMatrix]
Out[55]//TraditionalForm=
Наконец, сделаем функцию, которая будет вычислять ответ (функция Eigensystem выдает список собственных чисел и векторов):
In[56]:=
answer[4][matrix_] := Transpose[Eigensystem[matrix]]
In[57]:=
taskMatrix
Out[57]=
In[58]:=
task[4][%]
Out[58]//TraditionalForm=
In[59]:=
answer[4][%%]
Out[59]=
Построение одного варианта контрольной работы
Используя созданные функции мы можем теперь создать один вариант контрольной работы:
In[60]:=
variant := {{#, answer[1][#]} &[task[1]], {task[2][#], answer[2][#]} &[system], {task[3][#], answer[3][#]} &[det], {task[4][#], answer[4][#]} &[taskMatrix]}
In[61]:=
variant
Out[61]=
Генерация произвольного числа вариантов контрольной работы
Теперь создадим столько разных вариантов, сколько нам нужно, например 5:
In[62]:=
variants[n_] := Block[{variants, variantsGenerator}, variantsGenerator := Table[variant, {n}] ; variants = variantsGenerator ; While[Total[Length[DeleteDuplicates[#]] &/@Transpose[variants[[;;, ;;, 1]]]] != 4n, variants = variantsGenerator] ; variants]
In[63]:=
variants[5]
Out[63]=
Формирование готового списка задач для учащихся и ответов для преподавателя
Каждый вариант состоит из списков по два элемента — условия и ответа.
Разобьем список вариантов на два списка, условий и ответов:
In[64]:=
controlWorkTasksAndAnswers[n_] := Block[{Variants}, Variants = Transpose[variants[n]] ; {Transpose[Variants[[;;, ;;, 1]]], Transpose[Variants[[;;, ;;, 2]]]}]
In[65]:=
controlWorkTasksAndAnswers[5]
Out[65]=
Теперь представим варианты и ответы к ним в красивом, отформатированном виде.
Для начала сгенерируем некоторое количество вариантов, например 15:
In[66]:=
controlWork = controlWorkTasksAndAnswers[15] ;
Теперь подготовим варианты и ответы к ним:
In[67]:=
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}]
Out[67]=
In[68]:=
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}]
Out[68]=
После этого вам остается только экспортировать полученные варианты и ответы к ним, например, в TIFF и распечатать. Это можно сделать с помощью функции Export (функция NotebookDirectory дает адрес директории в которой сохранен текущий документ Mathematica, так что соответствующие файлы будут созданы там же):
In[69]:=
Export[NotebookDirectory[] <>"linear_algebra_kw_tasks.tiff", table["task"]]
Out[69]=
In[70]:=
Export[NotebookDirectory[] <>"linear_algebra_kw_answers.tiff", table["answer"]]
Out[70]=
После генерации, в данном случае картинок, остается их распечатать и разрезать на варианты. Теперь вы можете генерировать произвольное число качественных вариантов вместе с ответами к ним, при этом как все задачи, так и все ответы будут корректны.
Блог принадлежит “Русскоязычной поддержке Wolfram Mathematica"©
При любом использовании материалов блога, ссылка на блог обязательна.
Создано с помощью Wolfram Mathematica 8
При любом использовании материалов блога, ссылка на блог обязательна.
Создано с помощью Wolfram Mathematica 8
Спасибо. Очень подробно, доходчиво и наверняка будет кому-то полезно на практике.
ОтветитьУдалить