diff --git a/DIRECTORY.md b/DIRECTORY.md index 6d098f1f845a..e91db5292f2c 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -724,6 +724,7 @@ * [Collatz Sequence](maths/collatz_sequence.py) * [Combinations](maths/combinations.py) * [Continued Fraction](maths/continued_fraction.py) + * [Convolve 1D](maths/convolve_1d.py) * [Decimal Isolate](maths/decimal_isolate.py) * [Decimal To Fraction](maths/decimal_to_fraction.py) * [Dodecahedron](maths/dodecahedron.py) @@ -876,6 +877,7 @@ * [Two Pointer](maths/two_pointer.py) * [Two Sum](maths/two_sum.py) * [Volume](maths/volume.py) + * [Weighted Average](maths/weighted_average.py) * [Zellers Congruence](maths/zellers_congruence.py) ## [Matrix](matrix) diff --git a/maths/convolve_1d.py b/maths/convolve_1d.py new file mode 100644 index 000000000000..34565a1932d9 --- /dev/null +++ b/maths/convolve_1d.py @@ -0,0 +1,62 @@ +from dataclasses import dataclass, field +from math import floor + +# discrete_convolution +""" + * Calculate the discrete convolution of two + linear discrete sets + https://en.wikipedia.org/wiki/Convolution +""" + + +@dataclass +class Signal: + """ + A discrete representation of a signal as a n-dimensional vector + + >>> Signal([1.0,3.0,2.0,-1.0]) + Signal(signal=[1.0, 3.0, 2.0, -1.0], n=4) + """ + + signal: list[float] = field(default_factory=list) + n: int = 0 + + def __post_init__(self) -> None: + for i in self.signal: + if not isinstance(i, (float, int)): + raise TypeError("vector must be a list of numeric values.") + else: + self.n += 1 + + +@dataclass +class DiscreteConvolve1D: + """ + 1D discrete convolution between two linear signals + + >>> s1 = Signal([1,2,3,4,5]) + >>> s2 = Signal([1,-1,2,-3]) + >>> DiscreteConvolve1D(s1,s2) # doctest: +NORMALIZE_WHITESPACE + DiscreteConvolve1D(kern=Signal(signal=[1, 2, 3, 4, 5], n=5), + sig=Signal(signal=[1, -1, 2, -3], n=4)) + """ + + kern: Signal = field(default_factory=Signal) + sig: Signal = field(default_factory=Signal) + + @property + def convolve_1d(self) -> Signal: + conv = Signal() + for i in range(self.sig.n): + conv.signal.append(0) + for j in range(self.kern.n): + if ( + i + j - floor(self.kern.n / 2) < 0 + or i + j - floor(self.kern.n / 2) >= self.sig.n + ): + sig_val = 0.0 + else: + sig_val = float(self.sig.signal[i + j - floor(self.kern.n / 2)]) + conv.signal[i] += self.kern.signal[j] * sig_val + conv.n += 1 + return conv