Panjang berlipat ganda, 8 bit sekaligus


13

Anda diberi mesin 16-bit dan diberitahu untuk menerapkan multiplikasi bilangan bulat berukuran sewenang-wenang. Register Anda hanya dapat menampung angka 16-bit, dan instruksi pengali terbesar mengambil dua input 8-bit dan menghasilkan hasil 16-bit.

Program Anda harus memasukkan dua angka positif berukuran arbitrer dan mengeluarkan produk mereka. Setiap nomor input dikodekan pada barisnya sendiri sebagai array byte-endian kecil di mana setiap byte adalah nomor hex 2-digit. Outputnya harus diformat sama. Mungkin paling baik dijelaskan dengan sebuah contoh:

memasukkan

1f 4a 07
63 a3

keluaran

fd 66 03 a7 04

yang mengkodekan perkalian 477727 * 41827 = 19981887229.

Anda dapat mengasumsikan bahwa byte terakhir (paling signifikan) dari setiap nomor input adalah nol, dan potongan terakhir dari nomor yang Anda output harus bukan nol. Kedua nomor input paling panjang 100 byte.

Kode terkecil menang.

Ingat, multiply terbesar yang diizinkan untuk Anda gunakan adalah 1 byte * 1 byte, dan tidak ada tipe integer yang lebih besar dari 2 byte!


Ini sangat penting untuk bahasa, yang tidak memiliki tipe 8-bit default, seperti Haskell.
FUZxxl

1
Bagaimana dengan penambahan? Bisakah kita berpura-pura memiliki fungsi penambahan ukuran siap pakai? Jika tidak, apa yang bisa kita tambahkan?
Timwi

@ Timwi: Anda bisa melakukan apa saja yang Anda inginkan 16 bit sekaligus. Menambahkan, bergeser, apa pun. Setiap operasi yang lebih besar perlu Anda sintesiskan sendiri.
Keith Randall

+1 untuk pesanan byte yang benar
12Me21

Jawaban:


13

Perl, 137 karakter

($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

Peringatan

  • Terkadang mencetak 00byte tambahan di akhir hasil. Tentu saja hasilnya masih benar bahkan dengan byte tambahan itu.
  • Mencetak ruang ekstra setelah hex byte terakhir dalam hasilnya.

Penjelasan

Penjelasannya akan agak panjang, tapi saya pikir sebagian besar orang di sini akan menganggapnya menarik.

Pertama-tama, ketika saya berusia 10 tahun, saya diajari trik kecil berikut. Anda dapat mengalikan dua angka positif dengan ini. Saya akan menjelaskan ini menggunakan contoh 13 × 47. Anda mulai dengan menulis angka pertama, 13, dan membaginya dengan 2 (dibulatkan setiap kali) sampai Anda mencapai 1:

13
 6
 3
 1

Sekarang, di sebelah angka 13 Anda menulis angka lainnya, 47, dan terus mengalikannya dengan 2 kali yang sama:

13     47
 6     94
 3    188
 1    376

Sekarang Anda mencoret semua garis di mana angka di sebelah kiri genap . Dalam hal ini, ini hanya 6. (saya tidak bisa melakukan kode pemogokan, jadi saya hanya akan menghapusnya.) Akhirnya, Anda menambahkan semua angka yang tersisa di sebelah kanan:

13     47
 3    188
 1    376
     ----
      611

Dan ini jawaban yang tepat. 13 × 47 = 611.

Sekarang, karena Anda semua ahli komputer, Anda akan menyadari bahwa apa yang sebenarnya kami lakukan di kolom kiri dan kanan adalah x >> 1dan y << 1, masing-masing. Selanjutnya, kami menambahkan yhanya jika x & 1 == 1. Ini diterjemahkan langsung ke dalam algoritma, yang akan saya tulis di sini di pseudocode:

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

Kita dapat menulis ulang ifuntuk menggunakan perkalian, dan kemudian kita dapat dengan mudah mengubah ini sehingga bekerja pada basis byte demi byte alih-alih sedikit demi sedikit:

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

Ini masih mengandung perkalian dengan y, yang merupakan ukuran arbitrer, jadi kita perlu mengubahnya menjadi sebuah loop juga. Kami akan melakukannya di Perl.

Sekarang terjemahkan semuanya ke Perl:

  • $xdan $ymerupakan input dalam format hex, sehingga mereka memiliki byte paling signifikan pertama .

  • Jadi, bukannya x >> 8saya lakukan $x =~ s/.. *//s. Saya memerlukan bintang ruang + karena byte terakhir mungkin tidak memiliki ruang di atasnya (bisa menggunakan ruang + ?juga). Ini secara otomatis menempatkan byte yang dihapus ( x & 255) ke $&.

  • y << 8sederhana $y = "00$y".

  • The resultsebenarnya array numerik, @r. Pada akhirnya, setiap elemen @rberisi satu byte jawaban, tetapi di tengah-tengah perhitungan, ia mungkin berisi lebih dari satu byte. Saya akan membuktikan kepada Anda di bawah ini bahwa setiap nilai tidak pernah lebih dari dua byte (16 bit) dan hasilnya selalu satu byte pada akhirnya.

Jadi di sini adalah kode Perl terurai dan berkomentar:

# 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

Sekarang sebagai bukti bahwa ini selalu menghasilkan byte , dan perhitungan tidak pernah menghasilkan nilai lebih dari dua byte. Saya akan membuktikan ini dengan induksi di atas whileloop:

  • Kosong @rdi awal jelas tidak memiliki nilai lebih besar dari 0xFF di dalamnya (karena tidak memiliki nilai sama sekali). Ini menyimpulkan kasus dasar.

  • Sekarang, mengingat bahwa @rhanya berisi satu byte pada awal setiap whileiterasi:

    • The forLoop eksplisit &=s semua nilai dalam hasil array dengan 255 kecuali yang terakhir , jadi kita hanya perlu melihat yang terakhir.

    • Kami tahu bahwa kami selalu menghapus hanya satu byte dari $xdan $y:

      • Oleh karena itu, $e*hexadalah perkalian dua nilai byte tunggal, yang berarti berada dalam kisaran 0 — 0xFE01.

      • Dengan hipotesis induktif, $r[$i]adalah satu byte; oleh karena itu, $s = $r[$i] += $e*hexberada dalam kisaran 0 — 0xFF00.

      • Karena itu, $s >> 8selalu satu byte.

    • $ymenumbuhkan ekstra 00di setiap iterasi whileloop:

      • Oleh karena itu, dalam setiap iterasi whileloop, forloop dalam berjalan untuk satu iterasi lebih banyak daripada whileiterasi sebelumnya .

      • Oleh karena itu, $r[++$i] += $s >> 8dalam iterasi terakhir dari forlingkaran selalu menambahkan $s >> 8untuk 0, dan kita sudah menetapkan bahwa $s >> 8selalu satu byte.

    • Oleh karena itu, nilai terakhir yang disimpan di @rakhir forloop juga merupakan byte tunggal.

Ini mengakhiri tantangan yang luar biasa dan mengasyikkan. Terima kasih banyak untuk mempostingnya!


4

Solusi C

Solusi ini tidak memvalidasi input. Ini juga hanya diuji ringan. Kecepatan sebenarnya bukan pertimbangan. Memori Malloc, dan tidak terlalu pintar tentang berapa banyak yang diambilnya. Dijamin cukup, dan lebih dari yang diperlukan.

m () menerima string, mengharapkan dua baris baru dalam string, satu setelah setiap angka. Hanya mengharapkan angka, karakter huruf kecil, spasi, dan baris baru. Mengharapkan digit hex untuk selalu menjadi pasangan.

Tidak ada operasi penggandaan yang pernah digunakan (secara sadar). Pergeseran dilakukan pada variabel 8-bit. Satu penambahan 16-bit dilakukan. Tidak ada tipe data 32-bit.

Menyusut dengan tangan, dan hanya ringan. sunting: kebingungan lebih banyak, karakter lebih sedikit: D Kompilasi dengan peringatan dengan gcc.

Karakter: 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);}

Anda dapat menguji dengan ini:

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;
}

Hasil:

$ ./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 + Baterai, 362 karakter

Algoritma perkalian anak sekolah O (n * m) standar. Perhatikan bahwa untuk memenuhi persyaratan tantangan, operasi dilakukan pada byte string, yang dalam OCaml (mudah, dalam hal ini) bisa berubah. Perhatikan juga bahwa akumulator stidak pernah meluap 16 bit, karena 2 (2 ^ 8 - 1) + (2 ^ 8 - 1) ^ 2 = (2 ^ 8 - 1) (2 ^ 8 + 1) = 2 ^ 16 - 1 .

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)))

Sebagai contoh,

# 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"

0

JavaScript (Node.js) , 160 byte

x=>y=>x.map((t,i)=>y.map(u=>(f=i=>(c=s[i]>>8)&&f(i++,s[i]=c+~~s[i],s[i-1]%=256))(i,s[i]=~~s[i++]+`0x${t}`*`0x${u}`)),s=[])&&s.map(t=>(t<16?0:'')+t.toString(16))

Cobalah online!

Banyak bahasa yang lebih baru daripada waktu itu

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.