> Input
> Input
>> 1…2
>> ∤L
>> L’
>> Each 4 3
>> Select∧ 5 L
>> Each 7 6
>> Lⁿ10
> -1
>> Each 9 8
>> L‖R
>> Each 12 11 3
>> 13ᴺ
>> Each 9 14
> 0
>> 15ⁿ16
>> Output 17
Try it online!
Aside from revamping the algorithm, the only real golfing opportunity can be found on lines 9 and 10, but swapping them. When they're the other way round, the code is 1 byte longer, due to the repeated use of 9
or 10
.
The structure tree for this program is:
Red represents nilad lines, green represents function arguments. Arrows from \$a \to b\$ show the use of line \$a\$ as an argument for line \$b\$.
How it works.
We start at lines 1, 2 and 3. The first two simply take and store the inputs \$\alpha\$ and \$\beta\$ on lines 1 and 2 respectively. There two values are then passed to line 3, 1…2
, which form the range
$$R := [\alpha, \alpha+1, ..., \beta-1, \beta]$$
As you can see from the tree above, \$R\$ is used in two places: line 6 and line 13. As line 6 eventually leads to line 13, we'll take the path down 6.
Line 6 is an Each
statement, that maps a function (the first line reference) over an array (the second line reference). Here, the function is defined on line 4 as \$f(x) = \{i \: | \: (i | x), (i \le x), i \in \mathbb{N}\}\$ i.e. the array of \$x\$'s divisors. The array is \$R\$, so line 6 iterates over \$R\$, returning the divisors of each integer, forming a new array
$$D := [f(\alpha), f(\alpha+1), ..., f(\beta-1), f(\beta)]$$
We then get more complicated as we skip down to line 8. Line 8 is also an Each
statement, but its function argument, line 7 is split over two lines, 7 and 5:
>> L’
>> Select∧ 5 L
Mathematically, this is the function \$g(A) = \{i \: | \: (i \in \mathbb{P}), (i \in A)\}\$, that takes a set \$A\$ and returns all the primes in \$A\$. Line 8 is iterating over \$D\$, essentially filtering all composite numbers from each subarray in \$D\$, to form
$$A := [g(f(\alpha)), g(f(\alpha+1)), ..., g(f(\beta-1)), g(f(\beta))]$$
Then, we encounter yet another Each
statement on line 11, which iterates line 9 over \$A\$. Line 9 takes an array and returns the final element of that array. As \$g(A)\$ returns the array sorted, this is equivalent to return the largest value in that array. At this point, we remember that \$g(f(x))\$ returns the list of prime factors of \$x\$, so line 9 maps each \$x\$ to its largest prime factor, returning
$$B := [\max(g(f(\alpha))), \max(g(f(\alpha+1))), ..., \max(g(f(\beta-1))), \max(g(f(\beta)))]$$
Once we have \$B\$, we now have an array we can sort \$R\$ by, e.g. we sort each value \$x\$ in \$R\$ by the resulting value of \$\max(g(f(x)))\$. However, we do not have a sort-by command in Whispers, only the simple Python sort(list)
. Luckily, due to how sort
sorts lists of lists, we can concatenate each \$x\$ with \$\max(g(f(x))\$ and sort by the first element, what Python does naturally.
Lines 12 and 13 perform this concatenation, by iterating a concatenate function (line 12) over \$B\$ and \$R\$. This forms an array of pairs of numbers, in the form \$[\max(g(f(x))), x]\$. Next, we sort this array on line 14, which, due to the way Python compares lists, places the numbers with the lowest largest prime factor towards the end.
Almost finished. The only things left to do are to extract \$R\$, sorted by \$B\$ (we'll call this array \$R_B\$. This is done simply by taking the last element of each subarray (line 9 already does this for us, so we simply need Each 9 14
on line 15, rather than defining a new function). Once that's done, we simply take the first element in \$R_B\$, which will be (one of) the number with the lowest largest prime factor i.e. the smoothest number.