Skip to content

Commit 072e31f

Browse files
committed
Улучшена устойчивость расчёта биномиальных коэффициентов
Добавлен метод GetBinomialCoefficients(int N) для безопасного вычисления биномиальных коэффициентов в формате double без переполнения. Переведены фильтры Butterworth и Chebyshev на использование этого метода при формировании коэффициентов B, что повысило численную устойчивость. В ChebyshevLowPass.cs заменён LINQ на явный цикл. В тестах снят [Ignore] с класса, но добавлен [Ignore] к тесту импульсной характеристики для высоких порядков (требуется SOS-реализация).
1 parent ebf42c0 commit 072e31f

6 files changed

Lines changed: 28 additions & 7 deletions

File tree

MathCore.DSP/Filters/AnalogBasedFilter.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,23 @@ public static (Complex[] Poles, Complex[] Zeros) ToBandStop(
145145
}
146146
}
147147

148+
/// <summary>Получить биномиальные коэффициенты C(N,k) в вещественном формате без целочисленного переполнения</summary>
149+
/// <param name="N">Порядок бинома</param>
150+
/// <returns>Массив коэффициентов C(N,0..N)</returns>
151+
/// <exception cref="ArgumentOutOfRangeException">Если N меньше 0</exception>
152+
protected static double[] GetBinomialCoefficients(int N)
153+
{
154+
if (N < 0) throw new ArgumentOutOfRangeException(nameof(N), N, "Порядок бинома не может быть отрицательным");
155+
156+
var coefficients = new double[N + 1];
157+
coefficients[0] = 1;
158+
159+
for (var k = 1; k <= N; k++)
160+
coefficients[k] = coefficients[k - 1] * (N - (k - 1d)) / k;
161+
162+
return coefficients;
163+
}
164+
148165
/// <summary>Спецификация фильтра</summary>
149166
public readonly ref struct Specification
150167
{

MathCore.DSP/Filters/ButterworthHighPass.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,9 @@ public static (double[] A, double[] B) GetPolynoms(Specification Spec)
2626
var g_norm = z_poles.Multiply(z => (1 + z) / 2).Abs;
2727

2828
var B = new double[N + 1];
29+
var binomial = GetBinomialCoefficients(N);
2930
for (var i = 0; i < B.Length; i++)
30-
B[i] = BinomialCoefficient(N, i) * (i % 2 == 0 ? g_norm : -g_norm);
31+
B[i] = binomial[i] * (i % 2 == 0 ? g_norm : -g_norm);
3132
var A = GetCoefficientsInverted(z_poles).ToRe();
3233

3334
return (A, B);

MathCore.DSP/Filters/ButterworthLowPass.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,9 @@ public static (double[] A, double[] B) GetPolynoms(Specification Spec)
163163
var g_norm = z_poles.Multiply(z => (1 - z) / 2).Re;
164164

165165
var B = new double[N + 1];
166+
var binomial = GetBinomialCoefficients(N);
166167
for (var i = 0; i < B.Length; i++)
167-
B[i] = g_norm * BinomialCoefficient(N, i);
168+
B[i] = g_norm * binomial[i];
168169

169170
var A = GetCoefficientsInverted(z_poles).ToRe();
170171

MathCore.DSP/Filters/ChebyshevHighPass.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,9 @@ private static (double[] A, double[] B) InitializeI(Specification Spec)
2727
: 1 * k_poles;
2828

2929
var B = new double[N + 1];
30+
var binomial = GetBinomialCoefficients(N);
3031
for (var i = 0; i < B.Length; i++)
31-
B[i] = SpecialFunctions.BinomialCoefficient(N, i) * (i % 2 == 0 ? g_norm : -g_norm);
32+
B[i] = binomial[i] * (i % 2 == 0 ? g_norm : -g_norm);
3233
var A = Polynom.Array.GetCoefficientsInverted(z_poles).ToRe();
3334

3435
return (A, B);

MathCore.DSP/Filters/ChebyshevLowPass.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -113,9 +113,10 @@ private static (double[] A, double[] B) InitializeI(Specification Spec)
113113

114114
var g_norm = (N.IsOdd() ? 1 : Spec.Gp) / (2.Pow(N) / z_poles.Multiply(z => 1 - z).Re);
115115

116-
var B = Enumerable
117-
.Range(0, N + 1)
118-
.ToArray(i => g_norm * BinomialCoefficient(N, i));
116+
var binomial = GetBinomialCoefficients(N);
117+
var B = new double[N + 1];
118+
for (var i = 0; i < B.Length; i++)
119+
B[i] = g_norm * binomial[i];
119120

120121
return (A, B);
121122
}

Tests/MathCore.DSP.Tests/Filters/IirCoefficientsStabilityTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
namespace MathCore.DSP.Tests.Filters;
44

55
[TestClass]
6-
[Ignore("Диагностические стресс-тесты устойчивости; включить после внесения исправлений численной устойчивости")]
76
public class IirCoefficientsStabilityTests
87
{
98
private sealed record StressCase(string Name, double Dt, Func<DSP.Filters.IIR> Factory);
@@ -17,6 +16,7 @@ public void FrequencyResponse_ShouldRemainFinite_OnStressSpecifications() =>
1716
RunStressValidation(AssertFrequencyResponseIsFinite);
1817

1918
[TestMethod]
19+
[Ignore("Диагностический тест: для высоких порядков требуется секционная реализация (SOS) вместо прямой формы")]
2020
public void ImpulseResponse_ShouldRemainFinite_OnStressSpecifications() =>
2121
RunStressValidation((case_name, filter, dt) => AssertImpulseResponseIsFinite(case_name, filter));
2222

0 commit comments

Comments
 (0)