建立3分频电路


12

TCS中的布尔电路基本上是由“与”,“或”,“非”门组成的DAG,通过“功能完备性”已知,它们可以计算所有可能的功能。例如,这是ALU中的基本原理。

挑战:创建一个电路以确定8位二进制数是否可以被3整除,并以某种方式可视化您的结果(即某种形式的图片)

选民的判断标准基于生成电路的代码是否能很好地推广到任意大小的数字,以及算法创建的可视化是否紧凑/平衡但仍然可读(即不允许人工可视化)。也就是说,可视化仅适用于n = 8,但理想情况下,代码将适用于所有'n'。获奖作品只是最高票数。

有点类似的问题:使用NAND逻辑门构建乘法器


2
好多了。但是,“概括”和“美学”不是客观的。所有问题都必须有一个客观的获胜标准。如果要应用这些特征,请使用流行度竞赛。如果您需要最短的代码,请使用代码高尔夫。如果要将两者结合使用,请使用代码挑战,但要指定一个公式。例如1.25 *票- 0.25 *长度这个问题确实:codegolf.stackexchange.com/questions/23581/eiffel-tower-in-3d/...
等级河圣

确定使您想要的CHG。谢谢反馈。
vzn 2014年

哦,我猜编译后的VHDL或Verilog在进行所有优化后应该给出最短的答案。我稍后再试。
Kirill Kulakov 2014年

1
更好的获胜标准是高尔夫球场
TheDoctor 2014年

@医生 ???是什么gate-golf?该标签不存在。与会者注意:请说明您使用的语言/可视化工具。如果有人要输入plz评论。否则将接受优胜者tonite。到目前为止,这对受访者的影响比预期的要好得多!
vzn 2014年

Answers:


7

求模3的电路

该图在每个级别i上维护3个布尔值。它们表示数字的高阶i位等于0、1或2 mod 3的事实。在每个级别,我们都基于前三个位和下一个输入位来计算下三个位。

这是生成图形的python代码。只需更改N即可获得不同数量的位数,或更改K即可获得不同的模数。通过运行python程序的输出以生成图像。

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

大!也使用了 graphviz ...微小的错误,图中有未使用的AND / OR。还建议用不同的颜色突出显示输入位以显示其位置
vzn 2014年

@vzn:好的,已修复。
基思·兰德尔

12

深度:7(对数),18x与,6x或,7x异或,31门(线性)

让我以四为底,以三为模来计算数字总和:

具有清晰可见的层次结构的7层电路

Logisim中绘制的电路

形式化,正式化(希望有些可读):

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

现在用英语:

如果数字中的位数多于两位,则取最低的两对位,并将它们取模3,然后将结果附加到数字的末尾,如果最后一对是模3则返回零。数字中的位数,在顶部添加一个额外的零位,然后以恒定值传播进行抛光。

追加到后面而不是前面可以确保加法树是平衡树,而不是链表。反过来,这确保了位数的对数深度:五个门和用于消除对的三个电平,以及末尾的额外门。

当然,如果需要近似的平面度,请将未修改的顶部对传递到下一层,而不是将其缠绕在最前面。但是,这说起来容易执行起来难(即使是伪代码也是如此)。如果数字中的位数是2的幂(到2014年3月,在任何现代计算机系统中都是如此),则不会出现孤对。

如果布局器保留局部性/最小化路径长度,则应保持电路可读。

该Ruby代码将为任意数量的位(甚至一个位)生成一个电路图。要打印,请在Logisim中打开并导出为图像:

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"

最后,当要求创建32位输出时,我的布局器会生成此输出。诚然,对于非常广泛的输入,它并不是非常紧凑:

13层怪兽,空间浪费


到目前为止看起来确实很棒,而且电路/布局最佳。代码使用哪种语言?您使用了什么版面设计器?要求版面设计器是一种算法,除非有说明,否则必须假设未使用该算法(手动版式)...
vzn 2014年

@vzn布局器也必须实现吗?我理解该限制意味着我们可以手动绘制该图,但是可读性一定不取决于绘制该图的方式。TimWolla的电路肯定是手工产生的。伪代码主要基于Haskell,并添加了Javascript对象。
John Dvorak 2014年

通过算法创建的可视化基本上是指算法布局器,但现在承认可以被模棱两可地解释。对不起,缺乏透明度。您是否知道有任何一种自动布局器可以得到与手部布局几乎相似的结果?
vzn 2014年

抱歉不行。yed具有出色的布局器,但它忽略了方向。我从不熟悉dot,也找不到它的输出。我想我可以将此伪代码转换为Ruby(简单),并编写自己的专用布局器(不是太难,但很复杂),该布局器将输出logisim电路(它只是XML,甚至没有压缩,所以非常简单)。我应该(我想),并且应该将其作为单独的答案发布吗?另外,这算是手工设计的吗?
John Dvorak 2014年

所有的答案都不错,但这似乎是迄今为止最优雅的方法
Digital Trauma

5

2×24 NOT,2×10 + 5 AND,2×2 + 5 OR,2×2 NOR

这完全无法扩展。一点也不喜欢。也许我会尝试改善它。

我确实测试了最多30个数字,并且效果很好。

这两个大电路正在计算有效输入的数量:

  • 右上角的数字计算具有偶数次幂的位数(从零到4)
  • 左下角的数字计算具有奇数次幂的位数(0到4)

如果这些数字之差为03该数字可被整除3。右下电路基本上映射的各有效组合(4,44,13,33,02, 21, 10, 0)转化为或。

中间的小圆圈是一个LED指示灯,如果该数字可被3整除,则该指示灯点亮;否则,该指示灯熄灭。


哇好/快!... plz放置了指向代码的链接或对其进行内联...还详细说明了如何实现可视化...?
vzn14 2013年

@vzn可视化是使用logisim进行的。它是我亲手打造的,但是通用算法也可以很容易地通过程序来完成。答案中有部分解释。
TimWolla 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.