# Divisor skyline

For any positive integer k, let d(k) denote the number of divisors of k. For example, d(6) is 4, because 6 has 4 divisors (namely 1, 2, 3, 6).

Given a positive integer N, display a "skyline" in ASCII art using a fixed character, such that the height of the "building" located at horizontal position k is d(k) for k = 1, ..., N. See test cases below.

# Rules

• Any non-whitespace character may consistently be used, not necessarily # as shown in the test cases.
• The algorithm should theoretically work for arbitrarily high N. In practice, it is acceptable if the program is limited by time, memory, data-type size or screen size.
• Horizontally or vertically leading or trailing spaces or newlines are allowed.
• Input and output can be taken by any reasonable means.
• Programs or functions are allowed, in any programming language. Standard loopholes are forbidden.
• Shortest code in bytes wins.

# Test cases

N = 10:

     # # #
# # ###
#########
##########


N = 50:

                                               #
#           #
#     #     #   # #     #
#     #     #   # #     #
#     # #   #   # # #   #   # # ##  # #
#   # # #   #   # # #   #   # # ##  # #
# # # # ### # ### # ### # ##### ### # ### # #
# # ### # ### # ### ##### # ##### ### # ### ###
#################################################
##################################################


N = 200:

                                                                                                                                                                                   #
#
#                                               #           #
#                       #                       #           #
#                       #                       #           #           #
#                       #                       #           #           #
#           #           #     #     #           #           #     #     #       #   #     #     #   #       #           #           #     # #
#           #           #     #     #           #           #     #     #       #   #     #     #   #       #           #           #     # #
#           #           #       #   #     #     #           #   #       #     #     #       #   #     #     #   # #     #       #   #           #     # #
#           #           #           #       #   #     #     #   #       #   #       #     #     #       #   #     #     #   # #     #       #   #           #   # # #
#     #     #   # #     #     # #   #     #   # #     # #   #   # #     #   # # ##  # # # #     #     # # # #  ## # #   #     # # # #   # #  #  # #   # #   # # # #  ## #  ## # #
#     #     #   # #     #     # #   #   # #   # #     # #   #   # #     #   # # ##  # # # #     #     # # # #  ## # #   #     # # # #   # #  #  # #   # #   # # # #  ## #  ## # #
#     # #   #   # # #   #   # # ##  # # # # #   #  ## # # # #  ## # #   #   # # #   # ### # ##  # # # # ##  #   # # # # #  ## # #   #  ## # ### #   # # ##  # ### ###   # # # # ### #  ## # #
#   # # #   #   # # #   #   # # ##  # # # # #   #  ## # # # #  ## # ##  #   # # #   # ### # ##  # # # # ##  #   # # # # #  ## # #   #  ## # ### #   # # ##  # ### ###   # # # # ### #  ## # #
# # # # ### # ### # ### # ##### ### # ### # ### ##### # ##### ### # ##### ### ##### ####### ### # ### # ### ####### ##### ### ##### # ######### # ##### ##### ### # ### ##### # ######### # ### # #
# # ### # ### # ### ##### # ##### ### # ### ##### ##### # ##### ### # ##### ### ##### ####### ### # ### # ### ############# ### ##### # ######### # ##### ##### ### ##### ##### # ######### # ### # #
#######################################################################################################################################################################################################
########################################################################################################################################################################################################


# Jelly, 9 bytes

Uses 0 instead of #.

RÆD0ṁz⁶ṚY


Try it online!

• 5 minutes and 10 seconds :-) – Luis Mendo May 7 '17 at 16:37
• I was reading other stuff in the meantime... I'll be faster next time. – Leaky Nun May 7 '17 at 16:37

## C, 99959291 90 bytes

c,l,i,j;f(n){for(j=n;j--;puts(""))for(i=0;i<n;c=!putchar(32|c>j))for(l=++i;l;c+=i%l--<1);}


See it work here.

# Octave, 4140 32 bytes

Thanks to @StewieGriffin saved 8 bytes.

@(N)" #"(sort(~mod(k=1:N,k'))+1)


Try it online!

@(N)" #"(sort(~bsxfun(@mod,k=1:N,k')+1))


Try it online!

@(N)" #"(sort(ismember((k=1:N)./k',k))+1)


Try it online!

Explanation:

N=5;
d = (1:N)./(1:N)'    %divide each of numbers from 1 to N by 1 to N
%a [N by N] matrix created
d =

1.00   2.00   3.00   4.00   5.00
0.50   1.00   1.50   2.00   2.50
0.33   0.66   1.00   1.33   1.66
0.25   0.50   0.75   1.00   1.25
0.20   0.40   0.60   0.80   1.00

m = ismember(d,1:N)      %find divisors of each number
m =

1  1  1  1  1
0  1  0  1  0
0  0  1  0  0
0  0  0  1  0
0  0  0  0  1

idx = sort(m)                  %sort the matrix

idx =

0  0  0  0  0
0  0  0  0  0
0  0  0  1  0
0  1  1  1  1
1  1  1  1  1

" #"(idx+1)     %replace 0 and 1 with ' ' and '#' respectively

#
####
#####

• Very nice approach! – Luis Mendo May 8 '17 at 10:34
• Octave performs singleton expansion implicitly, so @(N)" #"(sort(~mod(k=1:N,k')+1)) saves you a few bytes :) You get a bunch of leading newlines though, I'm not sure what the rules are in that regard. – Stewie Griffin May 8 '17 at 18:22
• Same bytecount (32): @(N)['',35*sort(~mod(k=1:N,k'))]. – Stewie Griffin May 8 '17 at 18:33
• @StewieGriffin Thanks! I didn't aware of that feature . Is mod(1:N,(1:N).')  acceptable in MATLAB? – rahnema1 May 8 '17 at 18:55
• I think it is possible as of R2016b, but I can't test it myself unfortunately, since I don't have it. – Stewie Griffin May 8 '17 at 19:08

f takes an Int and returns a String.

f m|l<-[1..m]=unlines[[last$' ':drop(m-i)['#'|0<-mod n<$>l]|n<-l]|i<-l]


Try it online!

• m is the N of the OP (Haskell variables must be lowercase.)
• The abbreviation l=[1..m] is used in the nested list comprehensions for iterating through all of rows, columns, and potential divisors. This means some extra initial rows filled with whitespace.
• n is column (also number checked), i is row.
• ['#'|0<-mod n<$>l] is a list of '#' characters with length the number of divisors of n. # Octave, 61 bytes for i=1:input(''),p(1:nnz(~rem(i,1:i)),i)=35;end,[flip(p),'']  Explanation: for i=1:input('') % Loop, for i from 1 to the input value p(1:nnz(~rem(i,1:i)),i)=35;end,[flip(p),''] % Breakdown: ~rem(i,1:i) % Return true if the remainder of i divided by any of the values % in the vector 1 - i nnz(~rem(i,1:i)) % Check how many of them are non-zero p(1:nnz(~rem(i,1:i)),i)=35;end % Assign the value 35 (ASCII for #) to rows 1 % to the number of divisors of i, in column i end, % End loop [flip(p),''] % Flip the matrix, and concatenate it with the empty string  A few things I wish to highlight • Takes the input directly into the loop • Doesn't assign the input value to any variable • Doesn't initialize any array • It creates it on the fly, adding columns and rows as necessary • Automatically casts ASCII-value 0 to whitespace (ASCII-32) What happens inside the loop (suppose input 6) p = 35 p = 35 35 0 35 p = 35 35 35 0 35 35 p = 35 35 35 35 0 35 35 35 0 0 0 35 p = 35 35 35 35 35 0 35 35 35 35 0 0 0 35 0 p = 35 35 35 35 35 35 0 35 35 35 35 35 0 0 0 35 0 35 0 0 0 0 0 35  1. Starts of as a single 35 2. Expands one column and one row, to make room for the two divisors of 2 3. Expands one column, to make room for 3 (only two divisors) 4. Expands one column and one row, to make room for the 3 divisors (1,2,4) 5. Expands one column, to make room for 5 6. Expands one column and one row, to make room for the 4 divisors (1,2,3,6) Finally we flip it, and converts it to a string, implicitly changing the 0 to 32: warning: implicit conversion from numeric to char ans = # # # ##### ######  # Python 3, 111 bytes lambda n:'\n'.join([*map(''.join,zip(*['#'*sum(x%-~i==0for i in range(x))+n*' 'for x in range(1,n+1)]))][::-1])  Try it online! This produces some leading vertical whitespace # APL (Dyalog), 19 bytes ⊖⍉↑'#'⍴¨⍨+⌿0=∘.|⍨⍳⎕  Try it online! ⎕ get evaluated input (N) ⍳ 1...N ∘.|⍨ division remainder table with 1...N both as vertical and as horizontal axis 0= where equal to zero (i.e. it divides) +⌿ sum the columns (i.e. gives count of divisors for each number) '#'⍴¨⍨ use each number to reshape the hash character (gives list of strings) ↑ mix (list of strings into table of rows) ⍉ transpose ⊖ flip upside down ## Mathematica, 59 57 bytes Rotate[Grid@Map[X~Table~#&,0~DivisorSigma~Range@#],Pi/2]&  • Welcome to answering on PPCG, fellow Lego minifig :-) – Luis Mendo May 8 '17 at 11:01 • Now there's no turning back... – Luis Mendo May 8 '17 at 11:06 • Welcome! Nice to see another Mathematica golfer. This answer isn't entirely valid though because you've hardcoded the input into the snippet. Answers need to be full programs or callable functions (which may be unnamed though). So this can be fixed at no cost by replacing 50 with # and appending &. You can save some bytes with infix notation as well: X~Table~#& and 0~DivisorSigma~Range@# – Martin Ender May 8 '17 at 11:14 • @MartinEnder Thanks. I forgot that bit when I moved from testing to answering. And thanks for the hint about infixing. Its not something I normally use (as I don't really golf). – Ian Miller May 8 '17 at 11:30 • I thought as much. It was more a tongue in cheek comment. Sorry for the confusion. – Ian Miller May 8 '17 at 11:58 # C#, 333 281 Bytes using System.Linq;using C=System.Console;class P{static void Main(){var n=int.Parse(C.ReadLine());var v=new int[n];for(var k=2;k<=n;k++)for(var i=1;i<k;i++)if(k%i==0)v[k-1]++;for(var i=0;i<v.Length;i++)for(var u=v.Max()-v[i];u<v.Max();u++){C.SetCursorPosition(i,u);C.Write("#");}}}  With line breaks: using System.Linq; using C = System.Console; class P { static void Main() { var n = int.Parse(C.ReadLine()); var v = new int[n]; for (var k = 2; k <= n; k++) for (var i = 1; i < k; i++) if (k % i == 0) v[k - 1]++; for (var i = 0; i < v.Length; i++) for (var u = v.Max() - v[i]; u < v.Max(); u++) { C.SetCursorPosition(i, u); C.Write("#"); } } }  While I'm sure this is possible shorter as well, I hope we'll achieve a shorter solution together ;) Saved 52 Bytes with the help of raznagul • If you use an int-array instead of a list, you can save a lot of bytes from the using-statement. – raznagul May 8 '17 at 15:06 • @raznagul updated it. – MetaColon May 8 '17 at 15:16 • 222 bytes: using System.Linq;using C=System.Console;n=>{var v=new int[n];for(var k=1,i;++k<=n;)for(i=1;i<k;)if(k%i++==0)v[k-1]++;for(var i=0,u;i<v.Length;i++)for(u=v.Max()-v[i];u<v.Max();){C.SetCursorPosition(i, u++);C.Write("#");}}; Compile to an Action, move the increments around and a couple of other minor tweaks. I haven't tested that but it should work. – TheLethalCoder May 9 '17 at 16:21 • @TheLethalCoder I'll test it / update my answer tomorrow. – MetaColon May 9 '17 at 19:08 ## Mathematica, 99 bytes {T=DivisorSigma[0,Range@#];Row[Column/@Table[Join[Table[,Max[T]-T[[i]]],$~Table~T[[i]]],{i,1,#}]]}&


for N=50

• Are all those spaces (and newlines) necessary for the code to run? I've never programming Mathematica myself, but in most languages you could remove almost all of those spaces. – Kevin Cruijssen May 8 '17 at 10:40
• this is my first golf. thanks for the tips – J42161217 May 8 '17 at 22:47
• No problem, and welcome to PPCG! If you haven't yet, you might find Tips for golfing in <all languages> and Tips for golfing in Mathematica interesting to read through. :) Enjoy your stay. – Kevin Cruijssen May 9 '17 at 7:14
• The outer curly braces can be changed to round brackets to prevent them from showing in the output, and you can use some infix/prefix syntax to save 2 bytes: (T=0~DivisorSigma~Range@#;Row[Column/@Table[Join[Table[,Max@T-T[[i]]],$~Table~T[[i]]],{i,1,#}]])& – numbermaniac May 21 '17 at 2:13 # Charcoal, 2322 20 bytes Ｆ…·¹Ｎ«Ｊι⁰Ｆι¿¬﹪ι⁺κ¹↑#  Try it online! Link is to verbose version of code. Edit: Saved 1 byte by looping k from 0 to i-1 and adding 1 inside the loop. Saved a further two bytes by not storing the input in a variable. Explanation: Ｆ…·¹Ｎ for (i : InclusiveRange(1, InputNumber())) « { Ｊι⁰ JumpTo(i, 0); Ｆι for (k : Range(i)) ¿¬﹪ι⁺κ¹ if (!(i % (k + 1))) ↑# Print(:Up, "#"); }  Edit: This 18-byte "one-liner" (link is to verbose version of code) wouldn't have worked with the version of Charcoal at the time the question was submitted: Try it online! ↑Ｅ…·¹Ｎ⪫Ｅι⎇﹪ι⁺¹λω#ω  • – ASCII-only May 10 '17 at 21:52 # 05AB1E, 12 bytes Code: LÑ€g'#×.BøR»  Explanation: L # Create the range 1 .. input Ñ # Get the list of divisors of each €g # Get the length of each element '# # Push a hash tag character × # String multiply .B # Squarify, make them all of equal length by adding spaces ø # Transpose R # Reverse the array » # Join by newlines  Uses the 05AB1E encoding. Try it online! • Can't you use ζ instead of .Bø? Also, the character doesn't have to be # – Oliver Ni Aug 9 '17 at 13:27 • ζ didn't exist back then. – Magic Octopus Urn Aug 9 '17 at 14:37 # Python 2, 101 bytes N=input();r=range for i in r(N,0,-1):print''.join('# '[i>sum(x%-~p<1for p in r(x))]for x in r(1,1+N))  Try it online! This produces (a lot of) vertically leading whitespace. It prints a total of N lines, the great majority of which will typically be blank. # Japt, 34 33 16 14 bytes Saved 17 bytes thanks to @ETHproductions õ@'#pXâ lÃ·z w  Try it online! • You can save a lot of bytes by just letting z do the padding: õ_â lÃ£'#pXÃ·z w – ETHproductions May 8 '17 at 17:05 • Didn't know that padded the strings automatically. Thanks! – Luke May 8 '17 at 18:51 ## J, 28 bytes [:|:&.|.[('#',@$~1+_&q:)@-i.


Defines a monadic verb. Try it online!

## Explanation

[:|:&.|.[('#',@$~1+_&q:)@-i. Input is y. i. Range from 0 to y-1 [ - subtracted from y (gives range from y to 1). ( )@ For each of the numbers: _&q: Compute exponents in prime decomposition, 1+ add 1 to each, '#'$~               make char matrix of #s with those dimensions,
,@                 and flatten into a string.
The resulting 2D char matrix is padded with spaces.
[:|:&.|.                      Reverse, transpose, reverse again.


# PHP, 126 Bytes

for(;$n++<$argn;)for($c=$d=0;$d++<$n;)$n%$d?:$r[$n]=++$c;for($h=max($r);$h--;print"
")for($n=0;$n++<$argn;)echo$h<$r[$n]?:" ";


Try it online!

# Brachylog, 34 bytes

⟦fᵐlᵐgDh⌉⟦↔gᵐ;D↔z{z{>₁∧0ṫ|∧Ṣ}ᵐcẉ}ᵐ


Try it online!

## Alice, 33 bytes

I.!.t&w?t!aot&wh.Bdt.?-ex' +o&;k@


Try it online!

Input is (unfortunately) in the form of a code point. At least it reads a UTF-8 character, so you can use larger inputs than 255, but they're still limited and it's a pretty painful input format. For three additional bytes, we can read a decimal integer:

/
ki@/.!.t&w?t!aot&wh.Bdt.?-ex' +o&;


Try it online!

The non-whitespace character in the output is !.

Note that the solution also prints a ton of leading whitespace (it always starts with an empty line and then prints an NxN grid so for larger N, there will be many lines of spaces before the first !s.)

### Explanation

I've used and explained the &w...k construction before (for example here). It's a neat little idiom that pops an integer n and then runs a piece of code n+1 times (consequently, it's usually used as t&w...k to run a loop n times, with t decrementing the input value). This is done by working with the return address stack (RAS). w pushes the current IP address to the RAS, and if we repeat it with & then the address gets pushed n times. k pops one address from the RAS and jumps back there. If the RAS is empty, it does nothing at all and the loop is exited.

You might notice that it's not trivially possible to nest these loops, because at the end of the inner loop, the stack isn't empty, so the k doesn't become a no-op. Instead, the IP would jump back to the beginning of the outer loop. The general way to fix this involves wrapping the inner loop in its own subroutine. But if we can arrange the nested loop such that the outer loop ends with the inner loop, we can actually make use of this behaviour and even save on one k!

So this construction:

&wX&wYk


Is a working nested loop which runs the XYYYXYYYXYYY... (for some number of Ys in each iteration). It's pretty neat that we can end both loops with a single k, because it will consume an outer address from RAS each time the inner addresses have been depleted.

This idiom is used in the program to run the loop over the output grid.

I         Read a character and push its code point as input N.
.!        Store a copy on the tape.
.         Make another copy.
t&w       Run this outer loop N times. This loops over the lines of the
output, so the current iteration corresponds to a divisor count
no more than i (i counting down from N).
?t!       Decrement the value on the tape. That means we'll actually have
i-1 on the tape during the iteration.
ao        Print a linefeed. Doing this now leads to the weird leading linefeed,
but it's necessary for the &w...&w...k pattern.
t&w       Remember that we still have a copy of N on the stack. We'll also
ensure that this is the case after each loop iteration. So this
also runs the inner loop N times. This iterates over the columns
of the output grid so it's j that goes from 1 to N and corresponds
to the number whose divisors we want to visualise.
At this point the stack will always be empty. However, operating
on an empty stack works with implicit zeros at the bottom of
the stack. We'll use the top zero to keep track of j.
h         Increment j.
.B        Duplicate j and push all of its divisors.
dt        Push the stack depth minus 1, i.e. the divisor count of j.
.         Duplicate the divisor count.
?-        Retrieve i-1 from the tape and subtract it. So we're computing
d(j)-i+1. We want to output a non-whitespace character iff this
value is positive (i.e. if i is no greater than d(j)).
ex        Extract the most significant bit of this value. This is 1 for
all positive values and 0 for non-positive ones. It's the shortest
way (I believe) to determine if a value is positive.
' +       Add this to the code point of a space. This gives a 33, or '!',
if the cell is part of the skyline.
o         Output the character.
&;        We still have all the divisors and the divisor count on the stack.
We pop the divisor count to discard that many values (i.e. to
get rid of the divisors again).
k         Return to the appropriate loop beginning to run the continue the
nested loop.
@         Terminate the program.

• First Alice program with a single line ever? :-) – Luis Mendo May 7 '17 at 20:19
• @LuisMendo No, I think Leo has written a few Cardinal-only programs (and maybe I have as well... the quine and the Hello, World for example). Probably the most elaborate single-line program though. :) – Martin Ender May 7 '17 at 20:20
• Hmm, my solution could save some bytes with "ton of leading whitespace" trick – quintopia May 8 '17 at 16:51

# Actually, 25 bytes

R♂÷♂l;M' *╗⌠'#*╜@+⌡M┬♂ΣRi


Try it online!

## 22-byte version with a lot of leading newlines

;╗R⌠÷l'#*╜' *@+⌡M┬♂ΣRi


Try it online!

• Okay, the Readme on the github should be updated to include ÷l as the standard divisor counting function... – quintopia May 8 '17 at 17:03

# R, 83 82 bytes

-1 byte thanks to MickyT

N=scan();m=matrix('#',N,N);for(i in 1:N)m[i,1:sum(i%%1:N>0)]=' ';write(m,'',N,,'')


Reads N from stdin.

N=scan()                        # read N
m=matrix('#',N,N)               # make an NxN matrix of '#' characters
for(i in 1:N)                   # for each integer in the range
m[i,1:sum(i%%1:N!=0)]=' '   # set the first n-d(n) columns of row i to ' '
write(m,'',N,,'')               # print to console with appropriate formatting


Try it online!

• !=0 can be >0 – MickyT May 10 '17 at 22:47

# Pyth, 16 bytes

j_.tm*\#/%LdSQ0S


Try it online!

# SpecBAS - 149 bytes

1 INPUT n: DIM h(n)
2 FOR i=1 TO n
3 FOR j=1 TO i
4 h(i)+=(i MOD j=0)
5 NEXT j
6 NEXT i
7 FOR i=1 TO n
8 FOR j=51-h(i) TO 50
9  ?AT j,i;"#"
10 NEXT j
11 NEXT i


An array keeps track of number of divisors, then prints the right number of characters down to screen position 50.

# PHP, 99 bytes

for(;$d<$k?:$k++<$argn+$d=$n=0;)$k%++$d?:$r[--$n]=str_pad($r[$n],$k).H;ksort($r);echo join("
",$r);  prints one leading space; run as pipe with php -nr '<code>' or try it online. breakdown for(;$d<$k?: # inner: loop$d from 1 to $k$k++<$argn+$d=$n=0;) # outer: loop$k from 1 to $argn, reset$d and $n$k%++$d?: # if$d divides $k$r[--$n]=str_pad($r[$n],$k).H;  # add one store to building $k ksort($r);echo join("\n",$r); # reverse and print resulting array  # PowerShell, 101 bytes ((($d=1.."$args"|%{($x=$_)-(1..$_|?{$x%$_}).Count})|sort)[-1])..1|%{$l=$_;-join($d|%{' #'[$_-ge$l]})}  Less golfed test script: $f = {

$d=1.."$args"|%{
($x=$_)-(1..$_|?{$x%$_}).Count } #$d is the Divisor count array
$maxHeight=($d|sort)[-1]
$maxHeight..1|%{$l=$_ -join($d|%{' #'[$_-ge$l]})
}

}

@(
,(10,
"     # # #",
"   # # ###",
" #########",
"##########")

,(50,
"                                               #  ",
"                                   #           #  ",
"                       #     #     #   # #     #  ",
"                       #     #     #   # #     #  ",
"           #     # #   #   # # #   #   # # ##  # #",
"           #   # # #   #   # # #   #   # # ##  # #",
"     # # # # ### # ### # ### # ##### ### # ### # #",
"   # # ### # ### # ### ##### # ##### ### # ### ###",
" #################################################",
"##################################################")

,(200,
"                                                                                                                                                                                   #                    ",
"                                                                                                                                                                                   #                    ",
"                                                                                                                       #                                               #           #                    ",
"                                                                                                                       #                       #                       #           #                    ",
"                                                                                                                       #                       #                       #           #           #        ",
"                                                                                                                       #                       #                       #           #           #        ",
"                                                           #           #           #     #     #           #           #     #     #       #   #     #     #   #       #           #           #     # #",
"                                                           #           #           #     #     #           #           #     #     #       #   #     #     #   #       #           #           #     # #",
"                                               #           #           #       #   #     #     #           #   #       #     #     #       #   #     #     #   # #     #       #   #           #     # #",
"                                   #           #           #           #       #   #     #     #   #       #   #       #     #     #       #   #     #     #   # #     #       #   #           #   # # #",
"                       #     #     #   # #     #     # #   #     #   # #     # #   #   # #     #   # # ##  # # # #     #     # # # #  ## # #   #     # # # #   # #  #  # #   # #   # # # #  ## #  ## # #",
"                       #     #     #   # #     #     # #   #   # #   # #     # #   #   # #     #   # # ##  # # # #     #     # # # #  ## # #   #     # # # #   # #  #  # #   # #   # # # #  ## #  ## # #",
"           #     # #   #   # # #   #   # # ##  # # # # #   #  ## # # # #  ## # #   #   # # #   # ### # ##  # # # # ##  #   # # # # #  ## # #   #  ## # ### #   # # ##  # ### ###   # # # # ### #  ## # #",
"           #   # # #   #   # # #   #   # # ##  # # # # #   #  ## # # # #  ## # ##  #   # # #   # ### # ##  # # # # ##  #   # # # # #  ## # #   #  ## # ### #   # # ##  # ### ###   # # # # ### #  ## # #",
"     # # # # ### # ### # ### # ##### ### # ### # ### ##### # ##### ### # ##### ### ##### ####### ### # ### # ### ####### ##### ### ##### # ######### # ##### ##### ### # ### ##### # ######### # ### # #",
"   # # ### # ### # ### ##### # ##### ### # ### ##### ##### # ##### ### # ##### ### ##### ####### ### # ### # ### ############# ### ##### # ######### # ##### ##### ### ##### ##### # ######### # ### # #",
" #######################################################################################################################################################################################################",
"########################################################################################################################################################################################################")

) | % {
$n,$expected = $_$result = &$f$n
"$result"-eq"$expected"
#\$result    # uncomment this to enjoy the view of the Divisor skyline
}


Output:

True
True
True


# SOGL V0.12, 8 bytes

∫Λ⁄╗*}⁰H


Try it here!

# Wolfram Language (Mathematica), 46 44 bytes

Grid[PadLeft[0Divisors@Range@#-" "]+" "]&


Try it online! But maybe try it online! with ColumnForm instead of Grid, since Grid doesn't work in TIO. In Mathematica, it looks better:

A third Mathematica solution... Divisors@Range@# finds all the divisors in the range we want, and then we multiply by 0 and subtract " ", making every divisor equal to -" ".

PadLeft adds zeroes on the left, creating a sideways skyline, whose orientation we fix with =\[Transpose]. Finally, adding " " to everything makes all entries either 0 or " ".

As an alternative, the 59-byte ""<>Riffle[PadLeft["X"-" "+0Divisors@Range@#]+" ","\n"]& produces string output.