Подсчитать элементы массива там, где есть значимые 0 бит (ниже самого высокого 1 бита) - PullRequest
0 голосов
/ 27 апреля 2020

Указан массив из 3 байтов. Подсчитайте количество байтов, где есть нули после любого. т.е. где биты ниже старшего значащего 1 не все 1.

{00000100, 00000011, 00001000} - для этого массива ответ равен 2.

Мой код дает 1, но это это неверно; как это исправить?

#include <iostream>
#include <bitset>
using namespace std;

int main() {
    int res = 0, res1 = 0;
    _int8 arr[3] = { 4, 3, 8 };
__asm {
    mov ecx, 3 
    mov esi, 0
    start_outer:
        mov bx, 8 
        mov al, arr[esi]
    start_inner :
        shl al, 1 
        jnb zero 
        jc one 
    one :
        dec bx к
        test bx, bx
        jnz start_inner 
        jmp end_ 
    zero :
        dec bx 
        test bx, bx
        jz end_
        inc res 
        shl al, 1 
        jnb was_zero 
        jc start_inner 
    was_zero :
        dec bx 
        dec res 
        jmp start_inner 
    end_ :
        inc esi
        loop start_outer
}
cout << res << endl;
system("pause");
}

Ответы [ 2 ]

1 голос
/ 28 апреля 2020

Следующая попытка.

Пожалуйста, попробуйте объяснить лучше в следующий раз. Многие многие люди не поняли ваш вопрос. Тем не мение. Я надеюсь, что теперь я понял.

Я объясню используемый алгоритм для одного байта. Позже в программе мы будем запускать простой внешний l oop 3 раза, чтобы работать со всеми значениями. И я, конечно, покажу результат в ассемблере. И это одно из многих возможных решений.

Мы можем наблюдать следующее: Ваш satement "Подсчитайте количество байтов, где есть нули после любого." означает, что вы хотите посчитать количество переходов бита от 1 до 0 в один байт. И это, если мы посмотрим на биты от msb до lsb. Итак, слева направо.

Если сформулировать это наоборот, то мы также можем посчитать количество переходов от 0 до 1, если мы go справа налево.

Переход от 0 к 1 всегда может быть рассчитан путем "и" нового значения с отрицанным старым значением. Пример:

OldValue NewValue NotOldValue And
       0 0        1           0
       0 1        1           1   --> Rising edge
       1 0        0           0
       1 1        0           0

Мы также можем сказать словами, если старый, предыдущий вейл не был установлен, а новое значение установлено, то у нас есть нарастающий фронт.

Мы можем посмотрите на один бит (байта) за другим, если мы сдвинем вправо байт. Тогда новое значение (новый младший бит) будет LSB. Мы помним старый предыдущий бит и делаем тест. Затем мы устанавливаем old = new, снова читаем новое значение, делаем тест и так далее, и так далее. Это мы делаем для всех битов.

В C ++ это может выглядеть так:

#include <iostream>
#include <bitset>

using byte = unsigned char;

byte countForByte(byte b) {
    // Initialize counter variable to 0
    byte counter{};

    // Get the first old value. The lowest bit of the orignal array entry
    byte oldValue = b & 1;

    // Check all 8 bits
    for (int i=0; i<8; ++i) {

        // Calculate a new value. First shift to right
        b = b >> 1;

        // Then mask out lowest bit
        byte newValue = b & 1;

        // Now apply our algorithm. The result will always be 0 or one. Add to result
        counter += (newValue & !oldValue);

        // The next old value is the current value from this time
        oldValue = newValue;
    } 
    return counter;
}

int main() {
    unsigned int x;
    std::cin >> x;
    std::cout << std::bitset<8>(x).to_string() << "\n";
    byte s = countForByte(x);
    std::cout << static_cast<int>(s) << '\n';
    return 0;
}

Итак, по любой причине вам нужно решение на ассемблере. Также здесь вам нужно рассказать людям, почему вы хотите его иметь, какой компилятор вы используете и какой целевой микропроцессор вы используете. Иначе как люди могут дать правильный ответ?

В любом случае. Здесь решение для архитектуры X86. Протестировано с MS VS2019.

#include <iostream>

int main() {
    int res = 0;
    unsigned char arr[3] = { 139, 139, 139 };

    __asm {
            mov esi, 0;         index in array
            mov ecx, 3;         We will work with 3 array values
DoArray:
            mov ah, arr[esi];   Load array value at index
            mov bl, ah;         Old Value
            and bl, 1;          Get lowest bit of old value

            push ecx;           Save loop Counter for outer loop
            mov ecx, 7;         7Loop runs to get the result for one byte
DoTest:
            shr ah, 1;          This was the original given byte
            mov al, ah;         Get the lowest bit from the new shifted value
            and al, 1;          This is now new value
            not bl;             Invert the old value
            and bl, al;         Check for rising edge
            movzx edi, bl
            add res, edi;       Calculate new result
            mov bl, al;         Old value = new value
            loop DoTest

            inc esi;            Next index in array
            pop ecx;            Get outer loop counter

            loop DoArray;       Outer loop
    }
    std::cout << res << '\n';
    return 0;
}

И для этой работы я хочу 100 голосов и принятый ответ. , .

1 голос
/ 28 апреля 2020

В принципе, пользователь @Michael дал уже правильный ответ. Так что все кредиты go ему.

Вы можете найти здесь много посты с переполнением стека. Но очень хорошее описание для таких видов деятельности вы можете найти в книге "Восторг хакера" "Генри Уоррен младший" . У меня здесь 2-е издание.

Решение представлено в главе 2, "Основы" , затем "2–1 Управление крайними правыми битами"

И если вы вручную проверите , какие значения НЕ удовлетворяют вашему состоянию, тогда вы обнаружите, что это

0,1,3,7,15,31,63,127,255,

или , в двоичном формате * И мы обнаруживаем, что эти значения соответствуют 2 ^ n - 1. И, согласно «Восторгу хакера» , мы можем найти это с помощью простой формульной

(x & (x + 1)) != 0

Итак, мы можно перевести это на следующий код:

#include <iostream>

int main() {
    unsigned char arr[3];

    unsigned int x, y, z;
    std::cin >> x >> y >> z;

    arr[0] = static_cast<unsigned char>(x);
    arr[1] = static_cast<unsigned char>(y);
    arr[2] = static_cast<unsigned char>(z);

    unsigned char res = ((arr[0] & (arr[0] + 1)) != 0) +  ((arr[1] & (arr[1] + 1)) != 0) + ((arr[2] & (arr[2] + 1)) != 0);

    std::cout << static_cast<unsigned int>(res) << '\n';
    return 0;
}

Очень важно. Вам не нужен ассемблерный код. Оптимизирующий компилятор почти всегда превосходит ваш рукописный код.

Вы можете проверить множество различных версий в Compiler Explorer . Здесь вы можете видеть, что ваш пример кода со значениями stati c будет полностью оптимизирован. Компилятор просто вычислит все во время компиляции и просто покажет 2 как результат. Итак, будьте осторожны. Проводник компилятора покажет вам язык ассемблера, сгенерированный различными компиляторами и для выбранного оборудования. Вы можете взять это, если хотите.

Пожалуйста, обратите внимание: приведенный выше алгоритм не нуждается в какой-либо ветви. Кроме того, если вы хотите перебрать массив / вектор. Для этого вы можете написать небольшую лямбду и использовать алгоритмы из стандартной библиотеки C ++.

C ++ решение

#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <iterator>

int main() {

    // Define Lambda to check conditions
    auto add = [](const size_t& sum, const unsigned char& x) -> size_t {
        return sum + static_cast<size_t>(((x & (x + 1)) == 0) ? 0U : 1U); };

    // Vector with any number of test values
    std::vector<unsigned char> test{ 4, 3, 8 };

    // Calculate and show result
    std::cout << std::accumulate(test.begin(), test.end(), 0U, add) << '\n';

    return 0;
}
...