Знайдіть справжні корені многочлена


24

Напишіть автономну програму, яка при заданому поліномі та зв’язаному знаходить усі справжні корені цього многочлена до абсолютної помилки, що не перевищує межу.

Обмеження

Я знаю, що Mathematica та, ймовірно, деякі інші мови мають односимвольне рішення, і це нудно, тому вам слід дотримуватися примітивних операцій (додавання, віднімання, множення, ділення).

Існує певна гнучкість у форматах введення та виведення. Ви можете приймати дані через аргументи stdin або командного рядка в будь-якому розумному форматі. Ви можете дозволити плаваючу крапку або вимагати використання певного представлення раціональних чисел. Ви можете приймати зв'язану або зворотну зв'язану, і якщо ви використовуєте плаваючу крапку, ви можете припустити, що пов'язана буде не менше 2 ulp. Поліном повинен бути виражений у вигляді списку коефіцієнтів одночленів, але він може бути великим або малим-ендіаном.

Ви повинні бути в змозі обґрунтувати, чому програма завжди працюватиме (числові питання з модулем), хоча не потрібно надавати повний доказ.

Програма повинна обробляти поліноми з повторними коренями.

Приклад

x^2 - 2 = 0 (error bound 0.01)

Вхід може бути, наприклад

-2 0 1 0.01
100 1 0 -2
1/100 ; x^2-2

Вихід може бути, наприклад,

-1.41 1.42

але не

-1.40 1.40

як це має абсолютні помилки приблизно 0,014 ...

Тестові справи

Простий:

x^2 - 2 = 0 (error bound 0.01)

x^4 + 0.81 x^2 - 0.47 x + 0.06 (error bound 10^-6)

Кілька коренів:

x^4 - 8 x^3 + 18 x^2 - 27 (error bound 10^-6)

Поліном Вілкінсона:

x^20 - 210 x^19 + 20615 x^18 - 1256850 x^17 + 53327946 x^16 -1672280820 x^15 +
    40171771630 x^14 - 756111184500 x^13 + 11310276995381 x^12 - 135585182899530 x^11 +
    1307535010540395 x^10 - 10142299865511450 x^9 + 63030812099294896 x^8 -
    311333643161390640 x^7 + 1206647803780373360 x^6 -3599979517947607200 x^5 +
    8037811822645051776 x^4 - 12870931245150988800 x^3 + 13803759753640704000 x^2 -
    8752948036761600000 x + 2432902008176640000  (error bound 2^-32)

Примітка: Це питання знаходилось в пісочниці приблизно 3 місяці. Якщо ви вважаєте, що це потребує вдосконалення перед публікацією, відвідайте «Пісочницю» та прокоментуйте інші запропоновані питання, перш ніж вони будуть розміщені на Main.



@belisarius, ??
Пітер Тейлор

3
задумувався як жарт :(
доктор belisarius

Я знаю, що це давнє завдання, тому не відчувайте себе зобов'язаним відповідати, якщо ви не відчуваєте, як повторно відкривати його. (а) Чи можемо ми написати функцію або лише повну програму? (b) Якщо ми можемо записати функцію, чи можна вважати, що вхід використовує якийсь зручний тип даних, наприклад, Python fractions.Fraction(раціональний тип)? (c) Чи потрібно обробляти поліноми ступеня <1? (г) Чи можна вважати, що провідний коефіцієнт дорівнює 1?
Ell

(e) Що стосується многочленів з повторними коренями, то варто розрізняти корені непарних і парних множин (у тестових випадках є лише коріння непарних множин.) Хоча з коренями непарної кратності не надто важко впоратися, я ' не впевнений, наскільки відчутним є чисельне поводження з корінням рівномірної кратності, тим більше, що ви задаєте лише границю помилки для значень коренів, а не для їх існування. (...)
Елл

Відповіді:


8

Математика, 223

r[p_,t_]:=Module[{l},l=Exponent[p[x],x];Re@Select[NestWhile[Table[#[[i]]-p[#[[i]]]/Product[If[i!=j,#[[i]]-#[[j]],1],{j,l}],{i,l}]&,Table[(0.9+0.1*I)^i,{i,l}],2*Max[Table[Abs[#1[[i]]-#2[[i]]],{i,l}]]>t&,2],Abs[Im[#]]<t^.5&]]

Це рішення реалізує метод Дюранда-Кернера для розв’язання многочленів. Зауважте, що це не повне рішення (як буде показано нижче), оскільки я ще не можу впоратись з Поліномом Вілкінсона до заданої точності. Спочатку пояснення того, що я роблю: код у форматуванні математики

#[[i]]-p[#[[i]]]/Product[If[i!=j,#[[i]]-#[[j]],1],{j,l}]&: Таким чином функція обчислює для кожного індексу iнаступне наближення Дюранда-Кернера. Потім цей рядок інкапсулюється в Таблицю і застосовується за допомогою NestWhile до вхідних точок, згенерованих Table[(0.9+0.1*I)^i,{i,l}]. Умовою NestWhile є те, що зміна максимуму (за всіма умовами) від однієї ітерації до другої перевищує задану точність. Коли всі умови змінилися менше, ніж це, NestWhile закінчується і Re@Selectвидаляє нулі, які не потрапляють на реальну лінію.

Приклад виводу:

> p[x_] := x^2 - 2
> r[p, .01]
{1.41421, -1.41421}

> p[x_] := x^4 - 8 x^3 + 18 x^2 - 27
> r[p, 10^-6]
{2.99999, 3., 3.00001, -1.}

> p[x_] := x^20 - 210 x^19 + ... + 2432902008176640000 (Wilkinson's)
> Sort[r[p, 2^-32]]
{1., 2., 3., 4., 5., 6., 7.00001, 7.99994, 9.00018, 10.0002, 11.0007, \
11.9809, 13.0043, 14.0227, 14.9878, 16.0158, 16.9959, 17.9992, \
19.0001, 20.}

Як ви, напевно, можете бачити, коли ступінь зростає, цей метод починає відскакувати від правильних значень, ніколи насправді не наводячись повністю. Якщо я встановив умову зупинки мого коду чимось суворішим, ніж "від однієї ітерації до наступної, здогадки змінилися не більше ніж на epsilon", алгоритм ніколи не припиняється. Я думаю, що я повинен просто використовувати Дуранда-Кернера як вхід до методу Ньютона?


У Дюранда-Кернера також є потенційні проблеми з численними коренями. (Метод Ньютона, можливо, теж не дуже допоможе - поліном Вілкінсона спеціально обраний таким, що його не обумовлюють).
Пітер Тейлор

Ви цілком правильні: я відмовився від цього курсу дії після збільшення масштабу на Уілкінсона біля х = 17, це абсолютно безлад. Я переживаю, що мені доведеться піти на символічне рішення на основі Грубнера, щоб отримати набагато більшу точність.
Кая
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.