Во-первых, из границ синуса вы знаете, что любое решение должно быть в интервале [-abs(a),abs(a)]
. Если abs(a)\le 1
, то единственный корень в [-1,1]
- это x=0
Помимо интервала, содержащего ноль, вы также знаете, что в любом из интервалов есть ровно один корень корни из cos(x)=1/a
, которые являются экстремумами из a*sin(x)-x
. Установите phi=arccos(1/a) in [0,pi]
, тогда этими корнями являются -phi+2*k*pi
и phi+2*k*pi
.
Интервал для k=0
может содержать 3 корня, если 1<a<0.5*pi
. Для положительного корня известно x/a=sin(x)>x-x^3/6
, так что x^2>6-6/a
.
И, наконец, проблема симметрична, если x
является корнем, то же самое происходит с -x
, поэтому все, что вам нужно сделать, это найти положительные корни.
Итак, чтобы вычислить корни,
0
. abs(a)<=1
дальнейших корней нет, возвратите. Можно также использовать -pi/2<=a<=1
. 1<a<pi/2
, применить выбранный метод брекетинга к интервалу [sqrt(6-6/a), pi/2]
, добавить корень в список и вернуться. В остальных случаях, когда abs(a)>=0.5*pi
:
phi=arccos(1/a)
. k
примените метод брекетинга к интервалам [2 * (k-1) * pi + phi, 2 * k * pi-phi] и [2 * k * pi- phi, 2 * k * pi-phi, так что (k-0.5)*pi < abs(a)
[(k-0.5)*pi, (k+0.5)*pi]
до тех пор, пока нижняя граница интервала меньше, чем abs(a)
и функция имеет изменение знака в течение интервала.
let a=10;
function f(x) { return x - a * Math.sin(x); }
findRoots();
//-------------------------------------------------
function findRoots() {
log.innerHTML = `<p>roots for parameter a=${a}`;
rootList.innerHTML = "<tr><th>root <i>x</i></th><th><i>x-a*sin(x)</i></th><th>numSteps</th></tr>";
rootList.innerHTML += "<tr><td>0.0<td>0.0<td>0</tr>";
if( Math.abs(a)<=1) return;
if( (1.0<a) && (a < 0.5*Math.PI) ) {
illinois(Math.sqrt(6-6/a), 0.5*Math.PI);
return;
}
const phi = Math.acos(1.0/a);
log.innerHTML += `phi=${phi}<br>`;
let right = 2*Math.PI-phi;
for (let k=1; right<Math.abs(a); k++) {
let left = right;
right = (k+2)*Math.PI + ((0==k%2)?(-phi):(phi-Math.PI));
illinois(left, right);
}
}
function illinois(a, b) {
log.innerHTML += `<p>regula falsi variant illinois called for interval [a,b]=[${a}, ${b}]`;
let fa = f(a);
let fb = f(b);
let numSteps=2;
log.innerHTML += ` values f(a)=${fa}, f(b)=${fb}</p>`;
if (fa*fb > 0) return;
if (Math.abs(fa) < Math.abs(fb)) { var h=a; a=b; b=h; h=fa; fa=fb; fb=h;}
while(Math.abs(b-a) > 1e-15*Math.abs(b)) {
let c = b - fb*(b-a)/(fb-fa);
let fc = f(c); numSteps++;
log.innerHTML += `step ${numSteps}: midpoint c=${c}, f(c)=${fc}<br>`;
if ( fa*fc < 0 ) {
fa *= 0.5;
} else {
a = b; fa = fb;
}
b = c; fb = fc;
}
rootList.innerHTML += `<tr><td>${b}<td>${fb}<td>${numSteps}</tr>`;
}
aInput.addEventListener('change', () => {
let a_new = Number.parseFloat(aInput.value);
if( isNaN(a_new) ) {
alert('Not a number '+aInput.value);
} else if(a!=a_new) {
a = a_new;
findRoots();
}
});
<p>Factor <i>a</i>: <input id="aInput" value="10" /></p>
<h3>Root list</h3>
<table id="rootList" border = 1>
</table>
<h3>Computation log</h3>
<div id="log"/>
К сожалению, привязка данных в ListView
не поддерживает регулярный (объект) события уведомления об изменении (FooChanged
/ INotifyPropertyChanged
). Однако, если Вы знаете об изменении, можно заставить список снова переплетать себя. Так как Вы разделяете на подклассы, можно звонить:
this.RefreshItems();
или для единственного объекта:
this.RefreshItem(index);
Иначе, так как это не общедоступно, можно моделировать его путем изменения DisplayMember
:
lb.DisplayMember = "";
lb.DisplayMember = "Bar";
Немного hacky, возможно, но это работает и поддерживает текущий выбор и т.д. (в отличие от очистки DataSource
).
Почему Вы вручную не обновляете текст рассматриваемого объекта? Вы могли бы также рассмотреть развертывание Вашего собственного механизма привязки данных для ListBox. И проверьте ObjectListView, чтобы видеть, имеет ли это какую-либо справку.