В рамках личного проекта по вычислению жордановской нормальной формы квадратных матриц оказалось, что мне нужно разбирать многочлены с комплексными коэффициентами, чтобы упростить большую часть кода.
(соответствующий код внизу сообщения)
Полиномы, которые я хочу разобрать, имеют следующий вид:
- Коэффициент может быть действительным, мнимым или комплексным.
- Если коэффициент сложный, он будет заключен в круглые скобки. Если эти скобки являются ведущим коэффициентом, им не будет предшествовать
+
или-
. - Если коэффициент действительный, мнимый или комплексный, действительные и/или мнимые компоненты которого имеют величину 1,
1
не появится, а только знак. - Перед скобками может стоять только
+
. - Переменная
x
может иметь степень (>2
), может иметь степень 1, и тогда она появляется так же, какx
, или может вообще не появляться. - Больше нет правил относительно текстового представления многочлена, т.е. степени не обязательно упорядочены по возрастанию\убыванию.
Некоторые примеры правильно отформатированных полиномов:
1
-1
-2.1x
3i
x^2-1
-x^3+2x+1
(5-5i)x^2-x-1
(-1+i)x-5
-ix^3-x^2+1
..и некоторые плохо отформатированные:
1x
(ведущий ненужный1
)+(+1-2i)x
(скобки имеют ведущий+
, реальный компонент имеет ведущий+
)(5.1i)x^2
(круглые скобки не нужны, так как коэффициент мнимый)-(i-1)
(в начале комплексного коэффициента стоит-
)
После некоторого чтения в Интернете (SO, учебные пособия по Java, Java API) я быстро пришел к выводу, что регулярное выражение было бы самым простым подходом для синтаксического анализа, учитывая все ограничения, упомянутые выше. С формальной стороны возможно регулярное выражение для этой задачи, поскольку я нарисовал NFA, который принимает только такие допустимые выражения.
Я делаю этот TDD (через JUnit 4), и этот тест не проходит:
assertEquals("Polynomial parsed incorrectly.", poly07, PolyParser.parse(exp07));
где poly07
выглядит так: (5-5i)x^2-x-1
.
Это исключение, которое возникает:
java.lang.NumberFormatException: For input string: "5-5"
at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:2043)
at sun.misc.FloatingDecimal.parseDouble(FloatingDecimal.java:110)
at java.lang.Double.parseDouble(Double.java:538)
at PolyParser.parse(PolyParser.java:55)
at PolyParserTest.testParse(PolyParserTest.java:59)
Я пытался выполнить отладку и увидел, что регулярное выражение захватывает 5-5i
(а позже удаляет i
). Затем он пытается вызвать Double.parseDouble
со строкой аргумента 5-5
, что вызывает исключение.
После всего прочтения я не могу понять, что нужно изменить в регулярном выражении, чтобы все это шоу работало. Кроме того, регулярное выражение не упорядочено, как упомянутые выше ограничения представлений, потому что я хочу увидеть, является ли коэффициент сложным, прежде чем пытаться проанализировать его как реальный; также столкнулись с проблемами, когда действительные числа (т.е. с десятичной точкой) анализируются как целые числа, поэтому регулярное выражение сначала обрабатывает действительные числа.
Регулярное выражение:
public static final String POLYNOMIAL_REGEX =
"([+-])?" + // leading plus or minus
"(\\()?" + // parenthesis to denote the beginning of a complex number
"([+-])?(((\\d+.\\d+)|\\d+)i)?" + // component of coefficient, imaginary
"(((-)?\\d+.\\d+)|\\d+)?" + // component of coefficient, real
"(\\))?" + // parenthesis to denote the end of a complex number
"(x)?" + // variable
"(?:\\^(\\d+))?"; // power of the variable
Я не собираюсь публиковать здесь весь соответствующий код, потому что это загромождает вещи. Весь код находится на GitHub, только не забудьте переключиться на ветку PolyParser
.
Соответствующий код находится в файлах:
PolyParser.java
Polynomial.java
Complex.java
Тестовый модуль находится в файле PolyParserTest.java
.