Haskell , 166 154 byte
(-12 byte terima kasih kepada Laikoni, (pemahaman zip dan daftar alih-alih zipWith dan lambda, cara yang lebih baik untuk menghasilkan baris pertama))
i#n|let k!p=p:(k+1)![m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))|(l,m,r)<-zip3(1:p)p$tail p++[1]];x=1<$[2..2^n]=mapM(putStrLn.map("M "!!))$take(2^n)$1!(x++0:x)
Cobalah online!
Penjelasan:
Fungsi ini i#n
menggambar ASCII-Triangle dengan ketinggian 2^n
setelah i
langkah-langkah iterasi.
Pengkodean yang digunakan secara internal mengkodekan posisi kosong 1
dan posisi penuh sebagai 0
. Oleh karena itu, baris pertama dari segitiga dikodekan sebagai[1,1,1..0..1,1,1]
dengan 2^n-1
orang-orang di kedua sisi nol. Untuk membangun daftar ini, kita mulai dengan daftar x=1<$[2..2^n]
, yaitu daftar [2..2^n]
dengan semua yang dipetakan 1
. Kemudian, kami membangun daftar lengkap sebagaix++0:x
Operator k!p
(penjelasan terperinci di bawah), diberi indeks garis k
dan yang sesuai p
menghasilkan daftar garis tak terhingga yang mengikuti p
. Kami memohonnya dengan 1
dan garis awal yang dijelaskan di atas untuk mendapatkan seluruh segitiga, dan kemudian hanya mengambil 2^n
garis pertama . Kemudian, kami cukup mencetak setiap baris, mengganti1
dengan spasi dan 0
dengan M
(dengan mengakses daftar "M "
di lokasi 0
atau 1
).
Operator k!p
didefinisikan sebagai berikut:
k!p=p:(k+1)![m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))|(l,m,r)<-zip3(1:p)p$tail p++[1]]
Pertama, kami menghasilkan tiga versi p
: 1:p
yang p
dengan 1
prepended, p
sendiri dan tail p++[1]
yang semuanya kecuali elemen pertama p
, dengan yang 1
ditambahkan. Kami kemudian zip tiga daftar ini, memberi kami secara efektif semua elemen p
dengan tetangga kiri dan kanan mereka, sebagai (l,m,r)
. Kami menggunakan pemahaman daftar untuk kemudian menghitung nilai yang sesuai di baris baru:
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
Untuk memahami ungkapan ini, kita perlu menyadari ada dua kasus dasar untuk dipertimbangkan: Entah kita cukup memperluas baris sebelumnya, atau kita berada pada titik di mana titik kosong dalam segitiga dimulai. Dalam kasus pertama, kami memiliki tempat yang penuh jika ada tempat di lingkungan tersebut yang terisi. Ini dapat dihitung sebagaim*l*r
; jika salah satu dari ketiganya adalah nol, maka nilai yang baru adalah nol. Kasus lainnya agak rumit. Di sini, kita pada dasarnya membutuhkan deteksi tepi. Tabel berikut memberikan delapan lingkungan yang memungkinkan dengan nilai yang dihasilkan di baris baru:
000 001 010 011 100 101 110 111
1 1 1 0 1 1 0 1
Rumus sederhana untuk menghasilkan tabel ini adalah 1-m*r*(1-l)-m*l*(1-r)
yang disederhanakan m*(2*l*r-l-r)+1
. Sekarang kita harus memilih di antara dua kasus ini, di mana kita menggunakan nomor baris k
. Jika mod k (2^(n-i)) == 0
, kita harus menggunakan case kedua, jika tidak, kita menggunakan case pertama. Karena 0^(mod k(2^n-i))
itu istilahnya adalah 0
jika kita harus menggunakan kasus pertama dan1
jika kita harus menggunakan case kedua. Hasilnya, kita bisa menggunakan
m*l*r+(m*(l*r-l-r)+1)*0^mod k(2^(n-i))
total - jika kita menggunakan kasus pertama, kita cukup mendapatkan m*l*r
, sementara dalam kasus kedua, istilah tambahan ditambahkan, memberikan total keseluruhan m*(2*l*r-l-r)+1
.