我如何找到101b显示多少次?

时间:2019-06-15 08:01:37

标签: assembly x86 binary bit-manipulation

我需要找出序列101b以16位数字显示多少次。但是我还需要找到距离较远的那些。

例如:在数字01010101中出现4次。因为3是它的一部分,而第四个(如果索引0是左位)由索引1、4和7的3位组成。

因为您可以将其关联为对称的101b序列。

这看起来确实很复杂,但是真的吗?我认为这可能有点棘手。

我设法找出了它定期显示的次数,例如示例编号中可以看到的3次。但是我不知道如何找到对称的。 编辑:我的老师的意思是轮换,我误解了这个问题,尽管感谢大家的帮助

    mov cx,15
Check:

    push dx;the number that I need to check
    and dx,0111b
    cmp dx,101b
    jne Again
Again:

    pop dx
    shr dx,1
    loop check

4 个答案:

答案 0 :(得分:3)

类似这样的东西(未经测试):

mov dx, yourNumber
mov cx, 16
xor ax, ax
count:
  mov bx, dx
  and bx, 0b111
  cmp bx, 0b101
  jne nope
     inc ax
  nope:
  rol dx, 1
  dec cx
  jnz count

因此,基本上,将寄存器旋转16次,并在每次迭代中进行测试,以通过用0b111屏蔽该寄存器的最低3位是否等于0b101。经过此计算后,结果在ax中,而您测试的数字应仍在dx

答案 1 :(得分:2)

逻辑:

如果您将值与1010101010101010b(0xAA)进行异或运算,则任何0101b的4位组将变为1111b,而任何非4位的组均将变为1111b。

如果将1加到1111b,它将溢出,如果将1加到任何其他数字,它将不会溢出。您可以进行安排,以便当加法运算溢出时会导致进位标志被设置。

您可以使用adc指令将进位标志添加到计数/总和中,以获取溢出的组的总数(最初为0101b的组数)。

这只会找到与组的开头对齐的0101b。要使其适用于“ 0101b任何位置”,您需要进行4次操作,并且每次之间都要旋转一次。

代码:

    mov cx,4           ;Number of times to rotate/repeat
    xor ax,ax          ;ax = current sum of 4-bit groups that were 0101b = 0

.nextRotation:
    mov bx,dx          ;bx = the value
    xor bx,0xAAAA      ;dx = any 4-bit group that was 0101b is now 1111b
    add bx,0x1000      ;Carry set if fourth/highest group was originally 0101
    adc ax,0           ;Add carry to the sum
    add bl,0x10        ;Carry set if second group was originally 0101b
    adc ax,0           ;Add carry to the sum
    shl bx,4
    add bx,0x1000      ;Carry set if third group was originally 0101b
    adc ax,0           ;Add carry to the sum
    add bl,0x10        ;Carry set if first/lowest group of 4 bits was originally 0101b
    adc ax,0           ;Add carry to the sum
                       ;ax = number of 4-bit groups that were originally 0101b

    rol dx,1
    loop .nextRotation

答案 2 :(得分:2)

我不确定我是否了解第4个副本在哪里。我(和@sivizius)以为它是由7、0和1位(环绕到最高位)组成的。但这显然不是您想要的。

(同样,您的位编号没有意义。您说8位数字的最高位是位0,但是在注释中,您坚持要使用16位数字。所以{{ 1}}?)

无论如何,我想您想找到像1<<151.0.1这样的模式吗? (其中1..0..1是模式中的可忽略占位符,可以匹配0或1)。

您可以在.循环中查找具有类似内容的内容。

shr dx, 1

因此,与您为; dumb brute force method. countloop: ; do { mov ax, dx and al, 111b ; select the bits that matter in 101 cmp al, 101b ; check that it's the pattern we want ; do count += ZF somehow mov ax, dx and al, 10101b ; select the bits that matter in 1.0.1 cmp al, 10001b ; check that it's the pattern we want ; do count += ZF somehow mov ax, dx and al, 1001001b ; select the bits that matter in 1..0..1 cmp al, 1000001b ; check that it's the pattern we want ; do count += ZF somehow ... for all the rest of the patterns. ; Wider patterns will need to use AX instead of AL shr dx, 1 cmp dx, 101b jae countloop ; }while(x >= 0b101); ;;; exit the loop as soon as DX contains no bits high enough to possibly match ;;; with a big loop, this is worth the extra cmp ;;; instead of just using jnz on the shr dx,1 result 所做的操作完全相同,只是您将“无关位”强制为零。

我没有想到比在循环体内单独检查所有可能的模式更好的方法了。

“计数+ = ZF”可以是(386)101b / setz al,也可以是add cl, al上的简单jnz。 (我认为最大可能计数小于255,因此8位计数器就可以了。)

请注意,我没有将inc cx用作循环计数器,而是在将所有位移出cx后结束了循环

在没有SETCC的情况下无分支地执行此操作的另一种方法是

dx

答案 3 :(得分:1)

  

这看起来确实很复杂,但是真的吗?我认为这可能有点棘手。

是的,这确实很复杂。这是我的距离:

第1步:进行“行程编码”,忽略前导零和尾随零。示例(对于8位数字):

01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
11111101 = 6(1s), 1(0s), 1(1s)

第2步:找到“ 1的游程,0的游程,1的游程”的排列,并将每个游程的计数相乘。示例(对于8位数字):

01010101 = 1(1s), 1(0s), 1(1s), 1(0s), 1(1s), 1(0s), 1(1s)
         = 1     *1     *1
         + 1     *1                   *1
         + 1     *1                                 *1
         + 1                   *1     *1
         + 1                   *1                   *1
         + 1                                 *1     *1
         +               1     *1     *1
         +               1     *1                   *1
         +               1                   *1     *1
         = 1+1+1+1+1+1+1+1 = 9

11001011 = 2(1s), 2(0s), 1(1s), 1(0s), 2(1s)
         = 2     *2     *1
         + 2     *2                   *2
         + 2                   *1     *2
         +               1     *1     *2
         = 4+8+4+2 = 18

11111101 = 6(1s), 1(0s), 1(1s)
         = 6     *1     *1
         = 6

在这一点上;很容易得出结论,有效的实现将涉及列表,并且如果使用递归,则可能会更容易实现(查找涉及列表中第一个游程的排列,然后丢弃第一个游程和第一个零游程的排列并递归)。