한 번에 8 비트의 긴 곱셈


13

16 비트 컴퓨터가 제공되며 임의 크기의 정수 곱셈을 구현하라는 메시지가 표시됩니다. 레지스터는 16 비트 숫자 만 보유 할 수 있으며 가장 큰 곱하기 명령어는 두 개의 8 비트 입력을 사용하여 16 비트 결과를 생성합니다.

프로그램은 두 개의 임의 크기의 양수를 입력으로 사용하여 제품을 출력해야합니다. 각 입력 번호는 자체 줄에 리틀 엔디안 바이트 배열로 인코딩되며 각 바이트는 2 자리 16 진수입니다. 출력 형식도 비슷해야합니다. 아마도 예제로 가장 잘 설명되었을 것입니다.

입력

1f 4a 07
63 a3

산출

fd 66 03 a7 04

곱셈 477727 * 41827 = 19981887229를 인코딩합니다.

각 입력 숫자의 마지막 (가장 중요한) 바이트가 0이 아니며 출력하는 숫자의 마지막 청크가 0이 아니라고 가정 할 수 있습니다. 두 입력 숫자는 최대 100 바이트입니다.

가장 작은 코드가 승리합니다.

사용할 수있는 가장 큰 곱셈은 1 바이트 * 1 바이트이며 2 바이트보다 큰 정수 유형은 없습니다.


이는 Haskell과 같은 기본 8 비트 유형이없는 언어에 중요합니다.
FUZxxl

1
추가는 어떻습니까? 기성품 인 임의의 크기 추가 기능을 가지고있는 것처럼 가장 할 수 있습니까? 그렇지 않다면 무엇 을 추가 할 수 있습니까?
Timwi

@Timwi : 한 번에 16 비트를 원하는 모든 작업을 수행 할 수 있습니다. 무엇이든 추가, 이동 더 큰 작업은 스스로 합성해야합니다.
Keith Randall

정확한 바이트 순서에 대해 +1
12Me21

답변:


13

펄, 137 자

($x,$y)=<>;while($x=~s/.. *//s){$e=hex$&;$i=0;$s=$r[$i]+=$e*hex,$r[$i]&=255,$r[++$i]+=$s>>8 for$y=~/.. */gs;$y="00$y"}printf'%02x 'x@r,@r

경고

  • 때때로 00결과의 끝에 여분의 바이트를 인쇄합니다 . 물론 여분의 바이트를 사용해도 결과는 여전히 정확합니다.
  • 결과에서 마지막 16 진 바이트 다음에 추가 공간을 인쇄합니다.

설명

설명은 약간 길지만 여기에있는 대부분의 사람들은 흥미로울 것입니다.

우선 10 살 때 다음과 같은 작은 요령을 배웠습니다. 이 두 양수를 곱할 수 있습니다. 이것을 13 × 47의 예를 사용하여 설명하겠습니다. 첫 번째 숫자 인 13을 쓰고 1에 도달 할 때까지 숫자 를 2로 나눕니다 .

13
 6
 3
 1

이제 13 옆에 다른 숫자 47을 쓰고 같은 수의 2를 계속 하십시오.

13     47
 6     94
 3    188
 1    376

이제 왼쪽의 숫자가 짝수 인 모든 줄을 건너 뜁니다 . 이 경우 이것은 6입니다. (코드에서 취소 선을 사용할 수 없으므로 제거하겠습니다.) 마지막으로 오른쪽에 나머지 숫자를 모두 추가합니다.

13     47
 3    188
 1    376
     ----
      611

그리고 이것이 정답입니다. 13 × 47 = 611.

이제 여러분은 모두 컴퓨터 전문가이기 때문에 왼쪽 및 오른쪽 열에서 실제로 수행하는 작업은 각각 x >> 1y << 1입니다. 또한 yif 만 추가합니다 x & 1 == 1. 이것은 알고리즘으로 직접 변환되어 의사 코드로 작성합니다.

input x, y
result = 0
while x > 0:
    if x & 1 == 1:
        result = result + y
    x = x >> 1
    y = y << 1
print result

if곱셈을 사용 하도록를 다시 쓴 다음 비트 단위 대신 바이트 단위로 작동하도록 쉽게 변경할 수 있습니다.

input x, y
result = 0
while x > 0:
    result = result + (y * (x & 255))
    x = x >> 8
    y = y << 8
print result

여기에는 여전히 y임의의 크기 인 와의 곱셈이 포함되어 있으므로 루프로도 변경해야합니다. 우리는 펄에서 그렇게 할 것입니다.

이제 모든 것을 Perl로 번역하십시오 :

  • $x그리고 $y16 진수 형식의 입력이므로 가장 중요하지 않은 바이트가 먼저 있습니다.

  • 따라서, 대신 x >> 8내가 할 $x =~ s/.. *//s. 마지막 바이트에는 공백이 없을 수도 있으므로 space + star가 필요합니다 (space + ?도 사용할 수 있음 ). 제거 된 바이트 ( x & 255)를 자동으로에 넣 습니다 $&.

  • y << 8간단 $y = "00$y"합니다.

  • result실제로 숫자 배열이다 @r. 마지막에의 각 요소 @r에는 1 바이트의 답변 이 포함되지만 계산의 중간에는 2 바이트 이상이 포함될 수 있습니다. 아래에서 각 값이 2 바이트 (16 비트)를 넘지 않으며 결과가 항상 1 바이트임을 확인합니다.

Perl 코드는 다음과 같습니다.

# Input x and y
($x, $y) = <>;

# Do the equivalent of $& = x & 255, x = x >> 8
while ($x =~ s/.. *//s)
{
    # Let e = x & 255
    $e = hex $&;

    # For every byte in y... (notice this sets $_ to each byte)
    $i = 0;
    for ($y =~ /.. */gs)
    {
        # Do the multiplication of two single-byte values.
        $s = $r[$i] += $e*hex,
        # Truncate the value in $r[$i] to one byte. The rest of it is still in $s
        $r[$i] &= 255,
        # Move to the next array item and add the carry there.
        $r[++$i] += $s >> 8
    }

    # Do the equivalent of y = y << 8
    $y = "00$y"
}

# Output the result in hex format.
printf '%02x ' x @r, @r

이제 이것이 항상 bytes를 출력 하고 계산이 2 바이트 보다 큰 값을 생성하지 않는다는 증거입니다 . while루프를 통해 유도하여 이것을 증명할 것입니다 .

  • @r처음에 비어 있으면 값이 0xFF보다 크지 않습니다. 값이 전혀 없기 때문입니다. 이상이 기본 사례입니다.

  • 이제 @rwhile반복 의 시작 부분에 단일 바이트 만 포함됩니다 .

    • for루프는 명시 적으로 &=255 결과 배열의 모든 값이야 마지막 하나를 제외한를 우리가 아니라 마지막 볼 필요가 있도록.

    • 우리는 $xand 에서 항상 1 바이트 만 제거한다는 것을 알고 있습니다 $y.

      • 따라서 $e*hex2 바이트의 단일 바이트 값을 곱하면 범위 안에 있습니다 0 — 0xFE01.

      • 유도 가설에 따르면 $r[$i]1 바이트입니다. 따라서 $s = $r[$i] += $e*hex범위 안에 있습니다 0 — 0xFF00.

      • 따라서 $s >> 8항상 1 바이트입니다.

    • $y루프를 00반복 할 때마다 추가 로 증가합니다 while.

      • 따라서 while루프를 반복 할 때마다 내부 for루프가 이전 while반복 보다 한 번 더 반복됩니다.

      • 따라서 루프 $r[++$i] += $s >> 8의 마지막 반복에서 for항상에 추가 $s >> 8하고 항상 1 바이트 0임을 확인했습니다 $s >> 8.

    • 따라서 루프 @r의 끝에 저장된 마지막 값 for도 단일 바이트입니다.

이것으로 놀랍고 흥미로운 도전이 끝납니다. 게시 해 주셔서 감사합니다!


4

C 솔루션

이 솔루션은 입력 유효성 검사를 수행하지 않습니다. 또한 가볍게 테스트되었습니다. 속도는 실제로 고려되지 않았습니다. Malloc의 기억, 그리고 그것이 얼마나 많이 잡는 지에 대해 특별히 영리하지 않습니다. 충분하고 필요 이상으로 보장됩니다.

m ()은 문자열을 허용하며 문자열에 두 개의 줄 바꿈이 필요합니다 (각 숫자마다 하나씩). 숫자, 소문자, 공백 및 줄 바꿈 만 예상합니다. 16 진 숫자는 항상 쌍이어야합니다.

곱하기 연산은 사용되지 않습니다 시프트는 8 비트 변수에서 수행됩니다. 하나의 16 비트 추가가 수행됩니다. 32 비트 데이터 유형이 없습니다.

손으로 만 줄이십시오. 편집 : 더 난독 화, 적은 문자 : D gcc로 경고로 컴파일합니다.

캐릭터 : 675

typedef unsigned char u8;
#define x calloc
#define f for
#define l p++
#define E *p>57?*p-87:*p-48
#define g(a) --i;--a;continue
void m(u8*d){short n=0,m=0,a,b,i,k,s;u8*t,*q,*r,*p=d,o;f(;*p!=10;n++,l){}l;f(;*p
!=10;m++,l){}t=x(n,1);q=x(m,1);r=x(n,1);p=d;a=n;i=0;f(;*p!=10;i++,l){if(*p==32){
g(a);}t[i]=E;t[i]<<=4;l;t[i]|=E;}a/=2;b=m;i=0;l;f(;*p!=10;i++,l){if(*p==32){g(b)
;}q[i]=E;q[i]<<=4;l;q[i]|=E;}b/=2;f(k=0;k<8*b;k++){if(q[0]&1){o=0;f(i=0;i<n;i++)
{s=o+t[i]+r[i];o=s>>8;r[i]=s&255;}}f(i=n;i;i--){o=t[i-1]>>7&1;t[i-1]*=2;if(i!=n)
t[i]|=o;}f(i=0;i<m;i++){o=q[i]&1;q[i]/=2;if(i)q[i-1]|=(o<<7);}}k=(r[a+b-1]==0)?a
+b-1:b+a;f(i=0;i<k;i++){printf("%02x ",r[i]);}putchar(10);}

이것으로 테스트 할 수 있습니다 :

int main(void){
  m("1f 4a 07\n63 a3\n");
  m("ff ff ff ff\nff ff ff ff\n");
  m("10 20 30 40\n50 60 70\n");
  m("01 02 03 04 05 06\n01 01 01\n");
  m("00 00 00 00 00 00 00 00 00 00 00 00 01\n00 00 00 00 00 00 00 00 02\n");
  return 0;
}

결과:

$ ./long 
fd 66 03 a7 04 
01 00 00 00 fe ff ff ff 
00 05 10 22 34 2d 1c 
01 03 06 09 0c 0f 0b 06 
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 02 

3

OCaml + 배터리, 362 자

표준 O (n * m) 남학생 곱셈 알고리즘. 챌린지 요구 사항을 충족하기 위해 OCaml에서 (이 경우 편리하게) 변경할 수있는 문자열 바이트에서 작업이 수행됩니다. 또한 s2 (2 ^ 8-1) + (2 ^ 8-1) ^ 2 = (2 ^ 8-1) (2 ^ 8 + 1) = 2 ^ 16-1이므로 누산기 는 16 비트를 오버플로하지 않습니다. .

let(@)=List.map
let m a b=Char.(String.(let e s=of_list(((^)"0x"|-to_int|-chr)@nsplit s" ")in
let a,b=e a,e b in let m,n=length a,length b in let c=make(m+n)'\000'in
iteri(fun i d->let s,x=ref 0,code d in iteri(fun j e->let y=code e in
s:=!s+code c.[i+j]+x*y;c.[i+j]<-chr(!s mod
256);s:=!s/256)b;c.[i+n]<-chr!s)a;join" "((code|-Printf.sprintf"%02x")@to_list c)))

예를 들어

# m "1f 4a 07" "63 a3" ;;
- : string = "fd 66 03 a7 04"

# m "ff ff ff ff" "ff ff ff ff" ;;
- : string = "01 00 00 00 fe ff ff ff"

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.