membangun sirkuit untuk dibagi oleh 3

12

Sebuah sirkuit boolean di TCS pada dasarnya adalah DAG terdiri dari Dan, Atau, Tidak gerbang, dan dengan apa yang diketahui adalah "kelengkapan fungsional" mereka dapat menghitung semua fungsi mungkin. misalnya ini adalah prinsip dasar dalam ALU .

Tantangan: buat sirkuit untuk menentukan apakah angka 8-biner-digit dapat dibagi oleh 3 dan entah bagaimana memvisualisasikan hasil Anda (yaitu dalam beberapa jenis gambar)

Kriteria penilaian untuk pemilih didasarkan pada apakah kode untuk menghasilkan rangkaian digeneralisasikan dengan baik ke angka ukuran yang sewenang-wenang, dan apakah visualisasi yang dibuat secara algoritmik kompak / seimbang tetapi masih dapat dibaca oleh manusia (yaitu visualisasi dengan pengaturan tangan tidak diperbolehkan). yaitu visualisasi hanya untuk n = 8 tetapi kode idealnya akan bekerja untuk semua 'n'. entri pemenang baru saja terpilih.

Pertanyaan serupa: Bangun mesin pengganda menggunakan gerbang logika NAND

vzn
sumber
2
Jauh lebih baik. Namun "generalisasi" dan "estetika" tidak objektif. Semua pertanyaan harus memiliki kriteria kemenangan yang objektif. Jika Anda ingin menerapkan karakteristik itu, gunakan kontes popularitas. Jika Anda menginginkan kode terpendek, gunakan kode-golf. Jika Anda ingin membuat kombinasi keduanya, gunakan tantangan kode, tetapi tentukan formula. Misalnya 1.25 * suara - panjang 0,25 * seperti pertanyaan ini: codegolf.stackexchange.com/questions/23581/eiffel-tower-in-3d/…
Level River St
ok buat chgs yang kamu minta. terima kasih untuk umpan balik.
vzn
Oghh, saya kira VHDL atau Verilog yang dikompilasi setelah semua optimasinya harus memberikan jawaban terpendek. Saya akan mencobanya nanti.
Kirill Kulakov
1
Kriteria pemenang yang lebih baik adalah gate-golf
TheDoctor
@Dokter ??? apa gate-golf? tag itu tidak ada. catatan untuk peserta: sebutkan bahasa / alat visualisasi apa yang Anda gunakan. jika orang lain ingin memasukkan komentar ttg. jika tidak, akan menerima tonite pemenang. Terima kasih banyak kepada responden sejauh ini "BTE" lebih baik dari yang diharapkan!
vzn

Jawaban:

7

sirkuit untuk menghitung angka modulo 3

Grafik mempertahankan 3 boolean di setiap level i. Mereka mewakili fakta bahwa bit i tingkat tinggi dari angka tersebut sama dengan 0, 1, atau 2 mod 3. Pada setiap level kita menghitung tiga bit berikutnya berdasarkan pada tiga bit sebelumnya dan bit input berikutnya.

Inilah kode python yang menghasilkan grafik. Ubah saja N untuk mendapatkan jumlah bit yang berbeda, atau K untuk mendapatkan modulus yang berbeda. Jalankan output dari program python melalui titik untuk menghasilkan gambar.

N = 8
K = 3
v = ['0']*(K-1) + ['1']
ops = {}

ops['0'] = ['0']
ops['1'] = ['1']
v = ['0']*(K-1) + ['1']
for i in xrange(N):
  ops['bit%d'%i] = ['bit%d'%i]
  ops['not%d'%i] = ['not','bit%d'%i]
  for j in xrange(K):
    ops['a%d_%d'%(i,j)] = ['and','not%d'%i,v[(2*j)%K]]
    ops['b%d_%d'%(i,j)] = ['and','bit%d'%i,v[(2*j+1)%K]]
    ops['o%d_%d'%(i,j)] = ['or','a%d_%d'%(i,j),'b%d_%d'%(i,j)]
  v = ['o%d_%d'%(i,j) for j in xrange(K)]

for i in xrange(4):
  for n,op in ops.items():
    for j,a in enumerate(op[1:]):
      if ops[a][0]=='and' and ops[a][1]=='0': op[j+1]='0'
      if ops[a][0]=='and' and ops[a][2]=='0': op[j+1]='0'
      if ops[a][0]=='and' and ops[a][1]=='1': op[j+1]=ops[a][2]
      if ops[a][0]=='and' and ops[a][2]=='1': op[j+1]=ops[a][1]
      if ops[a][0]=='or' and ops[a][1]=='0': op[j+1]=ops[a][2]
      if ops[a][0]=='or' and ops[a][2]=='0': op[j+1]=ops[a][1]

for i in xrange(4):
  used = set(['o%d_0'%(N-1)])|set(a for n,op in ops.items() for a in op[1:])
  for n,op in ops.items():
    if n not in used: del ops[n]

print 'digraph {'
for n,op in ops.items():
  if op[0]=='and': print '%s [shape=invhouse]' % n
  if op[0]=='or': print '%s [shape=circle]' % n
  if op[0]=='not': print '%s [shape=invtriangle]' % n
  if op[0].startswith('bit'): print '%s [color=red]' % n
  print '%s [label=%s]' % (n,op[0])
  for a in op[1:]: print '%s -> %s' % (a,n)
print '}'
Keith Randall
sumber
Bagus! telah menggunakan graphviz juga ... berdalih kecil, ada AND / OR yang tidak digunakan dalam diagram. juga menyarankan untuk menyorot bit input dalam warna berbeda untuk menunjukkan lokasi mereka
vzn
@ vzn: Oke, sudah diperbaiki.
Keith Randall
12

Kedalaman: 7 (logaritmik), 18x AND, 6x OR, 7x XOR, 31 gerbang (linier)

Biarkan saya menghitung jumlah digit dalam basis empat, modulo tiga:

Sirkuit 7-layer dengan struktur hierarkis yang terlihat jelas

sirkuit digambar dalam Logisim

Generalisasi, secara formal (semoga agak mudah dibaca):

balance (l, h) = {
  is1: l & not h,
  is2: h & not l,
}

add (a, b) = 
  let aa = balance (a.l, a.h)
      bb = balance (b.l, b.h)
  in  { l:(a.is2 & b.is2) | (a.is1 ^ b.is1),
        h:(a.is1 & b.is1) | (a.is2 ^ b.is2)}

pairs [] = []
pairs [a] = [{h:0, l:a}]
pairs [rest.., a, b] = [pairs(rest..).., {h:a, l:b}]

mod3 [p] = p
mod3 [rest.., p1, p2] = [add(p1, p2), rest..]

divisible3 number =
  let {l: l, h: h} = mod3 $ pairs number
  in  l == h

sekarang dalam bahasa Inggris:

Sementara ada lebih dari dua bit dalam angka, ambil dua pasang bit terendah dan jumlah mereka modulo 3, kemudian tambahkan hasilnya ke belakang angka, kemudian kembali jika pasangan terakhir adalah nol modulo 3. Jika ada yang aneh jumlah bit dalam jumlah, tambahkan bit nol ekstra ke atas, lalu poles dengan propagasi nilai konstan.

Menambahkan ke belakang alih-alih ke depan memastikan pohon tambahan adalah pohon seimbang daripada daftar tertaut. Ini, pada gilirannya, memastikan kedalaman logaritmik dalam jumlah bit: lima gerbang dan tiga tingkat untuk pembatalan pasangan, dan gerbang tambahan di ujungnya.

Tentu saja, jika perkiraan planaritas diinginkan, berikan pasangan atas yang tidak dimodifikasi ke lapisan berikutnya alih-alih membungkusnya ke depan. Namun ini lebih mudah diucapkan daripada diimplementasikan (bahkan dalam pseudocode). Jika jumlah bit dalam angka adalah kekuatan dua (seperti yang berlaku di sistem komputer modern apa pun pada Maret 2014), tidak ada pasangan bebas akan terjadi.

Jika tata letak mempertahankan lokalitas / melakukan minimalisasi panjang rute, itu harus membuat sirkuit terbaca.

Kode Ruby ini akan menghasilkan diagram sirkuit untuk sejumlah bit (bahkan satu bit). Untuk mencetak, buka di Logisim dan ekspor sebagai gambar:

require "nokogiri"

Port = Struct.new :x, :y, :out
Gate = Struct.new :x, :y, :name, :attrs
Wire = Struct.new :sx, :sy, :tx, :ty

puts "Please choose the number of bits: "
bits = gets.to_i

$ports = (1..bits).map {|x| Port.new 60*x, 40, false};
$wires = [];
$gates = [];

toMerge = $ports.reverse;

def balance a, b
y = [a.y, b.y].max
$wires.push Wire.new(a.x   , a.y , a.x   , y+20),
          Wire.new(a.x   , y+20, a.x   , y+40),
          Wire.new(a.x   , y+20, b.x-20, y+20),
          Wire.new(b.x-20, y+20, b.x-20, y+30),
          Wire.new(b.x   , b.y , b.x   , y+10),
          Wire.new(b.x   , y+10, b.x   , y+40),
          Wire.new(b.x   , y+10, a.x+20, y+10),
          Wire.new(a.x+20, y+10, a.x+20, y+30)
$gates.push Gate.new(a.x+10, y+70, "AND Gate", negate1: true),
          Gate.new(b.x-10, y+70, "AND Gate", negate0: true)
end

def sum (a, b, c, d)
y = [a.y, b.y, c.y, d.y].max
$wires.push Wire.new(a.x   , a.y , a.x   , y+40),
          Wire.new(a.x   , y+40, a.x   , y+50),
          Wire.new(a.x   , y+40, c.x-20, y+40),
          Wire.new(c.x-20, y+40, c.x-20, y+50),
          Wire.new(b.x   , b.y , b.x   , y+30),
          Wire.new(b.x   , y+30, b.x   , y+50),
          Wire.new(b.x   , y+30, d.x-20, y+30),
          Wire.new(d.x-20, y+30, d.x-20, y+50),
          Wire.new(c.x   , c.y , c.x   , y+20),
          Wire.new(c.x   , y+20, c.x   , y+50),
          Wire.new(c.x   , y+20, a.x+20, y+20),
          Wire.new(a.x+20, y+20, a.x+20, y+50),
          Wire.new(d.x   , d.y , d.x   , y+10),
          Wire.new(d.x   , y+10, d.x   , y+50),
          Wire.new(d.x   , y+10, b.x+20, y+10),
          Wire.new(b.x+20, y+10, b.x+20, y+50)
$gates.push Gate.new(a.x+10, y+90, "XOR Gate"),
          Gate.new(b.x+10, y+80, "AND Gate"),
          Gate.new(c.x-10, y+80, "AND Gate"),
          Gate.new(d.x-10, y+90, "XOR Gate")
$wires.push Wire.new(a.x+10, y+90, a.x+10, y+100),
          Wire.new(b.x+10, y+80, b.x+10, y+90 ),
          Wire.new(b.x+10, y+90, a.x+30, y+90 ),
          Wire.new(a.x+30, y+90, a.x+30, y+100),
          Wire.new(d.x-10, y+90, d.x-10, y+100),
          Wire.new(c.x-10, y+80, c.x-10, y+90 ),
          Wire.new(c.x-10, y+90, d.x-30, y+90 ),
          Wire.new(d.x-30, y+90, d.x-30, y+100)
$gates.push Gate.new(d.x-20, y+130, "OR Gate"),
          Gate.new(a.x+20, y+130, "OR Gate")
end

def sum3 (b, c, d)
y = [b.y, c.y, d.y].max
$wires.push Wire.new(b.x   , b.y , b.x   , y+20),
          Wire.new(b.x   , y+20, b.x   , y+30),
          Wire.new(b.x   , y+20, d.x-20, y+20),
          Wire.new(d.x-20, y+20, d.x-20, y+30),
          Wire.new(c.x   , c.y , c.x   , y+60),
          Wire.new(c.x   , y+60, b.x+30, y+60),
          Wire.new(b.x+30, y+60, b.x+30, y+70),
          Wire.new(d.x   , d.y , d.x   , y+10),
          Wire.new(d.x   , y+10, d.x   , y+30),
          Wire.new(d.x   , y+10, b.x+20, y+10),
          Wire.new(b.x+20, y+10, b.x+20, y+30),
          Wire.new(b.x+10, y+60, b.x+10, y+70)
$gates.push Gate.new(b.x+10, y+60 , "AND Gate"),
          Gate.new(d.x-10, y+70 , "XOR Gate"),
          Gate.new(b.x+20, y+100, "OR Gate" )
end

while toMerge.count > 2  
puts "#{toMerge.count} left to merge"
nextToMerge = []
while toMerge.count > 3
 puts "merging four"
 d, c, b, a, *toMerge = toMerge
 balance a, b
 balance c, d
 sum *$gates[-4..-1]
 nextToMerge.push *$gates[-2..-1] 
end
if toMerge.count == 3
 puts "merging three"
 c, b, a, *toMerge = toMerge
 balance b, c
 sum3 a, *$gates[-2..-1]
 nextToMerge.push *$gates[-2..-1]
end
nextToMerge.push *toMerge
toMerge = nextToMerge
puts "layer done"
end

if toMerge.count == 2
b, a = toMerge
x = (a.x + b.x)/2
x -= x % 10
y = [a.y, b.y].max
$wires.push Wire.new(a.x , a.y , a.x , y+10),
          Wire.new(a.x , y+10, x-10, y+10),
          Wire.new(x-10, y+10, x-10, y+20),
          Wire.new(b.x , b.y , b.x , y+10),
          Wire.new(b.x , y+10, x+10, y+10),
          Wire.new(x+10, y+10, x+10, y+20)
$gates.push Gate.new(x, y+70, "XNOR Gate")
toMerge = [$gates[-1]]
end

a = toMerge[0]
$wires.push Wire.new(a.x, a.y, a.x, a.y+10)
$ports.push Port.new(a.x, a.y+10, true)

def xy (x, y)
"(#{x},#{y})"
end
circ = Nokogiri::XML::Builder.new encoding: "UTF-8" do |xml|
xml.project version: "1.0" do
xml.lib name: "0", desc: "#Base"
xml.lib name: "1", desc: "#Wiring"
xml.lib name: "2", desc: "#Gates"
xml.options
xml.mappings
xml.toolbar do
  xml.tool lib:'0', name: "Poke Tool"
  xml.tool lib:'0', name: "Edit Tool"
end #toolbar
xml.main name: "main"
xml.circuit name: "main" do
  $wires.each do |wire|
    xml.wire from: xy(wire.sx, wire.sy), to: xy(wire.tx, wire.ty)
  end #each 
  $gates.each do |gate|
    xml.comp lib: "2", name: gate.name, loc: xy(gate.x, gate.y) do
      xml.a name: "facing", val: "south"
      xml.a name: "size", val: "30"
      xml.a name: "inputs", val: "2"
      if gate.attrs
        gate.attrs.each do |name, value|
          xml.a name: name, val: value 
        end #each
      end #if
    end #comp
  end #each
  $ports.each.with_index do |port, index|
    xml.comp lib: "1", name: "Pin", loc: xy(port.x, port.y) do
      xml.a name: "tristate", val: "false"
      xml.a name: "output",   val: port.out.to_s
      xml.a name: "facing",   val: port.out ? "north" : "south"
      xml.a name: "labelloc", val: port.out ? "south" : "north"
      xml.a name: "label",    val: port.out ? "out" : "B#{index}"
    end #port
  end #each
end #circuit
end #project
end #builder

File.open "divisibility3.circ", ?w do |file|
file << circ.to_xml
end

puts "done"

akhirnya, ketika diminta untuk membuat output untuk 32 bit, layouter saya menghasilkan ini. Memang, ini tidak terlalu kompak untuk input yang sangat luas:

13-layer monstrositas dengan banyak ruang terbuang

John Dvorak
sumber
terlihat sangat bagus & sirkuit / tata letak terbaik sejauh ini. dalam bahasa apa kode ini? layouter apa yang Anda gunakan jika ada? layouter diminta menjadi sebuah algoritma, dan harus mengasumsikan bahwa algoritma tidak digunakan (tata letak tangan) kecuali dinyatakan ...
vzn
@vz penata letak harus diimplementasikan juga? Saya memahami batasan itu sebagai arti bahwa kita dapat menggambar diagram secara manual, tetapi keterbacaan tidak harus bergantung pada cara diagram itu digambar. Sirkuit TimWolla jelas buatan tangan. Kode pseudo sebagian besar didasarkan pada Haskell dengan objek Javascripty ditambahkan.
John Dvorak
visualisasi yang dibuat secara algoritmik dimaksudkan untuk pada dasarnya berarti tata letak algoritmik tetapi sekarang mengakui bahwa bisa ditafsirkan secara ambigu. maaf karena kurang jelasnya kristal. apakah Anda tahu ada tata letak otomatis yang bisa mendapatkan hasil yang hampir mirip dengan tata letak tangan Anda?
vzn
Sayangnya tidak ada. yEd memiliki tata letak yang bagus tetapi mengabaikan orientasi. Saya tidak pernah terbiasa dengan dot atau saya menemukan outputnya persis bagus. Saya kira saya bisa menerjemahkan pseudocode ini ke Ruby (mudah) dan menulis layouter khusus saya sendiri (tidak terlalu keras, tetapi kompleks) yang akan mengekspor rangkaian logisim (itu hanya XML, dan itu bahkan tidak gzip, jadi cukup mudah). Haruskah saya (saya mau), dan haruskah saya mempostingnya sebagai jawaban terpisah? Juga, apakah itu akan dianggap sebagai desain tangan?
John Dvorak
Semua jawaban bagus, tapi sejauh ini sepertinya yang paling elegan
Digital Trauma
5

2 × 24 BUKAN, 2 × 10 + 5 DAN, 2 × 2 + 5 ATAU, 2 × 2 NOR

Ini sama sekali tidak berskala. Seperti tidak sama sekali. Mungkin saya akan berusaha memperbaikinya.

Saya memang menguji ini untuk angka hingga 30 dan itu bekerja dengan baik.

Kedua sirkuit besar tersebut menghitung jumlah input aktif:

  • Yang kanan atas menghitung jumlah bit dengan kekuatan genap (nol hingga 4)
  • Kiri bawah menghitung jumlah bit dengan kekuatan aneh (nol hingga 4)

Jika selisih dari angka-angka itu adalah 0atau 3jumlahnya dapat dibagi dengan 3. Rangkaian kanan bawah pada dasarnya memetakan setiap kombinasi yang valid ( 4,4, 4,1, 3,3, 3,0, 2, 2, 1, 1, 0, 0) ke dalam atau.

Lingkaran kecil di tengah adalah LED yang menyala jika jumlahnya jika habis dibagi 3 dan mematikan sebaliknya.

TimWolla
sumber
wow bagus / cepat! ... tlg taruh tautan ke kode atau sebariskan ... juga detail bagaimana Anda melakukan visualisasi ...?
vzn
@vzn Visualisasi dibuat dengan logisim . Itu dibangun tangan saya, tetapi algoritma umum dapat dengan mudah dilakukan dengan suatu program juga. Sebagian dijelaskan dalam jawaban.
TimWolla