xây dựng mạch cho chia hết


12

Một mạch boolean trong TCS về cơ bản là một DAG bao gồm các cổng And, Hoặc, Không, và bằng cái được gọi là "tính đầy đủ chức năng", chúng có thể tính toán tất cả các chức năng có thể. ví dụ: đây là nguyên tắc cơ bản trong ALU .

Thách thức: tạo một mạch để xác định xem một số có 8 chữ số có chia hết cho 3 hay không và bằng cách nào đó hình dung ra kết quả của bạn (tức là trong một số loại hình ảnh)

Các tiêu chí đánh giá cho cử tri dựa trên việc liệu mã để tạo ra mạch có khái quát hóa các số kích thước tùy ý hay không và liệu hình ảnh được tạo bằng thuật toán có nhỏ gọn / cân bằng nhưng vẫn có thể đọc được bằng con người (nghĩa là không thể cho phép hiển thị bằng tay). tức là trực quan hóa chỉ dành cho n = 8 nhưng mã sẽ hoạt động lý tưởng cho tất cả 'n'. mục chiến thắng chỉ là bình chọn hàng đầu.

Một số câu hỏi tương tự: Xây dựng một máy nhân bằng cổng logic NAND


2
Tốt hơn nhiều. Tuy nhiên "khái quát hóa" và "thẩm mỹ" là không khách quan. Tất cả các câu hỏi phải có một tiêu chí chiến thắng khách quan. Nếu bạn muốn áp dụng những đặc điểm đó, hãy sử dụng cuộc thi phổ biến. Nếu bạn muốn mã ngắn nhất, hãy sử dụng mã golf. Nếu bạn muốn tạo sự kết hợp của cả hai, hãy sử dụng thử thách mã, nhưng chỉ định một công thức. Ví dụ 1,25 * phiếu - 0.25 * chiều dài như câu hỏi này không: codegolf.stackexchange.com/questions/23581/eiffel-tower-in-3d/...
Cấp sông St

ok làm cho những chks bạn yêu cầu. thx cho phản hồi.
vzn

Oghh, tôi đoán VHDL hoặc Verilog đã biên dịch sau khi tất cả các tối ưu hóa của nó sẽ đưa ra câu trả lời ngắn nhất. Tôi sẽ thử nó sau.
Kirill Kulakov

1
Một tiêu chí chiến thắng tốt hơn sẽ là môn đánh gôn
TheDoctor

@Bác sĩ ??? là gate-golfgì thẻ đó không tồn tại. lưu ý cho người tham gia: vui lòng cho biết bạn đang sử dụng ngôn ngữ / công cụ trực quan nào. nếu ai khác muốn nhập bình luận. nếu không sẽ chấp nhận người chiến thắng tonite. Thx nhiều cho người trả lời cho đến nay điều này đã đi "BTE" tốt hơn mong đợi!
vzn

Câu trả lời:


7

mạch để tính số modulo 3

Biểu đồ duy trì 3 booleans ở mỗi cấp i. Chúng đại diện cho thực tế là các bit i thứ tự cao của số bằng 0, 1 hoặc 2 mod 3. Ở mỗi cấp, chúng tôi tính ba bit tiếp theo dựa trên ba bit trước và bit đầu vào tiếp theo.

Đây là mã python đã tạo ra biểu đồ. Chỉ cần thay đổi N để có số bit khác nhau hoặc K để có mô-đun khác. Chạy đầu ra của chương trình python thông qua dấu chấm để tạo hình ảnh.

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 '}'

tuyệt quá! cũng đã sử dụng graphviz ... phân minh nhỏ, có các AND / OR không được sử dụng trong sơ đồ. cũng đề xuất có thể làm nổi bật các bit đầu vào bằng màu khác nhau để hiển thị vị trí của chúng
vzn

@vzn: Ok, đã sửa.
Keith Randall

12

Độ sâu: 7 (logarit), 18x AND, 6x OR, 7x XOR, 31 cổng (tuyến tính)

Hãy để tôi tính tổng chữ số trong cơ sở bốn, modulo ba:

Mạch 7 lớp với cấu trúc phân cấp rõ ràng

mạch được vẽ trong Logisim

Khái quát hóa, chính thức (hy vọng phần nào có thể đọc được):

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

bây giờ bằng tiếng Anh:

Trong khi có nhiều hơn hai bit trong số, hãy lấy hai cặp bit thấp nhất và tính tổng modulo 3, sau đó nối kết quả vào mặt sau của số, sau đó trả về nếu cặp cuối cùng bằng 0 modulo 3. Nếu có số lẻ số bit trong số, thêm một bit 0 vào đầu, sau đó đánh bóng với giá trị lan truyền không đổi.

Gắn vào mặt sau thay vì phía trước đảm bảo cây bổ sung là cây cân bằng chứ không phải là danh sách được liên kết. Điều này, đến lượt nó, đảm bảo độ sâu logarit về số lượng bit: năm cổng và ba cấp độ để hủy cặp và một cổng phụ ở cuối.

Tất nhiên, nếu muốn có độ phẳng gần đúng, hãy chuyển cặp trên cùng không được sửa đổi sang lớp tiếp theo thay vì bọc nó ở phía trước. Điều này nói dễ hơn thực hiện (ngay cả trong mã giả), tuy nhiên. Tuy nhiên, nếu số bit trong một số là lũy thừa của hai (như đúng trong bất kỳ hệ thống máy tính hiện đại nào kể từ tháng 3 năm 2014), tuy nhiên sẽ không có cặp đơn độc nào xảy ra.

Nếu layouter bảo tồn cục bộ / thực hiện giảm thiểu chiều dài tuyến đường, thì nó sẽ giữ cho mạch có thể đọc được.

Mã Ruby này sẽ tạo ra một sơ đồ mạch cho bất kỳ số bit nào (thậm chí một bit). Để in, mở trong Logisim và xuất dưới dạng hình ảnh:

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"

cuối cùng, khi được yêu cầu tạo đầu ra cho 32 bit, layouter của tôi tạo ra điều này. Phải thừa nhận rằng, nó không nhỏ gọn cho đầu vào rất rộng:

Quái vật 13 lớp với nhiều không gian lãng phí


trông thực sự tuyệt vời và mạch / bố trí tốt nhất cho đến nay. Mã trong ngôn ngữ nào? bạn đã sử dụng đồ trang trí gì nếu có? layouter được yêu cầu là một thuật toán và phải giả sử rằng thuật toán không được sử dụng (bố trí tay) trừ khi được nêu ...
vzn

@vzn các layouter cũng phải được thực hiện? Tôi hiểu rằng hạn chế đó có nghĩa là chúng ta có thể vẽ sơ đồ bằng tay, nhưng khả năng đọc không được phụ thuộc vào cách vẽ sơ đồ. Mạch của TimWolla chắc chắn được tạo bằng tay. Mã giả chủ yếu dựa trên Haskell với các đối tượng Javascripty được thêm vào.
John Dvorak

thuật toán trực quan được tạo ra có nghĩa là về cơ bản thuật toán layouter nhưng bây giờ thừa nhận rằng có thể được giải thích một cách mơ hồ. xin lỗi vì thiếu tinh thể rõ ràng Bạn có biết bất kỳ đồ trang trí tự động nào có thể nhận được kết quả gần như tương tự với bố cục tay của bạn không?
vzn

Tiếc là không có. yEd có bố cục tuyệt vời nhưng nó bỏ qua định hướng. Tôi chưa bao giờ làm quen với dấu chấm và tôi cũng không thấy đầu ra của nó chính xác. Tôi đoán rằng tôi có thể dịch mã giả này sang Ruby (dễ dàng) và viết trang trí chuyên dụng của riêng tôi (không quá khó, nhưng phức tạp) sẽ xuất ra một mạch logisim (nó chỉ là một XML và thậm chí nó không được nén, khá dễ dàng). Tôi có nên (tôi muốn), và tôi có nên đăng nó dưới dạng một câu trả lời riêng không? Ngoài ra, điều đó sẽ được tính là thiết kế bằng tay?
John Dvorak

Tất cả các câu trả lời tốt, nhưng điều này có vẻ như thanh lịch nhất cho đến nay
Digital Trauma

5

2 × 24 KHÔNG, 2 × 10 + 5 VÀ, 2 × 2 + 5 HOẶC, 2 × 2 BẮC

Điều này hoàn toàn không quy mô. Giống như không phải ở tất cả. Có lẽ tôi sẽ cố gắng cải thiện nó.

Tôi đã kiểm tra số này lên tới 30 và nó hoạt động tốt.

Hai mạch lớn này đang đếm số lượng đầu vào hoạt động:

  • Phía trên bên phải đếm số bit có công suất chẵn (từ 0 đến 4)
  • Cái dưới bên trái đếm số bit có công suất lẻ (từ 0 đến 4)

Nếu sự khác biệt của những số đó là 0hoặc 3số chia hết cho 3. Các mạch dưới bên phải cơ bản bản đồ mỗi sự kết hợp hợp lệ ( 4,4, 4,1, 3,3, 3,0, 2, 2, 1, 1, 0, 0) vào một hoặc.

Vòng tròn nhỏ ở giữa là một đèn LED bật nếu số nếu chia hết cho 3 và tắt đi.


wow tốt / nhanh! ... Xin vui lòng đặt một liên kết đến mã hoặc nội tuyến nó ... cũng chi tiết cách bạn thực hiện trực quan hóa ...?
vzn

@vzn Việc trực quan hóa được thực hiện với logisim . Nó được xây dựng trong tay tôi, nhưng thuật toán chung cũng có thể dễ dàng thực hiện với một chương trình. Nó được giải thích một phần trong câu trả lời.
TimWolla
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.