30
\$\begingroup\$

There is a nice way to perform long multiplication for two integers without having to do anything but counting, which occasional gets shared around the internet. You write the digits of each number as a bunch of slanted lines, with the two numbers at a 90 degree angle. Then you can simply count the intersections in the separate columns that arise. A diagram will probably clarify this. Here is an example for calculating 21 * 32:

enter image description here

If you google for "visual/graphical long multiplication" you'll find a lot more examples.

In this challenge, you are to generate these diagrams using ASCII art. For the same example, the output would look like this:

   \ /
    X /
 \ / X /
\ X / X
 X X / \ /
/ X X   X /
 / X \ / X
  / \ X / \
     X X
    / X \
     / \

It's probably easiest to figure out the construction rules for these from some examples (see below), but here some details:

  • Intersecting segments are X, non-intersecting segments of the lines are / or \.
  • There should be exactly one segment after the outermost intersections.
  • There should be exactly one segment between intersections belonging to different digits. If there are zero-digits, these will result in consecutive / or \ segments.
  • You have to support any positive input (at least up to some reasonable limit like 216 or 232), and any digits from 0 to 9. However, you may assume that there neither leading nor trailing 0s.
  • You must not print extraneous leading whitespace or leading or trailing empty lines.
  • You may print trailing whitespace but it must not exceed the diagram's axis-aligned bounding box.
  • You may optionally print a single trailing newline.
  • You may choose in which order you take the two input numbers. However, it you must support arbitrary numbers for either orientation, so you can't choose something like "The larger number is given first".
  • If you're taking input as a string, you can use any non-digit separator between the two numbers.

You may write a program or function, taking input via STDIN (or closest alternative), command-line argument or function argument and outputting the result via STDOUT (or closest alternative), function return value or function (out) parameter.

This is code golf, the shortest answer (in bytes) wins.

Examples

1*1
\ /
 X
/ \

2*61
 \ /
\ X /
 X X /
/ X X /
 / X X /
  / X X /
   / X X
    / X \ /
     / \ X
        X \
       / \

 45*1
         \ /
        \ X
       \ X \
      \ X \
     \ X \
      X \
   \ / \
  \ X
 \ X \
\ X \
 X \
/ \

21001*209
       \ /
        X /
       / X
      / / \
   \ / /   \ /
    X /     X /
 \ / X     / X /
\ X / \   / / X /
 X X   \ / / / X /
/ X \   X / / / X /
 / \ \ / X / / / X /
    \ X / X / / / X /
     X X / X / / / X /
    / X X / X / / / X
     / X X / X / / / \
      / X X / X / /
       / X X / X /
        / X X / X
         / X X / \
          / X X
           / X \
            / \
\$\endgroup\$
2
  • \$\begingroup\$ A function with 2 string parameters or just one single string and I have to split it in my code? \$\endgroup\$
    – edc65
    Commented Jun 12, 2015 at 12:30
  • \$\begingroup\$ @edc65 Two strings or even two integer parameters are fine. \$\endgroup\$ Commented Jun 12, 2015 at 12:39

10 Answers 10

4
\$\begingroup\$

python, 303

def f(s):
    a,b=s.split('*')
    a,b=map(lambda l:reduce(lambda x,y:x+[1]*int(y)+[0],l,[0]),[reversed(a),b])
    n=sum(map(len,[a,b]))
    l=[[' ']*n for i in xrange(n)]
    for i,x in enumerate(a):
        for j,y in enumerate(b):
            l[i+j][j-i+len(a)]=r' \/x'[x+2*y]
    return '\n'.join(''.join(x[2:-1]) for x in l[1:-2])

I think it's enough human-readable.
Verification:

print '---'
print '\n'.join('"%s"'%x for x in f('21001*209').split('\n'))
print '---'
---
"       \ /            "
"        x /           "
"       / x            "
"      / / \           "
"   \ / /   \ /        "
"    x /     x /       "
" \ / x     / x /      "
"\ x / \   / / x /     "
" x x   \ / / / x /    "
"/ x \   x / / / x /   "
" / \ \ / x / / / x /  "
"    \ x / x / / / x / "
"     x x / x / / / x /"
"    / x x / x / / / x "
"     / x x / x / / / \"
"      / x x / x / /   "
"       / x x / x /    "
"        / x x / x     "
"         / x x / \    "
"          / x x       "
"           / x \      "
"            / \       "
---
\$\endgroup\$
8
  • 1
    \$\begingroup\$ Just a few quick golfs: reversed is the same as [::-1], you can put the contents of the for loop into one line to save on indentation, len(a)+len(b) is shorter than sum(map(len,[a,b])), don't use xrange in golfing, the space in ) for can be removed, and since you are using python2, you can combine spaces and tabs in indentation. \$\endgroup\$
    – Maltysen
    Commented Jun 7, 2015 at 17:44
  • \$\begingroup\$ Thanks much. These gives 22 bytes. But I don't think it would be the shortest. I don't code pyth but I've seen 31-byte programs... Btw, 303 is the count when every 4-spaces is replaced by a tab actually. \$\endgroup\$ Commented Jun 7, 2015 at 18:05
  • \$\begingroup\$ Here, I was able to get 276 from simple syntactic golfing: gist.github.com/Maltysen/e8231c0a9b585e2a4941 \$\endgroup\$
    – Maltysen
    Commented Jun 7, 2015 at 18:09
  • \$\begingroup\$ Also, do you mind if I translate your program to Pyth and post it as a separate answer? \$\endgroup\$
    – Maltysen
    Commented Jun 7, 2015 at 18:10
  • 1
    \$\begingroup\$ You can set e=enumerate at the start to golf 4 chars \$\endgroup\$
    – sagiksp
    Commented Jun 8, 2017 at 10:47
2
\$\begingroup\$

Python 3, 205 bytes

L=a,b=[eval("+[0]+[1]*".join("0%s0"%x)[2:])for x in input().split()]
A,B=map(len,L)
for c in range(2,A+B-1):print((" "*abs(c-A)+" ".join(" \/X"[a[i-c]+2*b[i]]for i in range(max(0,c-A),min(c,B))))[1:A+B-2])

The expressions are quite long so I think there's a fair amount of room for improvement, but anyway...

Takes input space-separated via STDIN, e.g.

21 32
   \ /
    X /
 \ / X /
\ X / X  
 X X / \ /
/ X X   X /
 / X \ / X 
  / \ X / \
     X X  
    / X \
     / \

There's a possible trailing space on some lines, but the A+B-2 ensures that all of the trailing spaces are within the bounding box.

\$\endgroup\$
1
\$\begingroup\$

Pyth - 79 bytes

A translation of @AlexeyBurdin's answer. Can probably be golfed a lot more.

AzHmu++Gm1sH]Zd]Z,_zwK+lzlHJmm\ KK .e.eX@J+kY+-Yklz@" \/x"+byZHzjbmjk:d2_1:J1_2

Takes input as two numbers, newline separated. Explanation coming soon.

Try it online here.

\$\endgroup\$
1
\$\begingroup\$

C#, 451 bytes

void d(string s){var S=s.Split('*');int X=S[1].Select(c=>c-47).Sum(),Y=S[0].Select(c=>c-47).Sum(),L=9*(X+Y),A=1,B=L/3,i,j;var a=Range(0,L).Select(_=>new int[L]).ToArray();foreach(var c in S[1]){for(i=48;i<c;++i){for(j=-1;j<Y;++j)a[B-j][A+j]=1;A++;B++;}A++;B++;}A=1;B=L/3;foreach(var c in S[0]){for(i=48;i<c;++i){for(j=-1;j<X;++j)a[B+j][A+j]|=2;A++;B--;}A++;B--;}Write(Join("\n",a.Select(r=>Concat(r.Select(n=>@" /\X"[n]))).Where(r=>r.Trim().Any())));}

Formatted for readability, the function in context:

using System.Linq;
using static System.Console;
using static System.Linq.Enumerable;
using static System.String;

class VisualMultiply
{
    static void Main(string[] args)
    {
        new VisualMultiply().d("21001*209");

        WriteLine();
    }

    void d(string s)
    {
        var S = s.Split('*');

        int X = S[1].Select(c => c - 47).Sum(), 
            Y = S[0].Select(c => c - 47).Sum(),
            L = 9 * (X + Y),
            A = 1,
            B = L / 3,
            i,
            j;

        var a = Range(0, L).Select(_ => new int[L]).ToArray();

        foreach (var c in S[1])
        {
            for (i = 48; i < c; ++i)
            {
                for (j = -1; j < Y; ++j)
                    a[B - j][A + j] = 1;
                A++;
                B++;
            }
            A++;
            B++;
        }

        A = 1;
        B = L / 3;
        foreach (var c in S[0])
        {
            for (i = 48; i < c; ++i)
            {
                for (j = -1; j < X; ++j)
                    a[B + j][A + j] |= 2;
                A++;
                B--;
            }
            A++;
            B--;
        }

        Write(Join("\n", a.Select(r => Concat(r.Select(n => @" /\X"[n]))).Where(r => r.Trim().Any())));
    }
}

The bitwise OR was just for fun, but addition would work too.

\$\endgroup\$
1
\$\begingroup\$

JavaScript (ES6) 271

I'm sure that there is a solution that builds the output row by row, fiddling with math and x,y coordinates (x+y==k, x-y==k ...). But I still can't nail it.

So here is a solution that simply draws the lines one by one.

Run the snippet in Firefox to test.

F=(a,b)=>( // string parameters
  t=u=0,[for(v of a)t-=~v],[for(v of b)u-=~v],
  r=t+u,o=[...' '.repeat(r*r-r)],
  L=(x,y,n,z,m,c)=>{
    for(i=0;d=n[i++];)
      for(j=0;++x,y+=z,j++<d;)
        for(l=m+1,p=x+y*r-1-r;l--;p+=r-z,o[p-p%r-1]='\n')
          o[p]=o[p]>' '&&o[p]!=c?'X':c
  },
  L(u,0,a,1,u,'/'),
  L(0,u,b,-1,t,'\\'),
  o.join('')
)

// TEST

function test()
{
  O.innerHTML= F(A.value, B.value);
}

test();
<input id=A value=21001> * <input id=B value=209> <button onclick='test()'>-></button>
<pre id=O></pre>

\$\endgroup\$
1
\$\begingroup\$

VC++ (289)280

t(char*a){int i,j,k,r,c=1,e,A=0,B=0,*C,G[99],f,u;for(C=&A;c+48|(k=(c=(*(a+=c<1))---48)>0);G[2**(C=!(c+6)?&(B+=A):&(++*C))]=k**C);for(i=0;i<B*B;printf("\n%c"+!!j,32+15*((k=(c<(f=G[(c=i/B)+(j=i%B)+A+2]))*(j<f)*(f>A))+4*(r=(!!e*B>c+(e=G[A+j-c]))*(!!e*c>A-e-2)*(e<A)))+13*k*r),i++);

Usage

#include  <stdio.h>
#include  <conio.h>

int t(char*);

int main(void)
{   char a[]="123*45";
    t((char*)a);
    getch();
    return 0;
}

int 
//-------- padded code ------
t(char*a){int i,j,k,r,c=1,e,A=0,B=0,*C,G[99],f,u;memset(G,0,396);for(C=&A;c+48|(k=(c=(*(a+=c<1))---48)>0);G[2**(C=!(c+6)?&(B+=A):&(++*C))]=k**C);for(i=0;i<B*B;printf("\n%c"+!!j,32+15*((k=(c<(f=G[(c=i/B)+(j=i%B)+A+2]))*(j<f)*(f>A))+4*(r=(!!e*B>c+(e=G[A+j-c]))*(!!e*c>A-e-2)*(e<A)))+13*k*r),i++);

//---------------------------
return 0;}

Results

       \ /
      \ x /
     \ x x /
      x x x /
   \ / x x x
  \ x / x x \ /
   x x / x \ x /
\ / x x / \ x x /
 x / x x   x x x /
/ x / x \ / x x x /
 / x / \ x / x x x
  / x   x x / x x \
   / \ / x x / x \
      x / x x / \
     / x / x x
      / x / x \
       / x / \
        / x
         / \
  • the function iterates one single loop , and use some geometricks and ascii trifling.
\$\endgroup\$
5
  • \$\begingroup\$ What's the ---48 for? \$\endgroup\$ Commented Jun 14, 2015 at 12:56
  • \$\begingroup\$ @LegionMammal978 sometimes i write things then i forget even why did i put it there :D anyway im busy doing something else, when i finish , ill remember for you ; (does this code go well in your compilater) ? \$\endgroup\$
    – Abr001am
    Commented Jun 14, 2015 at 13:12
  • \$\begingroup\$ @LegionMammal978 here ,array content at specific (actual) index is subtracted to 48 before it decrements , subtracting 48 in order to wait a coming nil character then decrement step by step forward in loop (or backwards as ascii pattern) \$\endgroup\$
    – Abr001am
    Commented Jun 14, 2015 at 15:52
  • \$\begingroup\$ 48 is the ascii representation of "0" \$\endgroup\$
    – Abr001am
    Commented Jun 14, 2015 at 15:55
  • 1
    \$\begingroup\$ I see now, it works like ...)-- - 48).... \$\endgroup\$ Commented Jun 20, 2015 at 17:23
1
\$\begingroup\$

Canvas, 41 bytes

(A┬{[1}0}┐)
⁷L├X⁷lYø;{³×?³y³-╵x\╋
⁸⤢↔╶╶⁸n

Try it here!

\$\endgroup\$
0
\$\begingroup\$

C (329 b)

int f(char*a){int i,j,r,k,c,h,o,e=15,m=99,A=0,B=0,*C,L[m][m],G[m],*g=G;for(C=&A;(c=*a-48)+48;C=!(c+6)?&B:&(*C+=(*g++=c+1)),a++);for(i=B-1,j=0;j<(r=A+B-1);i--,j++)for(k=0,o=4*!!((*(g-=!*g))---1);k<=*(C=(h=i<0)?&B:&A);k++)L[abs(i)+k][j+k-2*k*h]+=o/(3*h+1)*e;for(i=0;i<r*r;i++)printf("\n%c"+!!(i%r),((h=L[i/r][i%r])>e*4)?120:h+32);}

TRY IT

\$\endgroup\$
2
  • \$\begingroup\$ It seems there are columns of spaces after every character, and the final non-intersecting segments at the bottom ends are missing. You're also using the digits in reverse order. \$\endgroup\$ Commented Jun 9, 2015 at 15:46
  • \$\begingroup\$ @MartinBüttner imagine someone is doing this on the moon and u watching it by telescope , that s the way you should perceive the diagram (-joking-i ll adjust that later) \$\endgroup\$
    – Abr001am
    Commented Jun 9, 2015 at 16:52
0
\$\begingroup\$

R, 294 bytes

d=do.call;r=outer;m=d(r,c(Map(function(y,w)d(c,c(lapply(y%/%10^rev(0:log10(y))%%10,function(z)c(0,rep(w,z))),0)),scan(),1:2),`+`))+1;p=matrix(" ",u<-sum(dim(m)),u);p[d(r,c(lapply(dim(m),seq),function(a,b)nrow(m)-a+b+u*(a+b-2)))]=c(" ","\\","/","X")[m];cat(apply(p,1,paste,collapse=""),sep="\n")

Try it online!

\$\endgroup\$
0
\$\begingroup\$

Jelly, 58 bytes

ŒDṙLN‘ƊṚị“\/X ”K€
L‘0xż1xⱮ$F
DÇ€Ḥ2¦+þ/µZJUṖ;J’⁶xⱮżÑṖḊẎḊ$€Y

Try it online!

Explanation

A full program that takes the two numbers as a list of two integers and returns a string.

Helper link 1: rotate the matrix

ŒD                        | get the diagonals
  ṙ                       | rotate left by
   LN‘Ɗ                   | minus num columns +1
       Ṛ                  | reverse order
        ị“\/X ”           | index into \/X
               K€         | join each with spaces

Helper link 2: generate the row and column templates

L‘0x                      | 0 copied (num digits + 1) times
    ż                     | interleaved with
     1xⱮ$                 | 1 copied as specified by the digits
         F                | flattened

Main link

D                         | convert to decimal digits
 ǀ                       | call above link for each number
   Ḥ2¦                    | double the second one
      +þ/                 | outer product using +
         µ                | start a new monadic chain
          ZJUṖ;J’         | reversed range counting down
                          | the columns, followed by range
                            counting up the rows (without
                            duplicating 0 in the middle)
                   ⁶xⱮ    | that many spaces (to provide indents)
                      ż   | interleaved with
                       Ñ  | rotated matrix
                        ṖḊẎḊ$€ Y | remove blank rows and columns and join with newlines
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.