circuito di costruzione per divisibilità per 3


12

Un circuito booleano in TCS è fondamentalmente un DAG costituito da porte And, Or, Not e, da ciò che è noto come "completezza funzionale", possono calcolare tutte le possibili funzioni. ad es. questo è il principio base in una ALU .

Sfida: crea un circuito per determinare se un numero di 8 cifre binarie è divisibile per 3 e in qualche modo visualizzare il tuo risultato (cioè in una sorta di immagine)

I criteri di giudizio per gli elettori si basano sul fatto che il codice per generare il circuito si generalizzi piacevolmente a numeri di dimensioni arbitrarie e che la visualizzazione creata algoritmicamente sia compatta / bilanciata ma ancora leggibile dall'uomo (ovvero la visualizzazione per disposizione manuale non è consentita). cioè la visualizzazione è solo per n = 8 ma il codice funzionerà idealmente per tutti 'n'. l'iscrizione vincente è la più votata.

Domanda un po 'simile: costruisci una macchina moltiplicatrice usando le porte logiche NAND


2
Molto meglio. Tuttavia, "generalizzare" ed "estetico" non sono obiettivi. Tutte le domande devono avere un criterio vincente oggettivo. Se si desidera applicare tali caratteristiche, utilizzare il concorso di popolarità. Se si desidera il codice più breve, utilizzare code-golf. Se si desidera creare una combinazione dei due, utilizzare code-challenge, ma specificare una formula. Ad esempio 1,25 * voti - 0,25 * lunghezza come fa questa domanda: codegolf.stackexchange.com/questions/23581/eiffel-tower-in-3d/…
Level River St

ok ha fatto quei chg che chiedi. grazie per il feedback.
vzn

Credo che VHDL o Verilog compilati dopo che tutte le sue ottimizzazioni dovrebbero dare la risposta più breve. Ci proverò più tardi.
Kirill Kulakov il

1
Un criterio vincente migliore sarebbe gate-golf
TheDoctor

@Il dottore ??? che cos'è gate-golf? quel tag è inesistente. nota ai partecipanti: si prega di indicare quale lingua / strumento di visualizzazione si sta utilizzando. se qualcun altro vuole inserire un commento per favore. altrimenti accetterà il vincitore tonite. grazie mille agli intervistati finora questo è andato "BTE" meglio del previsto!
vzn

Risposte:


7

circuito per calcolare il numero modulo 3

Il grafico mantiene 3 booleani ad ogni livello i. Rappresentano il fatto che i bit i di ordine superiore del numero sono uguali a 0, 1 o 2 mod 3. Ad ogni livello calcoliamo i tre bit successivi in ​​base ai tre bit precedenti e al bit di input successivo.

Ecco il codice Python che ha generato il grafico. Basta cambiare N per ottenere un numero diverso di bit o K per ottenere un modulo diverso. Esegui l'output del programma Python tramite punto per generare l'immagine.

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

grande! ho usatoanche Graphviz ... piccolo cavillo, ci sono AND / OR inutilizzati nel diagramma. suggerisco anche forse di evidenziare i bit di input di colore diverso per mostrare le loro posizioni
vzn

@vzn: Ok, risolto.
Keith Randall,

12

Profondità: 7 (logaritmico), 18x AND, 6x OR, 7x XOR, 31 cancelli (lineare)

Consentitemi di calcolare la somma delle cifre in base quattro, modulo tre:

Circuito a 7 strati con una struttura gerarchica chiaramente visibile

circuito tracciato in Logisim

Generalizzazione, formalmente (si spera in qualche modo leggibile):

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

ora in inglese:

Mentre ci sono più di due bit nel numero, prendi due coppie di bit più basse e sommale modulo 3, quindi aggiungi il risultato alla parte posteriore del numero, quindi ritorna se l'ultima coppia è zero modulo 3. Se c'è un dispari numero di bit nel numero, aggiungere un ulteriore zero bit all'inizio, quindi lucidare con propagazione di valore costante.

L'aggiunta alla parte posteriore anziché alla parte anteriore garantisce che l'albero di addizione sia un albero bilanciato anziché un elenco collegato. Questo, a sua volta, assicura la profondità logaritmica nel numero di bit: cinque porte e tre livelli per la cancellazione della coppia e un cancello extra alla fine.

Naturalmente, se si desidera una planarità approssimativa, passare la coppia superiore non modificata al livello successivo invece di avvolgerla in primo piano. Questo è più facile a dirsi che a implementare (anche in pseudocodice), tuttavia. Se il numero di bit in un numero è una potenza di due (come è vero in qualsiasi moderno sistema informatico a partire da marzo 2014), tuttavia, non si verificherà alcuna coppia solitaria.

Se il layouter mantiene la località / esegue la minimizzazione della lunghezza del percorso, dovrebbe mantenere leggibile il circuito.

Questo codice Ruby genererà uno schema circuitale per qualsiasi numero di bit (anche uno). Per stampare, apri in Logisim ed esporta come immagine:

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"

infine, quando mi viene chiesto di creare un output per 32 bit, il mio layouter genera questo. Certo, non è molto compatto per input molto ampi:

Mostruosità a 13 strati con un sacco di spazio sprecato


sembra davvero fantastico e il miglior circuito / layout finora. in che lingua è il codice? quale layouter hai usato? al layouter fu richiesto di essere un algoritmo e bisogna supporre che l'algoritmo non fosse usato (layout manuale) se non indicato ...
vzn

@vzn anche il layouter doveva essere implementato? Ho capito che la restrizione significa che potremmo disegnare il diagramma manualmente, ma la leggibilità non deve dipendere dal modo in cui il diagramma viene disegnato. Il circuito di TimWolla è sicuramente generato a mano. Lo pseudo-codice si basa principalmente su Haskell con l'aggiunta di oggetti Javascripty.
John Dvorak,

la visualizzazione creata algoritmicamente significava sostanzialmente un layout algoritmico, ma ora ammetto che potrebbe essere interpretata in modo ambiguo. scusate la mancanza di chiarezza cristallina. conosci qualche layout automatico che può ottenere risultati quasi simili al tuo layout manuale?
vzn

Sfortunatamente no. yEd ha ottimi layouter ma ignora l'orientamento. Non ho mai familiarità con il punto né trovo il suo output esattamente piacevole. Immagino di poter tradurre questo pseudocodice in Ruby (facile) e scrivere il mio layouter specializzato (non troppo difficile, ma complesso) che esporterebbe un circuito logisim (è solo un XML e non è nemmeno decompresso, quindi abbastanza facile). Dovrei (voglio) e pubblicarlo come risposta separata? Inoltre, sarebbe considerato progettato a mano?
John Dvorak,

Tutte buone risposte, ma questo sembra il più elegante finora
Digital Trauma

5

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

Questo totalmente non si ridimensiona. Come niente affatto. Forse proverò a migliorarlo.

Ho provato questo per numeri fino a 30 e ha funzionato bene.

Quei 2 grandi circuiti contano il numero di ingressi attivi:

  • Quello in alto a destra conta il numero di bit con una potenza pari (da zero a 4)
  • Quello in basso a sinistra conta il numero di bit con una potenza dispari (da zero a 4)

Se la differenza tra questi numeri è 0o 3il numero è divisibile per 3. Il circuito inferiore destro Mappe praticamente ogni combinazione valida ( 4,4, 4,1, 3,3, 3,0, 2, 2, 1, 1, 0, 0) in una o.

Il piccolo cerchio nel mezzo è un LED che è acceso se il numero è divisibile per 3 e spento altrimenti.


wow bello / veloce! ... per favore metti un link per codificare o incorporarlo ... dettaglia anche come hai fatto la visualizzazione ...?
vzn

@vzn La visualizzazione è stata fatta con logisim . È stato costruito la mia mano, ma l'algoritmo generale può essere facilmente eseguito anche con un programma. È parzialmente spiegato nella risposta.
TimWolla,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.