Реалізуйте 8-бітний суматор


12

Змагання

Реалізуйте функцію, яка приймає два цілі числа, значення яких варіюються від 0 - 255 і повертає суму цих цілих чисел mod 256. Ви можете використовувати лише побітові заперечення (~), порозрядне чи (|), бітові оператори зсуву (>>, <<) , і призначення (=).

Речі, які ви не можете використовувати, включають (але не обмежуються ними)

  • Додавання, віднімання, множення та ділення
  • Петлі
  • Умовні твердження
  • Функціональні дзвінки

Виграє мало хто з використання двійкових або, бінарних заперечень та операцій зі зміною бітів . У разі вирівнювання виграє найпопулярніше рішення. Як завжди, застосовуються стандартні лазівки .

Ось приклад простого 2-бітного суматора. Він використовує 77 бінарних відхилень, 28 двійкових орів та 2 біт-зсуви для загальної оцінки 107 (це можна побачити, запустивши C-препроцесор gcc -E). Це можна зробити набагато ефективнішим, видаляючи #defines та спрощуючи отримані вирази, але я залишив їх для ясності.

#include <stdio.h>

#define and(a, b) (~((~a)|(~b)))
#define xor(a, b) (and(~a,b) | and(a,~b))

int adder(int a, int b)
{
    int x, carry;
    x = xor(and(a, 1), and(b, 1));
    carry = and(and(a, 1), and(b, 1));
    carry = xor(xor(and(a, 2), and(b, 2)), (carry << 1));
    x = x | carry;
    return x;
}

int main(int argc, char **argv)
{
    int i, j;
    for (i = 0; i < 4; i++) {
        for (j = 0; j < 4; j++) {
            if (adder(i, j) != (i + j) % 4) {
                printf("Failed on %d + %d = %d\n", i, j, adder(i, j));
            }
        }
    }
}

Оновлення: Додано приклад та змінено кількість балів


2
чому не побито "і"?
rdans

@Ryan Більшість людей більше знайомі з воротами NAND, ніж воротами NOR :)
Orby

1
чи вважається рекурсія циклом?
rdans

@ Ryan Recursion вважається циклом, хоча я не впевнений, як би ви його реалізували без умовного твердження.
Орбі

Чи визначено переповнення чи я можу просто вивести щось, якщо воно переповнюється?
Комінтерн

Відповіді:


8

Пітон, 36 операцій

Метод, логарифмічний у параметрі "8"!

def add(a,b):
    H = a&b   #4 for AND
    L = a|b   #1 
    NX = H | (~L) #2
    K = NX 

    H = H | ~(K | ~(H<<1)) #5
    K = K | (K<<1) #2

    H = H | ~(K | ~(H<<2)) #5
    K = K | (K<<2) #2

    H = H | ~(K | ~(H<<4)) #5

    carry = H<<1 #1

    neg_res = NX ^ carry  #7 for XOR
    res_mod_256 = ~(neg_res|-256) #2
    return res_mod_256

Ідея полягає в тому, щоб з'ясувати, які показники переповнюються та викликають причину. Спочатку це лише місця, де обидва aandd bмають 1. Але оскільки перенесені шматочки можуть спричинити подальше переповнення, це потрібно визначити ітераційно.

Замість того, щоб переповнювати кожен індекс у наступний, ми прискорюємо процес, переміщуючи 1 індекс, потім 2 індекси, потім 4 індекси, обов’язково пам’ятаючи місця, де відбулося переповнення (H) і де переповнення більше не може відбутися (K ).


Більш просте ітеративне рішення з 47 операціями:

def add(a,b):
    H = a&b   #4 for AND
    L = a|b   #1 
    NX = H | (~L) #2

    c=H<<1  #1

    for _ in range(6): #6*5
        d = (~c)|NX
        e = ~d
        c = c|(e<<1)

    res = c ^ NX  #7 for XOR

    res_mod_256 = ~(res|-256) #2
    return res_mod_256

Тестова установка для кожного, хто хоче її скопіювати.

errors=[]
for a in range(256):
    for b in range(256):
        res = add(a,b)
        if res!=(a+b)%256: errors+=[(a,b,res)]

print(len(errors),errors[:10])

9

C - 0

Він використовує операторів за межами ~, |, >>, << і =, але я бачу рішення, що використовують оператори кастингу та комами, тому я думаю, що правило не надто суворе, якщо воно не використовує заборонені оператори.

unsigned char sum(unsigned char x, unsigned char y)
{
    static unsigned char z[] = {
        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
        16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
        32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
        48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
        64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
        80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
        96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
        112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
        128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
        144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
        160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
        176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
        192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
        208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
        224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
        240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,
        0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
        16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,
        32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,
        48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,
        64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,
        80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,
        96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,
        112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,
        128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,
        144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,
        160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
        176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,
        192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,
        208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,
        224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,
        240,241,242,243,244,245,246,247,248,249,250,251,252,253,254
    };

    return (&z[x])[y];
}

Це, очевидно, лазівка, але +1 для її вказівки.
Орбі

7

пітон, оцінка = 83 80

def g(x,y):
    for i in xrange(7):
        nx = ~x
        ny = ~y
        x,y = ~(x|ny)|~(nx|y), (~(nx|ny))<<1
    x = ~(x|~y)|~(~x|y)
    return ~(~x|256)

Розкручуємо цикл. Це 10 ops за цикл раз 7 циклів, 7 для останнього xor, і 3 для сквош 9-го біта в кінці.

Реалізуємо рівняння x+y = x^y + 2*(x&y), повторивши його 8 разів. Кожен раз, коли внизу є ще один нульовий біт y.


7

C, Оцінка: 77 60

Гольф просто для чорта, 206 169 131 байт:

#define F c=((~(~c|~m))|n)<<1;
a(x,y){int m=(~(x|~y))|~(~x|y),n=~(~x|~y),c;F F F F F F F return (unsigned char)(~(m|~c))|~(~m|c);}

Розширено:

int add(x,y)
{
    int m=(~(x|~y))|~(~x|y);
    int n=~(~x|~y);
    int c = 0;
    c=((~(~c|~m))|n)<<1; 
    c=((~(~c|~m))|n)<<1; 
    c=((~(~c|~m))|n)<<1; 
    c=((~(~c|~m))|n)<<1; 
    c=((~(~c|~m))|n)<<1;    
    c=((~(~c|~m))|n)<<1; 
    c=((~(~c|~m))|n)<<1; 
    return (int)((unsigned char)(~(m|~c))|~(~m|c));
}

По суті те саме рішення (математично), яке було придумано @KeithRandall @JuanICarrano, але він користується можливістю C грати швидко і збито зі змінними типами та покажчиками, щоб стерти все після перших 8 біт без використання більше операторів.

Залежить від ендіантності машини та розміру (), int та char, але він повинен мати можливість переноситися на більшість програм, що відповідають певній машині, з належною математикою вказівника.

EDIT: Це завдання, що C (або інші мови низького рівня) матимуть чітку позицію - якщо хтось не придумає алгоритм, який не повинен виконувати.


Якщо ви збираєтеся обробляти обгортання таким чином, ви можете просто кинути unsigned char. Це чистіше і портативніше.
Орбі

@Orby - я думаю, що введення тексту unsignedне приходить мені природним чином у гольф коду. Ви праві, звичайно, - оновлено.
Комінтерн

4

Пітон - оцінка 66 64

def xand(a,b):
    return ~(~a|~b) #4

def xxor(a,b):
    return (~(a|~b))|~(~a|b) #7

def s(a,b):
    axb = xxor(a,b)   #7
    ayb = xand(a,b)   #4

    C = 0
    for i in range(1,8):
        C = ((xand(C,axb))|ayb)<<1    #(1+1+4)x7=6x7=42

    return xxor(axb,xand(C,255))    #7 + 4 = 11
    #total: 7+4+42+11 = 64

Це рівняння для пульсатора. C - це перенесення. Він обчислюється по одному біту за раз: у кожній ітерації носій поширюється ліворуч. Як вказував @Orby, оригінальна версія не зробила модульного доповнення. Я зафіксував це, а також зберег цикл ітерації, оскільки перший перенос завжди дорівнює нулю.


3
Хороша робота, але ваш код не обертається належним чином (тобто s(255,2)повертається, 257а не 1). Ви можете виправити це, змінивши останній рядок, return ~(~xxor(axb,C)|256) який додає 3 бали.
Орбі

2

C ++ - оцінка: 113

#define ands(x, y) ~(~x | ~y) << 1
#define xorm(x, y) ~(y | ~(x | y)) | ~(x | ~(x | y))

int add(int x, int y)
{
int x1 = xorm(x, y);
int y1 = ands(x, y);

int x2 = xorm(x1, y1);
int y2 = ands(x1, y1);

int x3 = xorm(x2, y2);
int y3 = ands(x2, y2);

int x4 = xorm(x3, y3);
int y4 = ands(x3, y3);

int x5 = xorm(x4, y4);
int y5 = ands(x4, y4);

int x6 = xorm(x5, y5);
int y6 = ands(x5, y5);

int x7 = xorm(x6, y6);
int y7 = ands(x6, y6);

int x8 = xorm(x7, y7);
int y8 = ands(x7, y7);

return (x8 | y8) % 256;
}

add(1, 255)повертає 128 для мене, @Ryan.
Орбі

@Orby виправлено зараз
rdans

%немає в списку дозволених операторів, а саме ~, |, >>, і <<. Може замінити його ands(x8|y8, 255)>>1?
Орбі

Насправді, вдало ~(~x8 | y8 | 0xFFFFFF00)б виконати трюк лише з 4+ до вашого рахунку.
Орбі

2
Але чи не введення типу byteзамість цього intзробить його переповненням автоматично?
гордий haskeller
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.