Skip to content

Commit ee32248

Browse files
authored
add practice exercise fft (#875)
* add practice exercise fft * Update instructions.md delete duplicate entry * Update config.json * Update introduction.md * updates to introduction.md, instructions.md and hints.md * update example.jl * Update instructions.md fix typos * typo fixes
1 parent 6d0254e commit ee32248

File tree

8 files changed

+189
-0
lines changed

8 files changed

+189
-0
lines changed

config.json

+10
Original file line numberDiff line numberDiff line change
@@ -1311,6 +1311,16 @@
13111311
"prerequisites": [],
13121312
"difficulty": 6
13131313
},
1314+
{
1315+
"slug": "fft",
1316+
"name": "FFT",
1317+
"uuid": "d1a733da-acd5-4166-927e-044c051e153c",
1318+
"practices": [
1319+
"complex-numbers"
1320+
],
1321+
"prerequisites": [],
1322+
"difficulty": 6
1323+
},
13141324
{
13151325
"slug": "pythagorean-triplet",
13161326
"name": "Pythagorean Triplet",

exercises/practice/fft/.docs/hints.md

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# Hints
2+
3+
## 1. DFT
4+
- This can be done straight from the definition of the DFT: `Xₖ = Σ xₙ * ℯ^(-2iπk * n/N)`
5+
- While the complexity is fixed at `O(n²)`, there are still methods to do this which are more efficient.
6+
- Loops are generally slower than matrix multiplication.
7+
8+
## 2. FFT
9+
- As stated in the introduction, this is typically done with a divide-and-conquer strategy, so recursion is likely your friend, but while loops are not ruled out (and can even be more efficient).
10+
- An outline of an algorithm is as follows:
11+
- When the length of the input vector is less than or equal to 1, return the input vector. Otherwise...
12+
- The signal is split into two parts, the elements with even indices `Xₑ` and the elements with odd indices `Xₒ`.
13+
- The FFT is run on both parts.
14+
- The vector `f(n) = ℯ^(-2iπ * n/N)` is made (with `n` running from 0 to N/2-1).
15+
- The two parts `Xₑ` and `Xₒ` are recombined into a full vector `[Xₑ + f(n) * Xₒ; Xₑ - f(n) * Xₒ]`, where the addition and multiplications are elementwise.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Instructions
2+
3+
Your friend is a sound engineer who has asked you to help them code up a Fourier Transform for some proprietary software they are putting together.
4+
Since you want to get this right, you will first implement the Discrete Fourier Transform (DFT) in all its naive glory.
5+
This could then be used to test the correctness of your final objective, the Fast Fourier Transform (FFT).
6+
7+
## 1. DFT
8+
9+
Implement the DFT.
10+
11+
Note, that this is of `O(n²)` complexity because you get one `k` for a run through all `n`, and you'll want a `Xₖ` vector that is the same length as the signal `Xₙ` vector (for invertibility), so `n` times through `n` points is `O(n²)`.
12+
13+
## 2. FFT
14+
15+
Implement the FFT.
16+
17+
~~~~exercism/note
18+
There are a lot of resources to find more detail on the particulars of this algorithm, so feel free to do some research.
19+
~~~~
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Introduction
2+
3+
### Fourier Transform
4+
5+
The [Fourier Transform](https://en.wikipedia.org/wiki/Fourier_transform) is seen often in physics and mathematics, which translates to it having many everyday uses as well.
6+
7+
In short, the Fourier Transform takes an input signal, often in time or space domains, and transforms it to the frequency domain.
8+
And more concretely, with an input signal that is made of a jumble (i.e. superposition) of different frequencies, the transform will separate out the individual frequencies and show their relative strengths.
9+
10+
This makes the transform a principal tool in signal processing (e.g. filtering, compression, etc.) and many data science applications.
11+
Therefore, it's perhaps more widely used than one might first imagine, which also makes it a prime example of where imaginary numbers make themselves *indispensable*, considering the output of the Fourier Transform is a complex number in general.
12+
13+
While the Fourier Transform has an integral formulation, we are digital, so we're interested in the discrete version.
14+
The derivation of the transform is beyond the scope of this exercise, but the forward [discrete transform](https://en.wikipedia.org/wiki/Discrete_Fourier_transform) (to the frequency domain)takes the following form:
15+
`Xₖ = Σ xₙ * ℯ^(-2iπk * n/N)`
16+
Here, the sum runs over `n`, and `N` is the number of samples, `k` is the frequency, `i` the imaginary unit and `xₙ` is an individual sample.
17+
18+
### Fast Fourier Transform
19+
20+
While extremely useful, the Fourier Transform, naively implemented, is a bit slow (`O(n²)`).
21+
The [Fast Fourier Transform](https://en.wikipedia.org/wiki/Fast_Fourier_transform) (FFT) is an algorithm which drastically speeds this up.
22+
This is typically done through a divide-and-conquer strategy which exploits some symmetries in the formulation and results in an asymptotically `O(Nlog(N))` complexity.
23+
This speed up greatly improves the practically and range of applications suitable for the transform.
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"authors": [],
3+
"files": {
4+
"solution": [
5+
"fft.jl"
6+
],
7+
"test": [
8+
"runtests.jl"
9+
],
10+
"example": [
11+
".meta/example.jl"
12+
]
13+
},
14+
"blurb": "Write a Fast Fourier Transform algorithm using complex numbers"
15+
}
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
function dft(x)
2+
N = length(x)
3+
n = 0:(N-1)
4+
M = @. cispi(-2n' * n / N)
5+
M * x
6+
end
7+
8+
function fft(x)
9+
length(x) 1 && return x
10+
N = length(x)
11+
xeven, xodd = fft(x[1:2:end]), fft(x[2:2:end])
12+
factors = cispi.(-(0:2:N-2) / N)
13+
[xeven .+ factors .* xodd; xeven .- factors .* xodd]
14+
end

exercises/practice/fft/fft.jl

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
function dft()
2+
3+
end
4+
5+
function fft()
6+
7+
end

exercises/practice/fft/runtests.jl

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
using Test
2+
3+
include("fft.jl")
4+
5+
@testset verbose = true "tests" begin
6+
@testset "dft" begin
7+
x = [0.1735745757945074, 0.32166161915780656, 0.25858546995315457, 0.16643864408566544,
8+
0.5270150071089016, 0.48302213696845187, 0.3906633890864917, 0.802762551279973,
9+
0.9807576556964709, 0.09443144857141617, 0.5447580773835046, 0.4339142195043123,
10+
0.2112283719951349, 0.18342915487003242, 0.3475083889757653, 0.6226472050312687]
11+
ex = [6.542397915462858 + 0.0im, -1.2135321845196991 - 0.32950423224009995im, 0.8224621781013692 + 0.6952615524269858im, -0.03238019886418542 + 0.12357864695990722im,
12+
0.35106028519609866 + 0.9432182603335124im, -1.1162463918912673 - 0.851673419434497im, 0.009715526672514373 + 0.8256050909757902im, -0.8665735443327022 - 0.041609758179437434im,
13+
0.32578395652500447 + 0.0im, -0.8665735443327022 + 0.041609758179437434im, 0.009715526672514373 - 0.8256050909757902im, -1.1162463918912673 + 0.851673419434497im,
14+
0.35106028519609866 - 0.9432182603335124im, -0.03238019886418542 - 0.12357864695990722im, 0.8224621781013692 - 0.6952615524269858im, -1.2135321845196991 + 0.32950423224009995im]
15+
@test dft(x) ex
16+
17+
x = [0.4222584550315842, 0.7378274427445809, 0.10083458248601018, 0.32106572972106817,
18+
0.9695150609084499, 0.012898301755861596, 0.7555027304121053, 0.3467415729179013,
19+
0.5293228338621144, 0.7928071928758357, 0.9153753178220317, 0.907410564584128,
20+
0.18909001622655808, 0.19934942931986965, 0.7839550064174631, 0.9848878347210348]
21+
ex = [8.968842071806597 + 0.0im, -0.2771699513464767 + 0.7948792439327731im, 0.7981567981285712 - 0.3360522179707701im, 1.041405875055292 + 1.7211398439521313im,
22+
-0.44548127110890334 + 0.8172233352479843im, -0.14383767230595634 - 1.0318823950363676im, -1.2122043746111895 - 1.3825478910138231im, -1.0486557667249796 + 1.1635571836718415im,
23+
0.3628659345260363 + 0.0im, -1.0486557667249796 - 1.1635571836718415im, -1.2122043746111895 + 1.3825478910138231im, -0.14383767230595634 + 1.0318823950363676im,
24+
-0.44548127110890334 - 0.8172233352479843im, 1.041405875055292 - 1.7211398439521313im, 0.7981567981285712 + 0.3360522179707701im, -0.2771699513464767 - 0.7948792439327731im]
25+
@test dft(x) ex
26+
27+
x = [0.5531575942573769, 0.8246259746206648, 0.314531809826071, 0.9185324489490104,
28+
0.7325068768586672, 0.35630422442695775, 0.652064000263128, 0.27034333128149246,
29+
0.9075517683926037, 0.09931672467692998, 0.6853214600877686, 0.19624579432624933,
30+
0.12535570766646598, 0.22154192490613434, 0.11011919058623454, 0.4792350437459888]
31+
ex = [7.446753874871744 + 0.0im, 0.08813008504936026 - 1.7176105659192953im, 0.589338692223578 - 0.7406325998160835im, 0.10570805619257095 + 0.33699703770428036im,
32+
0.5565354864119116 + 0.36256776967205395im, 0.47630504762741355 - 0.635255352965023im, 0.6163548640261166 - 0.2652924416871295im, -2.0877198854102517 - 0.26125827981979355im,
33+
0.7144629410048879 + 0.0im, -2.0877198854102517 + 0.26125827981979355im, 0.6163548640261166 + 0.2652924416871295im, 0.47630504762741355 + 0.635255352965023im,
34+
0.5565354864119116 - 0.36256776967205395im, 0.10570805619257095 - 0.33699703770428036im, 0.589338692223578 + 0.7406325998160835im, 0.08813008504936026 + 1.7176105659192953im]
35+
@test dft(x) ex
36+
end
37+
38+
@testset "fft" begin
39+
@testset "dft ≈ fft" begin
40+
x = rand(1024)
41+
@test dft(x) fft(x)
42+
43+
x = rand(2048)
44+
@test dft(x) fft(x)
45+
46+
x = rand(4096)
47+
@test dft(x) fft(x)
48+
end
49+
50+
@testset "standard tests" begin
51+
x = [0.02989961280834119, 0.4804460331252213, 0.7121071892533332, 0.7907040793300502,
52+
0.2819418772194955, 0.6870809563317686, 0.9536386596130818, 0.10967579000276617,
53+
0.37074326488356757, 0.38559877832349365, 0.1197045709710659, 0.9191293621241056,
54+
0.17702063776277954, 0.9587046786149889, 0.24453725760612732, 0.18605312354211245]
55+
ex = [7.4069858715122985 + 0.0im, -0.21037159043914788 - 0.662694603016581im, -1.6096025621450665 - 0.08219872013299256im, -0.3250984449072115 - 0.985537187629967im,
56+
-1.1703822847694243 - 0.5062680913964384im, -0.191551856587344 + 0.6452249704327266im, 1.492963287564334 - 0.8149270341226125im, -0.6363527163672021 + 1.3877525128729766im,
57+
-1.6277997312767143 + 0.0im, -0.6363527163672021 - 1.3877525128729766im, 1.492963287564334 + 0.8149270341226125im, -0.191551856587344 - 0.6452249704327266im,
58+
-1.1703822847694243 + 0.5062680913964384im, -0.3250984449072115 + 0.985537187629967im, -1.6096025621450665 + 0.08219872013299256im, -0.21037159043914788 + 0.662694603016581im]
59+
@test fft(x) ex
60+
61+
x = [0.8857615933270173, 0.3770996704519639, 0.6191717861360299, 0.2279864320418037,
62+
0.3710550677149468, 0.7137080247554721, 0.9086552981275375, 0.26940612875036585,
63+
0.9316001574819012, 0.1422009638569891, 0.2511923754500279, 0.14389246582402393,
64+
0.3447025861042857, 0.5699305816496036, 0.0035841776770096923, 0.9428327322458285]
65+
ex = [7.702780041594807 + 0.0im, 0.3907235308150495 - 0.9692435153153347im, 1.155359762369303 + 1.1765681059731916im, 0.7366840160341982 - 0.38148062030969077im,
66+
0.750515767237546 - 0.21882148185200667im, -0.06879876415681918 + 1.3661797431885399im, 1.0478484316100691 + 1.0928174775362127im, -1.241963039311964 + 0.8838267746255402im,
67+
0.928666042442706 + 0.0im, -1.241963039311964 - 0.8838267746255402im, 1.0478484316100691 - 1.0928174775362127im, -0.06879876415681918 - 1.3661797431885399im,
68+
0.750515767237546 + 0.21882148185200667im, 0.7366840160341982 + 0.38148062030969077im, 1.155359762369303 - 1.1765681059731916im, 0.3907235308150495 + 0.9692435153153347im]
69+
@test fft(x) ex
70+
71+
x = [0.015145960415561266, 0.7740229564774506, 0.27686735614211755, 0.5465482642348628,
72+
0.47788759427457317, 0.6661395362891422, 0.7191192700035132, 0.3760197064432971,
73+
0.12855568619998692, 0.6361812496114388, 0.9306949171337181, 0.23141549028028874,
74+
0.9923346738399057, 0.6607734659198129, 0.6806463216266808, 0.09985602163660001]
75+
ex = [8.21220847052895 + 0.0im, -0.6121903695332016 + 0.49503317221231513im, -1.481233338429867 - 0.08030094872996373im, 0.03700001242312634 - 0.33916717005182734im,
76+
-0.9934039501760026 - 1.4832777257027958im, 0.7152413055946621 - 0.18051584974980955im, -1.1718079045679939 - 0.46470758543868085im, -0.5936898516222895 - 1.4041038257469973im,
77+
0.23029508874316384 + 0.0im, -0.5936898516222895 + 1.4041038257469973im, -1.1718079045679939 + 0.46470758543868085im, 0.7152413055946621 + 0.18051584974980955im,
78+
-0.9934039501760026 + 1.4832777257027958im, 0.03700001242312634 + 0.33916717005182734im, -1.481233338429867 + 0.08030094872996373im, -0.6121903695332016 - 0.49503317221231513im]
79+
@test fft(x) ex
80+
end
81+
82+
@testset "Nlog(N) test" begin
83+
@test !isempty(fft(rand(2^14)))
84+
end
85+
end
86+
end

0 commit comments

Comments
 (0)