| Bytes | Lang | Time | Link |
|---|---|---|---|
| 315 | Python3 | 240831T180636Z | Ajax1234 |
| 086 | R | 210611T205507Z | Dominic |
| 021 | Jelly | 210611T172529Z | hyper-ne |
| 060 | Charcoal | 210612T234453Z | Neil |
| 129 | JavaScript ES6 | 210611T192550Z | Arnauld |
| 022 | Jelly | 210611T211658Z | Nick Ken |
| 021 | Jelly | 210611T204035Z | Jonathan |
| 093 | Julia 0.7 | 210611T192429Z | MarcMush |
Python3, 315 bytes
import math
def f(d):
A=[8,11.25,22.5,45,90]
q,s=[(A,[])],[]
for a,b in q:
if sum(b)>359 or[]==a:continue
for i in range(math.ceil((d-sum(b))/a[0])+1):q+=[(a[1:],T:=b+[a[0]]*i)];s+=[T]
m={}
for i in s:K=abs(sum(i)-d);m[K]=m.get(K,[])+[i]
P=[0]*5
for i in min(m[min(m)],key=len):P[A.index(i)]+=1
return P
R, 95 88 87 86 bytes
Edit: -1 byte thanks to pajonk
function(a,b=expand.grid(rep(list(23:0),5)))b[order((c(8,90/2^(3:0))%*%t(b)-a)^2)[1],]
Ungolfed code
fittings=
function(a){ # a=desired angle
b=expand.grid(rep(list(23:0),5)) # b=all combinations of 5 elements of 23..0 (note: largest-to-smallest - see below)
# (23 is number of smallest (8-degree) fittings needed to get over 180 degrees)
c=colSums(t(b)*c(8,2^(-2:1)*45)) # c=the total angle made by each combination of fittings
d=which.min((d-a)^2) # d=the first combination that minimized the absolute difference to angle a
# (we take the square to save a byte getting the absolute difference,
# and we automatically get the combination with the smallest numbrer of fittings
# since we constructed the combinations largest-to-smallest)
return(b[d,]) # finally, return that element of b
Jelly, 21 bytes
Ḷṗ5SÞðḋ90H3С;8¤ạðÞḢṚ
Note - after Nick Kennedy's golfs, this answer is actually identical to Jonathan Allan's answer, which they arrived at independently. Make sure to upvote them.
-9 bytes thanks to Nick Kennedy by rewriting the conversion from counts to total angle
ḶṗSÞ5ðḋ90H3С;8¤ạðÞḢṚ Main Link
Ḷ Lowered range; [0, 1, ..., N-1]
ṗ5 Cartesian product; size 5 lists of the above elements
SÞ Sort by sum
ð ðÞ Sort by:
ạ - absolute difference of the following and the input:
ḋ - dot product with
90H3С;8¤ - [90, 45, 22.5, 11.25, 8] via the following
90 - 90
3С - repeat 3 times and collect results:
H - halve
;8 - append 8
Ḣ take the first one (since it'll already be sorted by count and it's stable)
Ṛ reverse it
Charcoal, 60 bytes
≔↔⁻NEφ⁺×¹¹·²⁵﹪ι¹⁶×⁸÷ι¹⁶θ≔E⌕Aθ⌊θ…⮌⊞O↨⁺¹⁶ι²÷ι¹⁶¦⁵θI⊟Φθ⁼Σι⌊EθΣλ
Try it online! Link is to verbose version of code. Outputs using Charcoal's default array format i.e. each element on its own line. Explanation:
≔↔⁻NEφ⁺×¹¹·²⁵﹪ι¹⁶×⁸÷ι¹⁶θ
Calculate the possible angles using sets of up to 999 8° fittings and optionally one each of each of the other fittings, and see how closely they compare to the input.
≔E⌕Aθ⌊θ…⮌⊞O↨⁺¹⁶ι²÷ι¹⁶¦⁵θ
Find the indices of the most accurate sets and calculate the fittings corresponding to each of those indices.
I⊟Φθ⁼Σι⌊EθΣλ
Output the last set with the fewest fittings.
JavaScript (ES6), 129 bytes
f=(n,a=[0,0,0,0,0],c=e=C=1/0)=>n<(n*n<e|n*n==e&c<C&&[e=n*n,C=c,o=a])?o:a.map((_,i,[...a])=>f(n-(i?90/(16>>i):8),a,-~c,a[i]++))&&o
Commented
f = ( // f is a recursive function taking:
n, // n = input
a = [0, 0, 0, 0, 0], // a[] = pipe counters
c = // c = current number of pipes
e = // e = best (smallest) squared error
C = 1 / 0 // C = number of pipes for the best solution
) => //
n < ( //
n * n < e | // if the current error is less than the best error
n * n == e & c < C && // or they're equal but we're using less pipes:
[ e = n * n, // update the best error
C = c, // update the best count
o = a // update the output
] // if e, C and o have been updated, we compare n with
// an array, which is always falsy
) ? // otherwise, we test whether n is negative:
o // in which case we return o[]
: // else:
a.map( //
(_, i, [...a]) => // for each value at position i in a[], using a copy
// of a[]:
f( // do a recursive call:
n - ( // subtract from n:
i ? // if i is not equal to 0:
90 / (16 >> i) // 11.25, 22.5, 45 or 90
: // else:
8 // 8
), //
a, // pass the copy of a[]
-~c, // increment c
a[i]++ // increment a[i]
) // end of recursive call
) && o // end of map(); return o[]
Jelly, 22 bytes
ṗⱮẎSạ¥ÞḢċⱮ⁸
90H3С;8Ṛç
A pair of links which takes a single argument, the number of degrees to match, and returns a list of 5 integers. Hopelessly slow for increasing values, but should theoretically work for all of the required range.
This is a much more efficient implementation but is three bytes longer.
Jelly, 21 bytes
Ḷṗ5SÞḋ90H3С;8¤ạɗÞ⁸ḢṚ
A monadic Link that accepts an integer in \$[0,180]\$ and yields a list of the five pipe counts in the prescribed order.
Try it online! although it'll time out for inputs above \$15\$ :(
How?
Builds all selections of pipes consisting of less than input-angle of each pipe, sorts these by their number of pipes, then (stable) sorts by the difference between the angle-sum and input-angle and yields the first.
Ḷṗ5SÞḋ90H3С;8¤ạɗÞ⁸ḢṚ - Link: angle, A
Ḷṗ5 - (A) Cartesian product 5 -> all selections of the five pipes with
up to A-1 of each pipe
SÞ - sort by sum (number of pipes)
Þ - sort (the elements X of that) by:
ɗ ⁸ - last three links as a dyad - f(X, A)
¤ - nilad followed by links as a nilad:
90 - 90
3С - collect & repeat three times:
H - halve -> [90, 45, 22.5, 11.25]
;8 - concatenate 8 -> [90, 45, 22.5, 11.25, 8]
ḋ - (X) dot-product (that)
ạ - (that) absolute-difference (A)
Ḣ - head
Ṛ - reverse (to the prescribed order of pipe counts)
Julia 0.7, 93 bytes
!x=minimum(i->((4x-[32,45,90,180,360]⋅i)^2,sum(i),i),Iterators.product(fill(0:22,5)...))[3]