Układ logiki cyfrowej - pytanie egzaminacyjne

14

Mam pytanie z egzaminu, którego nie udało mi się rozwiązać:

Muszę zbudować cyfrowy układ logiczny, który otrzymuje 4-bitową liczbę i zwrócić, truejeśli liczba jest 0, 7lub 14. Mam tylko jedną XORbramę (2 wejścia), jedno NOR(3 wejścia), jedno NAND(2 wejścia) i jeden dekoder 3 do 8.

Myślę, że to pytanie jest nierozwiązywalne, nie znalazłem żadnej kombinacji, która mogłaby to zrobić. Masz pomysł, jak to rozwiązać?

nrofis
źródło
1
Jako wskazówka: biorąc pod uwagę 4 bity i dekoder 3-8, musisz traktować jeden z bitów inaczej.
Brian Drummond,
2
@BrianDrummond, ale już to wiem i nadal nie udało mi się go rozwiązać. Każde rozwiązanie, które próbowałem, czuje, że brakuje jednej bramki OR. Nie mogę znaleźć takiej kombinacji z podanymi bramami, która mogłaby rozwiązać problem ... Zauważ, że mam tylko JEDNĄ bramę dla każdego typu ...
nrofis
3
@BrianDrummond: jeśli opublikujesz opis rozwiązania, które Twoim zdaniem istnieje, możemy je zweryfikować. Trudno powiedzieć, że nie istnieje żadne rozwiązanie, ale łatwo jest zweryfikować, czy rozwiązanie jest prawidłowe.
pasaba por aqui
2
@Ido Kessler ... Zaintrygowało mnie twoje rozwiązanie i jeśli twój dowód jest poprawny, przepraszam, że go usunąłeś. Jak dotąd wydaje się, że nikt nie ma rozwiązania. Być może, jeśli podasz opis swojego algorytmu, poprawi to odpowiedź. Jaką masz pewność, że jest poprawna i wolna od błędów?
Tut
3
@jalalipop, zrobiłem wczoraj. Ido Kessler i pasaba por aqui mieli rację, mój profesor powiedział, że pytanie było złe i NAND powinien być NOR ....
nrofis

Odpowiedzi:

24

Napisałem algorytm w języku C #, który wypróbowuje każdą możliwą kombinację tych Nor 3->1 Xor 2->1 Nand 2->1i Decoder 3->8.

Po uruchomieniu go przez 7 i pół miliona lat przez 2 godziny, zwrócił 42 Fałsz. Uważam, że to dowodzi, że pytanie nie ma odpowiedzi, ponieważ ten algorytm sprawdza każdą możliwą kombinację. :)

Poproszono mnie o opisanie tego, więc następna część zawiera wyjaśnienie części kodu, część po części. TL; DR - możesz po prostu przejść do kodu na końcu :)


Porozmawiajmy o liniach wejściowych, mają one 0 lub 1 stanów i dla każdego z możliwych wejść (od 0 do 15) mają różne wartości:

dla pierwszego wiersza wygląda to tak: 0 1 0 1 0 1 ... Drugi to: 0 0 1 1 0 0 1 1 ... trzeci: 0 0 0 0 1 1 1 1 .... jak binarny liczenie ... masz pomysł: P

Stworzyłem więc obiekt reprezentujący każdą linię w każdym z jego stanów:

class BitLine{
    bool[] IsActiveWhenInputIs = new bool[16];
}

Jak to mówi bitLine.IsActiveWhenInputIs [5] zwraca, czy linia była aktywna, gdy wejście miało wartość 5.

To jest kod, który tworzy linie wejściowe:

var bitLineList = new BitLine[6]; // initialize new array of bitLines
for (int i = 0; i < 6; i++) bitLineList [i] = new BitLine(); // initialize each bitLine
for (int i = 0; i < 16; i++)
{
    for (int j = 0; j < 4; j++)
    {
        int checker = 1 << j; // check whether the j-th bit is activated in the binary representation of the number.
        bitLineList[j].IsActiveWhenInputIs[i] = ((checker & i) != 0); // if it's active, the AND result will be none zero, and so the return value will be true - which is what we need :D
    }
}

Stworzymy również „zawsze prawdziwe” i „zawsze fałszywe” linie bitowe - w celu zapewnienia stałego wejścia „0” lub „1”.

for (int i = 0; i < 16; i++){
    bitLineList[4].IsActiveWhenInputIs[i] = false;
    bitLineList[5].IsActiveWhenInputIs[i] = true;
}

Teraz, jeśli zauważysz, to, czego szukamy, to konkretna bitLine, która jest prawdziwa, gdy dane wejściowe wynoszą 0, 7, 14. Przedstawmy to w naszej klasie:

var neededBitLine = new BitLine();
for (int i = 0; i < 16; i++){
    neededBitLine.IsActiveWhenInputIs[i] = ((i % 7) == 0); // be true for any number that is devideble by 7 (0,7,14)
}

To sprawiło, że wszystko stało się naprawdę proste: tak naprawdę szukamy sposobu na „wykuwanie” tej potrzebnej BitLine z wejściowej linii bitowej (w ten sposób reprezentuję mój program, co chcę, aby moje wyjście było).

Teraz, to jak idziemy na: za każdym razem używamy jakiegoś logicznego elementu na naszych bitLines jak Xor, Nor, Nanda nawet Decoderjesteśmy rzeczywiście tworzenia nowego bitLine \ s. Znamy wartość każdej linii na każdym możliwym wejściu od 0 do 15, więc możemy obliczyć nową wartość bitLine również na każdym możliwym wejściu!

Nand Nor i Xor są proste:

void Xor(BitLine b1, BitLine b2, BitLine outputBitLine)
{
    for (var i = 0; i < 16; i++)
    {
        outputBitLine.IsActiveWhenInputIs[i] = b1.IsActiveWhenInputIs[i] != b2.IsActiveWhenInputIs[i];
    }
}

void Nand(BitLine b1, BitLine b2, BitLine outputBitLine)
{
    for (var i = 0; i < 16; i++)
    {
        outputBitLine.IsActiveWhenInputIs[i] = !(b1.IsActiveWhenInputIs[i] && b2.IsActiveWhenInputIs[i]);
    }
}

void Nor(BitLine b1, BitLine b2, BitLine b3, BitLine outputBitLine)
{
    for (var i = 0; i < 16; i++)
    {
        outputBitLine.IsActiveWhenInputIs[i] = !(b1.IsActiveWhenInputIs[i] || b2.IsActiveWhenInputIs[i] || b3.IsActiveWhenInputIs[i]);
    }
}

Dla każdego możliwego wejścia reprezentuje on działanie nowej BitLine.

Obsługa dekodera jest nieco trudna, ale pomysł jest taki: „jeśli bity na wejściu reprezentują liczbę x w formacie binarnym, wówczas x-ta wyjściowa linia bitowa będzie prawdziwa, podczas gdy wszystkie inne będą fałszywe. W przeciwieństwie do innych funkcja ta pobiera tablicę bitline i dodaje 8 nowych bitline do tablicy.

void Decoder(BitLine b1, BitLine b2, BitLine b3, List<BitLine> lines, int listOriginalLength)
{
    for (int optionNumber = 0; optionNumber < 8; optionNumber++)
    {
        for (var i = 0; i < 16; i++)
        {
            int sum = 0;
            if (b1.IsActiveWhenInputIs[i]) sum += 4;
            if (b2.IsActiveWhenInputIs[i]) sum += 2;
            if (b3.IsActiveWhenInputIs[i]) sum += 1;

            lines[listOriginalLength+optionNumber].IsActiveWhenInputIs[i] = (sum == optionNumber);
        }
    }
}

Teraz mamy wszystkie nasze podstawowe elementy, więc porozmawiajmy o algorytmie:

Zrobimy algorytm rekurencyjny, na każdej głębokości będzie próbował użyć innych elementów (ani \ nand \ xor \ dekodera) na aktualnie dostępnych liniach bitowych, a następnie ustawi element na niezdatny do użytku dla następnej rekurencyjnej głębokości. Za każdym razem, gdy dojdziemy do końca i nie mamy już więcej elementów do użycia, sprawdzimy, czy mamy linię bitową, której szukaliśmy.

Ten kod w dowolnym momencie sprawdza, czy bieżąca grupa linii zawiera poszukiwaną linię:

bool CheckIfSolutionExist(List<BitLine> lines, int linesLength BitLine neededLine)
{
    for(int i = 0; i<linesLength; i++){
         if (lines[i].CheckEquals(neededLine))
        {
            return true;
        }

    }
    return false;
}

Jest to funkcja, której używa do sprawdzania, czy dwie linie są równe:

bool CheckEquals(BitLine other)
{
    for (var i = 0; i < 16; i++)
    {
        if (this.IsActiveWhenInputIs[i] != other.IsActiveWhenInputIs[i])
        {
            return false;
        }
    }
    return true;
}

Ok, więc teraz w głównej części jest to główny algorytm:

bool Solve(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
{
    if ((!nand) && (!nor) && (!xor) && (!decoder))
    {
        return CheckIfSolutionExist(lines, listLength, neededLine);
    }
    else
    {
        if (HandleNand(lines, nand, nor, xor, decoder, neededLine,listLength))
        {
            return true;
        }
        if (HandleNor(lines, nand, nor, xor, decoder, neededLine,listLength))
        {
            return true;
        }
        if (HandleXor(lines, nand, nor, xor, decoder, neededLine,listLength))
        {
            return true;
        }
        if (HandleDecoder(lines, nand, nor, xor, decoder, neededLine,listLength))
        {
            return true;
        }
        return false;
    }
}

Ta funkcja otrzymuje listę dostępnych linii bitowych, długość listy, wartość logiczną wskazującą, czy każdy element jest aktualnie dostępny (xor / nor / nand / dekoder) oraz bitLine reprezentującą poszukiwaną bitLine.

Na każdym etapie sprawdza, czy mamy więcej elementów do użycia, jeśli nie - sprawdza, czy zarchiwizowaliśmy potrzebną linię.

Jeśli nadal mamy więcej elementów, to dla każdego elementu wywołuje funkcję, która powinna obsługiwać tworzenie nowych bitLines przy użyciu tych elementów i wywoływanie następnej głębokości rekurencyjnej.

Wszystkie następne funkcje obsługi są dość proste, można je przetłumaczyć na „wybierz 2 \ 3 z dostępnych linii bitowych i połącz je za pomocą odpowiedniego elementu. Następnie wywołaj następną głębokość rekurencji, tyle że tym razem nie będzie ona zawierała ten element! ”.

są to funkcje:

bool HandleNand(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
{
    if (nand)
    {
        for (int i = 0; i < listLength; i++)
        {
            for (int j = i; j < listLength; j++)
            {
                BitLine.Nand(lines[i], lines[j],lines[listLength]);
                if (Solve(lines,listLength+1, false, nor, xor, decoder, neededLine))
                {
                    return true;
                }
            }
        }
    }
    return false;
}

bool HandleXor(List<BitLine> lines,  int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
{
    if (xor)
    {
        for (int i = 0; i < listLength; i++)
        {
            for (int j = i; j < listLength; j++)
            {
                BitLine.Xor(lines[i], lines[j],lines[listLength]);
                if (Solve(lines,listLength+1, nand, nor, false, decoder, neededLine))
                {
                    return true;
                }

            }
        }
    }
    return false;
}

bool HandleNor(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
{
    if (nor)
    {
        for (int i = 0; i < listLength; i++)
        {
            for (int j = i; j < listLength; j++)
            {
                for (int k = j; k < listLength; k++)
                {
                    BitLine.Nor(lines[i], lines[j], lines[k],lines[listLength]);
                    if (Solve(lines,listLength+1, nand, false, xor, decoder, neededLine))
                    {
                        return true;
                    }

                }
            }
        }
    }
    return false;
}

bool HandleDecoder(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
{
    if (decoder)
    {
        for (int i = 0; i < listLength; i++)
        {
            for (int j = i; j < listLength; j++)
            {
                for (int k = j; k < listLength; k++)
                {
                    BitLine.Decoder(lines[i], lines[j], lines[k],lines,listLength);
                    if (Solve(lines,listLength+8, nand, nor, xor, false, neededLine))
                    {
                        return true;
                    }
                }
            }
        }
    }
    return false;
}

I to jest to, po prostu wywołujemy tę funkcję z potrzebną linią, której szukamy, i sprawdza każdą możliwą kombinację części elektrycznych, aby sprawdzić, czy można je połączyć w taki sposób, że ostatecznie jedna linia będzie generowane z potrzebnymi wartościami.

* zauważ, że używam tej samej listy przez cały czas, więc nie będę musiał cały czas tworzyć nowych instancji linii bitów. Z tego powodu daję mu bufor 200.


Oto kompletny program:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace ConsoleApp2
{
    public class BitLine
    {
        public bool[] IsActiveWhenInputIs = new bool[16];

        public static void Xor(BitLine b1, BitLine b2, BitLine outputBitLine)
        {
            for (var i = 0; i < 16; i++)
            {
                outputBitLine.IsActiveWhenInputIs[i] = b1.IsActiveWhenInputIs[i] != b2.IsActiveWhenInputIs[i];
            }
        }

        public static void Nand(BitLine b1, BitLine b2, BitLine outputBitLine)
        {
            for (var i = 0; i < 16; i++)
            {
                outputBitLine.IsActiveWhenInputIs[i] = !(b1.IsActiveWhenInputIs[i] && b2.IsActiveWhenInputIs[i]);
            }
        }

        public static void Nor(BitLine b1, BitLine b2, BitLine b3, BitLine outputBitLine)
        {
            for (var i = 0; i < 16; i++)
            {
                outputBitLine.IsActiveWhenInputIs[i] = !(b1.IsActiveWhenInputIs[i] || b2.IsActiveWhenInputIs[i] || b3.IsActiveWhenInputIs[i]);
            }
        }

        public static void Decoder(BitLine b1, BitLine b2, BitLine b3, List<BitLine> lines, int listOriginalLength)
        {
            for (int optionNumber = 0; optionNumber < 8; optionNumber++)
            {
                for (var i = 0; i < 16; i++)
                {
                    int sum = 0;
                    if (b1.IsActiveWhenInputIs[i]) sum += 4;
                    if (b2.IsActiveWhenInputIs[i]) sum += 2;
                    if (b3.IsActiveWhenInputIs[i]) sum += 1;

                    lines[listOriginalLength + optionNumber].IsActiveWhenInputIs[i] = (sum == optionNumber);
                }
            }
        }

        public bool CheckEquals(BitLine other)
        {
            for (var i = 0; i < 16; i++)
            {
                if (this.IsActiveWhenInputIs[i] != other.IsActiveWhenInputIs[i])
                {
                    return false;
                }
            }
            return true;
        }

    }

    public class Solver
    {
        bool CheckIfSolutionExist(List<BitLine> lines, int linesLength, BitLine neededLine)
        {
            for (int i = 0; i < linesLength; i++)
            {
                if (lines[i].CheckEquals(neededLine))
                {
                    return true;
                }

            }
            return false;
        }

        bool HandleNand(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
        {
            if (nand)
            {
                for (int i = 0; i < listLength; i++)
                {
                    for (int j = i; j < listLength; j++)
                    {
                        BitLine.Nand(lines[i], lines[j], lines[listLength]);
                        if (Solve(lines, listLength + 1, false, nor, xor, decoder, neededLine))
                        {
                            return true;
                        }
                    }
                }
            }
            return false;
        }

        bool HandleXor(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
        {
            if (xor)
            {
                for (int i = 0; i < listLength; i++)
                {
                    for (int j = i; j < listLength; j++)
                    {
                        BitLine.Xor(lines[i], lines[j], lines[listLength]);
                        if (Solve(lines, listLength + 1, nand, nor, false, decoder, neededLine))
                        {
                            return true;
                        }

                    }
                }
            }
            return false;
        }

        bool HandleNor(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
        {
            if (nor)
            {
                for (int i = 0; i < listLength; i++)
                {
                    for (int j = i; j < listLength; j++)
                    {
                        for (int k = j; k < listLength; k++)
                        {
                            BitLine.Nor(lines[i], lines[j], lines[k], lines[listLength]);
                            if (Solve(lines, listLength + 1, nand, false, xor, decoder, neededLine))
                            {
                                return true;
                            }

                        }
                    }
                }
            }
            return false;
        }

        bool HandleDecoder(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
        {
            if (decoder)
            {
                for (int i = 0; i < listLength; i++)
                {
                    for (int j = i; j < listLength; j++)
                    {
                        for (int k = j; k < listLength; k++)
                        {
                            BitLine.Decoder(lines[i], lines[j], lines[k], lines, listLength);
                            if (Solve(lines, listLength + 8, nand, nor, xor, false, neededLine))
                            {
                                return true;
                            }
                        }
                    }
                }
            }
            return false;
        }

        public bool Solve(List<BitLine> lines, int listLength, bool nand, bool nor, bool xor, bool decoder, BitLine neededLine)
        {
            if ((!nand) && (!nor) && (!xor) && (!decoder))
            {
                return CheckIfSolutionExist(lines, listLength, neededLine);
            }
            else
            {
                if (HandleNand(lines, listLength, nand, nor, xor, decoder, neededLine))
                {
                    return true;
                }
                if (HandleNor(lines, listLength, nand, nor, xor, decoder, neededLine))
                {
                    return true;
                }
                if (HandleXor(lines, listLength, nand, nor, xor, decoder, neededLine))
                {
                    return true;
                }
                if (HandleDecoder(lines, listLength, nand, nor, xor, decoder, neededLine))
                {
                    return true;
                }
                return false;
            }
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            List<BitLine> list = new List<BitLine>();
            var bitLineList = new BitLine[200];
            for (int i = 0; i < 200; i++) bitLineList[i] = new BitLine();

            // set input bit:
            for (int i = 0; i < 16; i++)
            {
                for (int j = 0; j < 4; j++)
                {
                    int checker = 1 << j;
                    bitLineList[j].IsActiveWhenInputIs[i] = ((checker & i) != 0);
                }
            }

            // set zero and one constant bits:
            for (int i = 0; i < 16; i++)
            {
                bitLineList[4].IsActiveWhenInputIs[i] = false;
                bitLineList[5].IsActiveWhenInputIs[i] = true;
            }

            list.AddRange(bitLineList);

            var neededBitLine = new BitLine();
            for (int i = 0; i < 16; i++)
            {
                neededBitLine.IsActiveWhenInputIs[i] = (i%7==0); // be true for any number that is devideble by 7 (0,7,14)
            }

            var solver = new Solver();
            Console.WriteLine(solver.Solve(list, 6, true, true, true, true, neededBitLine));
            Console.ReadKey();
        }
    }
}

Mam nadzieję, że tym razem jest to prawidłowe wyjaśnienie: P

Ido Kessler
źródło
6
Możesz dołączyć ogólny opis działania tego rodzaju solvera. Nie jest to od razu oczywiste po przeczytaniu tego całkowicie niekomentowanego zrzutu kodu.
Dave Tweed
2
To intrygujące rozwiązanie i mam nadzieję, że możesz podać opis algorytmu. Czy wykonałeś podobne przypadki testowe w celu udowodnienia metody? (BTW, podoba mi się subtelne odniesienie do Douglasa Adamsa)
Tut
2
Dodam, że wypróbowałem ten algorytm w przypadku testowym, który mógłbym sprawdzić: x == 2, x == 3, x == 4, ..., x == 11. Ponieważ uruchomienie zajmuje dużo czasu, zauważam, że x% 3 == 0 i x% 5 == 0 mogą być również niemożliwe i nie mogłem znaleźć odpowiedzi na oba z nich. Ale algorytm zwrócił prawdę dla wszystkich powyższych przypadków, dla których znalazłem rozwiązanie ręcznie.
Ido Kessler,
3
+1! @IdoKessler czy możesz spróbować zmienić 2-bitowy wejściowy NAND na 2-bitowy wejściowy NOR i sprawdzić, czy twoje oprogramowanie daje rozwiązanie? W rzeczywistości z tą bramą zamiast NAND istnieje rozwiązanie.
następny hack
3
@ next-hack zwrócił Prawdę, gdy zmieniłem go na 2-bitowy NOR
Ido Kessler
8

To nie jest odpowiedź, aby odrzucić najbardziej oczywiste rozwiązanie.

b1b2)b4b8

b2)b4

(ani {x=0,x=3),x=6}) nand (b2) xor b4)

b1b4b8

Uproszczenie poprzedniego wyrażenia jest jednak następujące:

(x=0 lub x=3) lub x=6) lub (b2)=b4)

to nie jest oczekiwane:

(x=0 lub x=3) lub x=6) i (b2)=b4)

Z tego powodu uważam, że prawdopodobny jest błąd w pytaniu, ponieważ „nand” oznacza „ani”, ani jeden.

pasaba por aqui
źródło
2
Może to prawda, nigdzie nie znalazłem odpowiedzi.
nrofis
2
+1. Wierzę, że masz rację, a NAND powinien być NOR.
Brian Drummond,
2

Poprawną odpowiedzią na twoje pytanie będzie każdy obwód, który zawsze zwraca wartość true. Ponieważ zwróci wartość true również wtedy, gdy liczby wejściowe to 0,7 lub 14.

Sądzę, że pytanie powinno wyraźnie dotyczyć obwodu, który odpowiada prawdzie, jeśli liczby wejściowe wynoszą 0,7 lub 14, a w przeciwnym razie zwraca fałsz.

Agustin Tena
źródło
2
Wow, nie spodziewałem się takiej odpowiedzi. Obwód powinien zwrócić wartość true wtedy i tylko wtedy, gdy wejście ma wartość 0, 7 lub 14 ...
nrofis 19.09.17
1
Dokładnie tak.
Agustin Tena,
2
+1 za uważne przyjrzenie się specyfikacjom. Jest to zła inżynieria przy uzyskiwaniu takiej specyfikacji od klienta. W takim przypadku właściwą odpowiedzią jest wskazanie klientowi problemu ze specyfikacją i sprawdzenie, czego naprawdę chce. Ale w przypadku pytania egzaminacyjnego pokazuje myślenie od razu i poprawnie zapewnia bardzo prostą odpowiedź.
Olin Lathrop,
-3

To jest wykonalne. Podpowiedź, że dwa środkowe bity są równe dla wszystkich tych wzorów bitów, więc xoring ich da 0, które następnie mogą być wejściem do dekodera z pozostałymi dwoma bitami. Pozostałe bramki są stosowane do trzech wyjść dekodera w celu zapewnienia prawidłowego wyjścia jednobitowego.

Jan
źródło
Już to zrobiłem. Nie znalazłem żadnej kombinacji, która rozwiązałaby pytanie ...
nrofis,
Użyj xora, aby zmniejszyć cztery bity do trzech bitów dla dekodera. Dekoder będzie miał trzy wyjścia, które są 1 dla trzech pasujących wzorców. Ani one razem i nie używaj bramki nand jako falownika.
John
4
@John ... Twoje rozwiązanie daje 6 warunków produktu (bez uproszczenia), z których 3 są nieprawidłowe. Innymi słowy, chociaż twoje rozwiązanie zwraca true dla 0, 7 lub 14; zwraca również wartość true dla 1, 6 lub 8.
Tut