การคูณเอธิโอเปีย


17

คำถามนี้ได้รับแรงบันดาลใจจากคำตอบนี้ บังเอิญฉันเคยใช้การคูณเอธิโอเปียเมื่อฉันยังเป็นเด็ก แต่ไม่เคยรู้จักชื่อของวิธีนี้มาก่อนจนกระทั่งเมื่อไม่นานมานี้

การคูณเอธิโอเปียเป็นวิธีการคูณจำนวนเต็มโดยใช้การบวก, การเพิ่มและการแบ่งครึ่งเท่านั้น

วิธี:

  1. นำตัวเลขสองตัวมาคูณและจดไว้ที่ด้านบนของสองคอลัมน์
  2. ในคอลัมน์ซ้ายมือให้ลดจำนวนสุดท้ายซ้ำ ๆ ละครึ่งละทิ้งเศษที่เหลือและเขียนผลลัพธ์ด้านล่างสุดท้ายในคอลัมน์เดียวกันจนกว่าคุณจะเขียนค่า 1
  3. ในคอลัมน์ด้านขวาซ้ำสองครั้งเป็นจำนวนสุดท้ายและเขียนผลลัพธ์ด้านล่าง หยุดเมื่อคุณเพิ่มผลลัพธ์ในแถวเดียวกับที่คอลัมน์ซ้ายมือแสดง 1
  4. ตรวจสอบตารางที่สร้างและทิ้งแถวใด ๆ ที่ค่าในคอลัมน์ด้านซ้ายเป็นเลขคู่ รวมค่าในคอลัมน์ขวามือที่ยังคงอยู่เพื่อสร้างผลลัพธ์ของการคูณสองตัวเลขเดิมเข้าด้วยกัน

ตัวอย่างเช่น: 17 x 34

17    34

แบ่งครึ่งคอลัมน์แรก:

17    34
 8
 4
 2
 1

สองเท่าของคอลัมน์ที่สอง:

17    34
 8    68
 4   136 
 2   272
 1   544

แถวนัดหยุดงานซึ่งมีเซลล์แรกเป็นคู่เราจะทำสิ่งนี้โดยการใส่ตัวเลขเหล่านั้นทางด้านขวาในวงเล็บเหลี่ยม:

17    34
 8   [68]
 4  [136]
 2  [272]
 1   544

รวมตัวเลขที่เหลืออยู่ในคอลัมน์ทางขวา:

17    34
 8   [68]
 4  [136]
 2  [272]
 1   544
    =====
     578

ดังนั้น 17 คูณด้วย 34, โดยวิธีเอธิโอเปียคือ 578

งาน:

รหัสกอล์ฟที่ใช้ตัวเลขสองตัวระหว่าง 1 ถึง 1,000 และใช้เค้าโครงและอัลกอริธึมเดียวกันแสดงผลิตภัณฑ์ด้านล่าง

วิธีการป้อนข้อมูล: อย่างไรก็ตามคุณเลือก ...

อินพุตตัวอย่าง:

19 427

ผลลัพธ์ที่ได้:

19   427
 9   854
 4 [1708]
 2 [3416]
 1  6832
   ======
    8113

โปรดทราบการจัดตำแหน่งของตัวเลข สิ่งนี้สำคัญที่สุดในโครงร่าง นอกจากนี้โปรดทราบว่าเส้นคู่ที่จัดวางโดยเครื่องหมายเท่ากับต้องมีความยาวสองอักขระเกินกว่าคำตอบโดยรวมและต้องอยู่กึ่งกลาง

การทดสอบ

คุณจะทดสอบสิ่งนี้อย่างไร โดยการให้โปรแกรมของคุณทำงานโดยใช้ตัวเลขสองตัว หมายเลขเหล่านี้สามารถดึงมาจากหมายเลข ID ผู้ใช้ของคุณ (ซึ่งสามารถรับได้โดยการเลื่อนเคอร์เซอร์ไปที่รูปประจำตัวของคุณในหน้าต่างด้านบน) ใช้หมายเลขของคุณและนำตัวเลขสามหลักสุดท้ายซึ่งจะเป็นหมายเลข B นำสิ่งอื่นที่เหลืออยู่ที่ด้านหน้าซึ่งจะเป็นหมายเลข A จากนั้นทดสอบ A ครั้ง B

ตัวอย่างการทดสอบ:

หมายเลข ID ผู้ใช้ของฉันคือ 8555 ดังนั้นตัวเลขของฉันคือ 8 และ 555 ดังนั้นผลลัพธ์ของฉันควรเป็นดังนี้:

8  [555]
4 [1110]
2 [2220]
1  4440
  ======
   4440

ข้อ จำกัด:

ไม่มีตัวดำเนินการคูณแบบดั้งเดิมที่ได้รับอนุญาตให้บันทึกในการใช้ "เสแสร้ง" ตามที่กล่าวไว้ในอัลกอริทึม กล่าวอีกนัยหนึ่งหากคุณใช้โอเปอเรเตอร์เช่น * จะสามารถใช้สำหรับการคูณด้วย 2 เท่านั้น

รายการที่ไม่เป็นไปตามนี้จะไม่ได้รับการพิจารณาและผู้ใช้จะถูกพาออกจากสถานที่ด้วยกล่องกระดาษที่เต็มไปด้วยข้าวของ แต่ละรายการจะมีรหัสรวมถึงการทดสอบตามหมายเลข ID ผู้ใช้ของคุณ

นี่คือรหัสกอล์ฟ จำนวนไบต์ที่สั้นที่สุดจะได้รับรางวัลเกียรติยศและความชื่นชมจากเพื่อน ๆ ของพวกเขา ... (และอาจเป็น Lamborghini ... ฉันพูดว่า "อาจจะ"!)


5
"ไม่ต้องมีการคูณจริง" - สิ่งนี้ไม่สามารถสังเกตได้ คุณอาจ จำกัด การใช้อักขระบางตัว (เช่น*หรือx) แต่เป็นไปไม่ได้ที่จะตรวจสอบว่ามีการใช้การคูณหรือไม่ ยกเว้นส่วนนั้นความท้าทายนั้นน่าสนใจ

บางทีคุณควรถามรายละเอียดทั้งหมดของรหัสเพื่อพิสูจน์ว่าอัลกอริทึมนั้นถูกนำไปใช้โดยไม่มีการคูณหรือการจำลองแบบไม่ จำกัด ที่ให้ผลลัพธ์ที่ต้องการ แต่นั่นเป็นความท้าทายที่แตกต่างสำหรับฉัน
Arnauld

1
ตามที่ระบุไว้ในทรายที่เกี่ยวข้องกับการตกเป็นเหยื่อที่เป็นไปได้ @ FelixPalmen ใช่นี่คือการคูณแบบยาวในไบนารี
Peter Taylor

คำตอบ:


8

ถ่าน , 91 ไบต์

≔⟦⟧τ≔⁰σNθNηWθ«⊞τ⪫  Iθ⊞υ⪫⎇﹪θ²  ¦[]Iη≔⁺σ∧﹪θ²ησ≔÷θ²θ≔⁺ηηη»⊞υ…=⁺²LIσ⊞υ⪫  Iσ←E⮌τ⮌ιM⌈EυLιLυ←E⮌υ⮌ι

ลองออนไลน์! การเชื่อมโยงคือการใช้รหัสเวอร์ชันอย่างละเอียด คำอธิบาย:

≔⟦⟧τ≔⁰σ

ชุดtไปยังรายการที่ว่างเปล่าและเพื่อs 0( uมีค่าเริ่มต้นเป็นรายการว่างอยู่แล้ว)

NθNη

ใส่ตัวเลขสองตัว

Wθ«

ทำซ้ำในขณะที่qไม่ใช่ศูนย์

   ⊞τ⪫  Iθ

ห่อในการขยายและผนวกไปที่รายการqt

   ⊞υ⪫⎇﹪θ²  ¦[]Iη

Wrap hทั้งใน padding หรือ[]ขึ้นอยู่กับว่าเป็นเลขคี่และผนวกไปที่รายการqu

   ≔⁺σ∧﹪θ²ησ

เพิ่มhไปsถ้าqเป็นเลขคี่

   ≔÷θ²θ

จำนวนเต็มหารqด้วย 2

   ≔⁺ηηη»

เพิ่มhไปเอง

⊞υ…=⁺²LIσ

ผนวกสตริงที่เหมาะสมของสัญญาณในรายการ=u

⊞υ⪫  Iσ

ผนวกรวมเบาะในรายการsu

←E⮌τ⮌ι

หมุนรายการt180 °และพิมพ์ขึ้นโดยหงายขึ้น

M⌈EυLιLυ←E⮌υ⮌ι

เลื่อนเคอร์เซอร์เพื่อให้uมีการจัดชิดขอบด้านขวาบนให้ตรงกับมุมบนขวาที่เราเพิ่งไปถึงและพิมพ์uชิดขอบขวา


การทำงานที่น่าตื่นตาตื่นใจ. คุณมีผู้นำอยู่แล้ว @Neil ฉันจะหาข้อมูลเพิ่มเติมเกี่ยวกับภาษาได้ที่ไหนมีลิงค์หรือไม่
WallyWest

1
@WallyWest ชื่อมีการเชื่อมโยงไปยังหน้า GitHub และจากนั้นคุณสามารถอ่านวิกิสำหรับข้อมูลเพิ่มเติม
Neil

8

Python 2 , 203 202 187 133 ไบต์

a,b=input()
s=0
while a:print'%3s%9s'%(a,'[ %%dd] '[a%2::2]%b);s+=[0,b][a%2];a/=2;b*=2
print'%10s==\n%11s'%(''.rjust(len(`s`),'='),s)

ลองออนไลน์!

ถ้าฉันสามารถใช้*สำหรับการคูณสตริง ( '='*R) และเป็น 'ตัวเลือก' ( b*(a%2)แทน[0,b][a%2]) ฉันจะได้รับ:

118 ไบต์

a,b=input()
s=0
while a:print'%3s%9s'%(a,'[ %%dd] '[a%2::2]%b);s+=a%2*b;a/=2;b*=2
print'%10s==\n%11s'%('='*len(`s`),s)

ลองออนไลน์!


คำอธิบาย:

a,b=input()                   #Get input
L=len(`a`)                    #Get length of first number for adjusting text
l=[]                          #Output list
s=0                           #Sum
while a:
 B=['[%d]',' %d '][a%2]%b     #B is either '[b]' or ' b ' depending on if a is odd/even
 l+=[(`a`,B)]                 #Add a,B to output list
 s+=[0,b][a%2]                #Add b to sum if a is odd
 a/=2;                        #Halve a
 b*=2;                        #Double b
R=len(B)                      #Length of last B for adjusting output
l+=[('',''.rjust(R,'='))]     #Add double line ==== to output list
l+=[('','%d '%s)]             #Add sum to output list
for x,y in l:
 print x.rjust(L),y.rjust(R)  #Print adjusted numbers


4

Java (OpenJDK 8) , 353 316 267 214 210 ไบต์

(a,b)->{int g=0;for(;a>0;g+=a%2*b,a/=2,b*=2)System.out.printf("%1$8d%2$10s\n",a,a%2<1?"["+b+"]":b+" ");System.out.printf("%1$19s%2$18s","".valueOf(new char[(int)Math.log10(g)+3]).replace("\0","=")+"\n",g+" ");}

ลองออนไลน์!


1
214 ไบต์:(a,b)->{int g=0;for(;a>0;g+=a%2*b,a/=2,b*=2)System.out.printf("%1$8d%2$10s\n",a,a%2<1?"["+b+"]":" "+b+" ");System.out.printf("%1$19s%2$18s","".valueOf(new char[(int)Math.log10(g)+3]).replace("\0","=")+"\n",g+" ");}
2560

@ วันที่a%2*bดีและเรียบง่ายขอบคุณ
Roberto Graham

4

Mathematica, 264 ไบต์

(s=#;k=(i=IntegerLength)@s;t=#2;w=0;P=Print;T=Table;While[s>0,If[OddQ@s,P[""<>T[" ",k-i@s],s,"  ",""<>T[" ",i[s(t)]-i@t],t];w=w+t,P[""<>T[" ",k-i@s],s,""<>T[" ",i[s(t)]-i@t]," [",t,"]"]];s=Quotient[s,2];t=2t];P[" "<>T[" ",k],""<>T["=",i@w+2]];P["  "<>T[" ",k],w])&


อินพุต

[19427]

เอาท์พุต

19   427  
 9   854  
 4 [1708]  
 2 [3416]  
 1  6832  
   ======  
    8113  

คุณสามารถบันทึกมหันต์หนึ่งไบต์โดยใช้สัญกรณ์มัดบนs=Quotient[s,2]:)
numbermaniac

3

Perl 5 , 157 ไบต์

155 ไบต์ของโค้ด + 2 การตั้งค่าสถานะบรรทัดคำสั่ง ( -nl)

$\=<>;$w=y///c;$y=2+length$\<<((log)/log 2);while($_){$s+=$\if$_%2;printf"%${w}s %${y}s\n",$_,$_%2?$\.$":"[$\]";$_>>=1;$\<<=1}say$"x++$w,'='x$y;say$"x++$w,$s

ลองออนไลน์!


3

JavaScript 2017, 221 ไบต์

ปัญหาส่วนใหญ่ของการจัดรูปแบบผลลัพธ์

(a,b)=>{for(t=b,r=0,l=[],w=`${a}`.length;a;l.push([a,t]),a>>=1,t+=t)z=`${r+=a&1&&t}`.length+2;P=(s,w)=>`${s}`.padStart(w);return[...l.map(([a,b])=>P(a,w)+P(a&1?b+' ':`[${b}]`,z+1)),P('='.repeat(z),z-~w),P(r,z+w)].join`
`}

น้อย golfed

(a, b) => {
  var w=`${a}`.length, r=0, l=[]
  while(a) {
    r += a&1 && b
    l.push([a,b])
    a >>= 1
    b += b
  }
  // algo complete, result in r, now display it and the steps in l[]
  var z=`${r}`.length+2
  var P= (s,w) => `${s}`.padStart(w)
  return [... l.map( ([a,b]) => P(a,w) + P(a&1?b+' ' : `[${b}]`, z+1) )
    , P('='.repeat(z), z+w+1)
    , P(r, z+w)
  ].join`\n`
}

ทดสอบ

var F=
(a,b)=>{for(t=b,r=0,l=[],w=`${a}`.length;a;l.push([a,t]),a>>=1,t+=t)z=`${r+=a&1&&t}`.length+2;P=(s,w)=>`${s}`.padStart(w);return[...l.map(([a,b])=>P(a,w)+P(a&1?b+' ':`[${b}]`,z+1)),P('='.repeat(z),z-~w),P(r,z+w)].join`
`}

function update(){
  var i=I.value, [a,b]=i.match(/\d+/g)
  O.textContent=F(+a,+b)
}

update()
<input id=I value='21x348' oninput='update()'><pre id=O></pre>


เพิ่งกลับมาถามคำถามนี้อีก ... PadStart ทำอะไรกันแน่ ฉันไม่รู้จักวิธีนี้ ...
WallyWest


จะดูดเพื่อให้ทำงานใน IE! ;)
WallyWest

3

C, C ++, 319 313 301 299 ไบต์

-8 ไบต์ขอบคุณZacharý

ขอบคุณมากค่ะ printfเวทย์มนตร์ที่เพิ่งเรียนรู้ใน 60 นาทีระหว่างการแก้ไข

#include<string.h>
#include<stdio.h>
#define O printf("%*d %c%*d%c\n",5,a,a%2?32:91,9,b,a%2?32:93);
void m(int a,int b){int r=0,i=0;O while(a>1){r+=a%2*b;a/=2;b*=2;O}r+=b;char t[20],p[20];memset(t,0,20);memset(p,0,20);sprintf(t,"%d",r);memset(p,61,strlen(t)+2);printf("%*c%*s\n%*d",5,32,12,p,16,r);}

การเพิ่มประสิทธิภาพ C ++ แทนที่ส่วนหัวstdio.hด้วยcstdioและstring.h by cstring, ประหยัด 2 byte

การคอมไพล์ด้วย MSVC จำเป็นต้องเพิ่ม #pragma warning(disable:4996)เพื่อใช้งานsprintf

ทดสอบด้วย ID PPCG ของฉัน:

72 x 535 =>

   72 [      535]
   36 [     1070]
   18 [     2140]
    9       4280
    4 [     8560]
    2 [    17120]
    1      34240
          =======
           38520

มันเคารพกฎตัวเลขจะถูกจัดตำแหน่งและสัญญาณที่เท่ากันจะเป็น 2 อักขระที่ใหญ่กว่าหมายเลขสุดท้ายเสมอ ตัวอย่างด้วย 17 x 34 =>

   17         34
    8 [       68]
    4 [      136]
    2 [      272]
    1        544
            =====
             578

ฉันคิดว่าคุณสามารถเปลี่ยนสองบรรทัดสุดท้ายเป็น#define O printf("%*d %c%*d%c\n",5,a,a%2?' ':'[',9,b,a%2?' ':']');และvoid m(int a,int b){int r=0,i=0;O while(a>1){r+=a%2*b;a/=2;b*=2;O}r+=b;char t[20],p[20];memset(t,0,20);memset(p,0,20);sprintf(t,"%d",r);for(;i<strlen(t)+2;++i)p[i]='=';printf("%*c%*s\n%*d",5,' ',12,p,16,r);}
Zacharý

ใช่ฉันรู้อย่างนั้น แต่ทำไมมันถึงสำคัญ โฆษณามีความสำคัญ%และ*เหมือนกันดังนั้นr+=a%2*bควรใช้งานได้
Zacharý

@ Zacharýอันที่จริงฉันผิดคุณพูดถูก
HatsuPointerKun

คุณไม่จำเป็นที่จะต้องรวม <cstdio> คุณจะไม่สามารถใช้เคล็ดลับเดียวกับที่คุณทำที่นี่ ?
Zacharý


3

[Bash], 144 142 140 131 128 ไบต์

การแสดงผลที่ดีขึ้นโปรดทราบว่ามีอักขระเว้นวรรคต่อท้าย

read a b;for((;a;));{ ((a%2))&&((r+=b))&&x=$b\ ||x=[$b];printf %3s%9s\\n $a "$x"
((a/=2,b+=b));};printf %12s\\n =${r//?/=}= $r\ 

คำตอบแรก

read a b;while((a));do ((a%2))&&((r+=b))&&printf "%6s  %6s
" $a $b||printf "%6s [%6s]
" $a $b;((a/=2,b+=b));done;printf "%6s %7s
" \  ==== \  $r

2

Haskell , 305 ไบต์

i=iterate
s=show
l=length.s
a!b=zip((takeWhile(>0).i(`div`2))a)(i(*2)b)
a?b=sum[y|(x,y)<-a!b,rem x 2>0]
a%b=l(snd.last$a!b)
a#b=unlines$[(' '<$[1..l a-l x])++s x++(' '<$[-1..a%b-l y])++if mod x 2<1then show[y]else(' ':s y)|(x,y)<-a!b]++map((++)(' '<$[-1..l a+a%b-l(a?b)]))['='<$[1..l a+1+a%b],' ':(s$a?b)]

ลองออนไลน์!

!ผู้ประกอบการสร้างสองรายการ?คำนวณสินค้า %และ#ใช้สำหรับเค้าโครง ASCII


1

C, 205 201 190 183 156 150 143 ไบต์

สิ่งนี้จะรวบรวมคำเตือนในฐานะ C89 และฉันไม่เชื่อว่ามันเป็น C99 ที่ถูกต้อง แต่มันก็เล็กลงกว่ารุ่นของ HatsuPointerKun เนื่องจากจะช่วยประหยัดไบต์ด้วยการ#includeใช้คำสั่งไม่ใช่การใช้ความยาวแบบไดนามิกในการพิมพ์ตามที่ไม่จำเป็น ใช้log10()ในการคำนวณจำนวน=ที่ต้องการ:

r;m(a,b){r=0;while(a){printf(a%2?"%4d%10d\n":"%4d [%8d]\n",a,b);r+=a%2?b:0;a/=2;b<<=1;}printf("%15.*s\n%14d",(int)log10(r)+3,"==========",r);}

ตามหมายเลขของ64586ฉันฉันใช้โปรแกรมทดสอบนี้เพื่อคำนวณ64 * 586:

#include <stdio.h>
int m(int a, int b);
int main(void)
{
    m(64, 586);
    putchar('\n');
}

& เอาท์พุท:

  64 [     586]
  32 [    1172]
  16 [    2344]
   8 [    4688]
   4 [    9376]
   2 [   18752]
   1     37504
        =======
         37504

แก้ไข

บันทึกแล้ว 4 ไบต์โดยกฎ "implicit int"

แก้ไข 2

บันทึก 11 ไบต์โดยเปลี่ยนเป็นdo...while()ลูปและย้าย printf ไปเป็นลูปจากมาโคร นอกจากนี้ควรทำงานอย่างถูกต้องถ้าa=1นอกจากนี้ยังควรจะทำงานอย่างถูกต้องถ้า

แก้ไข 3

บันทึกแล้ว 7 ไบต์และทำให้รหัสทำงานถูกต้อง

แก้ไข 4

บันทึก 26 ไบต์ด้วยเล่ห์กลของ printf

แก้ไข 5

บันทึก 6 ไบต์ด้วยการยุบส่วนเสริมพิเศษเป็น 1 หมายเลข

แก้ไข 6

บันทึก 7 ไบต์โดย printfery กับผู้ประกอบการที่ประกอบไปด้วย & ไม่ประกาศตัวแปรที่ไม่ได้ใช้


เยี่ยมมากจัสติน! หวังว่าจะได้เห็นคุณมากขึ้นในอีกไม่กี่สัปดาห์ข้างหน้า!
WallyWest

ขอขอบคุณ. ฉันหวังว่าจะได้มากขึ้นในอีกไม่กี่สัปดาห์ข้างหน้าเช่นกัน
JustinCB

1

Excel VBA ขนาด 183 ไบต์

ฟังก์ชันหน้าต่าง VBE แบบไม่ระบุชื่อแบบทันทีที่รับอินพุตจากช่วง[A1:B1]และเอาต์พุตไปยังคอนโซล

a=[A1]:b=[B1]:While a:c=a Mod 2=0:?Right(" "& a,2);Right("   "&IIf(c,"["&b &"]",b &" "),7):s=s+IIf(c,0,b):a=Int(a/2):b=b*2:Wend:?Right("     "&String(Len(s)+2,61),9):?Right("    "&s,8)

Ungolfed

Sub EthiopianMultiply(ByVal a As Integer, b As Integer)
    While a
        Let c = a Mod 2 = 0
        Debug.Print Right(" " & a, 2);
        Debug.Print Right("    " & IIf(c, "[" & b & "]", b & " "), 7)
        Let s = s + IIf(c, 0, b)
        Let a = Int(a / 2)
        Let b = Int(b * 2)
    Wend
    Debug.Print Right("     " & String(Len(s) + 2, 61), 9)
    Debug.Print Right("     " & s, 8)
End Sub

เอาท์พุต

61   486 
30  [972]
15  1944 
 7  3888 
 3  7776 
 1 15552 
  =======
   29646
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.