23
\$\begingroup\$

Introduction

Let's define a new arithmetical operation, which I call zipper multiplication. To zipper multiply two nonnegative integers, you add leading zeros to make the lengths match, multiply the corresponding base-10 digits of the numbers, add leading zeros to the results to get 2-digit numbers, concatenate them, and finally drop leading zeros.

Here's an example with A = 1276 and B = 933024:

1. Add leading zeros
 A = 001276
 B = 933024

2. Multiply digit-wise
 A = 0  0  1  2  7  6
 B = 9  9  3  0  2  4
 ->  0  0  3  0 14 24

3. Pad to 2 digits
 -> 00 00 03 00 14 24

4. Concatenate
 -> 000003001424

5. Drop leading zeros
 -> 3001424

The operation is extended to all integers with the usual sign rules: positive times negative is negative, negative times negative is positive and so on.

The task

Your inputs are two integers, and your output is their zipper multiplication. You should be able to handle arbitrarily large inputs. Input and/or output can be in string format (and indeed must be, if your language doesn't support arbitrarily large integers). Note that -0 is not a valid input or output.

Rules and scoring

You can write a full program or a function, and the lowest byte count wins.

Test cases

0 0 -> 0
302 40 -> 0
302 -40 -> 0
-4352 448 -> -122016
0 6623 -> 0
0 -6623 -> 0
20643 -56721 -> -1000420803
63196 21220 -> 1203021800
1276 933024 -> 3001424
-1276 933024 -> -3001424
-1276 -933024 -> 3001424
5007204555 350073039 -> 12001545
-612137119 -8088606033 -> 816060042000327
3389903661 -6619166963 -> -18180881090018543603
-23082746128560880381 1116941217 -> -8050600723200060807
-668336881543038127783364011867 896431401738330915057436190556 -> -485448120906320001351224000900090235004021121824000900403042
402878826066336701417493206805490000415 312487283677673237790517973105761463808 -> 120004325656161618004242182118140007280900200921180018080025285400000000320040
\$\endgroup\$

15 Answers 15

9
\$\begingroup\$

Jelly, 11 10 bytes

ƓDUz0P€Uḅ³

Try it online!

I couldn't get this down to 10 bytes by myself, but @Pietu1998 pointed me to an atom I'd missed, this giving this 10-byte solution. Unusually for Jelly, this one takes input from standard input (in the form 1276,933024), not from the command line (this enables the use of the ³ command, which returns the command line argument, defaulting to 100).

Explanation:

ƓDUz0P€Uḅ³
Ɠ           read standard input
 D          convert to base 10
  U         reverse elements
   z0       transpose, padding the end with zeroes
     P€     take the product of each (€) inner list
       U    reverse elements back
        b³  convert from base 100

The use of base 100 is a simple way to implement the "pad to 2 digits, then convert to base 10" technique. The only other subtle thing here is the reversing; we want to pad with zeroes at the start of the number, but Jelly's z command pads at the end, so reversing the lists means that z will pad correctly.

\$\endgroup\$
1
  • 3
    \$\begingroup\$ You can replace b⁵ with D to get the 10 bytes. :P \$\endgroup\$ Dec 9, 2016 at 9:37
4
\$\begingroup\$

Python 2, 99 bytes

a,b=input();o=0;p=1-2*(a*b<0);a,b=abs(a),abs(b)
while a:o+=a%10*(b%10)*p;p*=100;a/=10;b/=10
print o

A lot of the bytes are there to account for the sign in case of negative input. In Python, n%d is always non-negative if d is positive1. In my opinion this is generally desirable, but here it seems inconvenient: removing the calls to abs would break the above code. Meanwhile p keeps track of the "place value" (ones, hundreds, etc.) and also remembers the desired sign of the output.

The code is basically symmetric in a and b except in the while condition: we keep going until a is zero, and terminate at that time. Of course if b is zero first, then we'll end up adding zeroes for a while until a is zero as well.


1 For example, (-33)%10 returns 7, and the integer quotient of (-33)/10 is -4. This is correct because (-4)*10 + 7 = -33. However, the zipper product of (-33) with 33 must end in 3*3 = 09 rather than 7*3 = 21.

\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 44 bytes

f=(x,y)=>x&&f(x/10|0,y/10|0)*100+x%10*(y%10)

Conveniently this automatically works for negative numbers.

\$\endgroup\$
2
  • \$\begingroup\$ @Jakube I'm always doing that, although at least I included the f= in the byte count. Also, the |0 is because I need integer division, I don't know how you think you're getting the right answer without it. \$\endgroup\$
    – Neil
    Dec 9, 2016 at 10:50
  • \$\begingroup\$ Ah, that makes sense. Now I also get wrong answers when removing |0. Maybe the reassignment of the new function to f didn't work and I still tested the old version with |0. \$\endgroup\$
    – Jakube
    Dec 9, 2016 at 11:03
2
\$\begingroup\$

C, 77 bytes

-2 bytes for removing redundant braces (* is associative).

r,t;f(a,b){t=1;r=0;while(a|b)r+=t*(a%10)*(b%10),a/=10,b/=10,t*=100;return r;}

t=1,100,10000,... is used for padding. As long as a or b is not zero keep multiplicating the last digit %10 with t and accumulate. Then erease the last digit of a and b (/=10) and shift t by 2 digits (*=100).

Ungolfed and usage:

r,t;
f(a,b){
 t=1;
 r=0;
 while(a|b)
  r+=t*(a%10)*(b%10),
  a/=10,
  b/=10,
  t*=100;
 return r;
}

main(){
 printf("%d\n", f(1276,933024));
}
\$\endgroup\$
1
  • \$\begingroup\$ Suggest for(r=0;a|b;t*=100)r+=a%10*t*(b%10),a/=10,b/=10 instead of r=0;while(a|b)r+=t*(a%10)*(b%10),a/=10,b/=10,t*=100 \$\endgroup\$
    – ceilingcat
    May 2, 2019 at 18:27
1
\$\begingroup\$

Actually, 23 19 bytes

Input is taken as two strings. Also, apparently attempting to convert from base 100, as ais523 does in their Jelly answer, doesn't work so well in Actually. Would have saved 9 bytes too if it worked :/ Golfing suggestions welcome! Try it online!

Edit: -4 bytes from changing how the result is built up into a new number.

k`♂≈R`M┬ñ`iτ╤@π*`MΣ

Ungolfing

          Implicit input a and b.
k         Wrap a and b into a list.
`...`M    Map over the list of strings.
  ♂≈        Convert each digit to its own int.
  R         Reverse for later.
┬         Transpose to get pairs of digits from a and b.
ñ         enumerate(transpose) to get all of the indices as well.
`...`M    Map over the transpose.
  i         Flatten (index, pair) onto the stack.
  τ╤        Push 10**(2*index) == 100**index to the stack.
  @π        Swap and get the product of the pair of integers.
  *         Multiply the product by 100**index.
Σ         Sum everything into one number.
          Implicit return.
\$\endgroup\$
1
\$\begingroup\$

Mathematica 66 Bytes

i=IntegerDigits;p=PadLeft;FromDigits@Flatten@p[i/@Times@@p[i/@#]]&

Ungolfed:

IntegerDigits/@{1276,933024}
PadLeft[%]
Times@@%
IntegerDigits/@%
PadLeft[%]
Flatten@%
FromDigits@%

where % means the previous output yields

{{1,2,7,6},{9,3,3,0,2,4}}
{{0,0,1,2,7,6},{9,3,3,0,2,4}}
{0,0,3,0,14,24}
{{0},{0},{3},{0},{1,4},{2,4}}
{{0,0},{0,0},{0,3},{0,0},{1,4},{2,4}}
{0,0,0,0,0,3,0,0,1,4,2,4}
3001424
\$\endgroup\$
1
\$\begingroup\$

R, 182 110 107 86 bytes

No longer the longest answer (thanks, Racket), and in fact shorter than the Python solution (a rare treat)! An anonymous function that takes two integers as input.

function(a,b)sum((s=function(x)abs(x)%%10^(99:1)%/%(e=10^(98:0))*e)(a)*s(b))*sign(a*b)

Here's how it works.

The zipper multiplication involves splitting the input numbers into their constituent digits.We take the absolute value of number and carry out modulo for descending powers of 10:

abs(x) %% 10^(99:1)

So here we're taking one number, x, and applying modulo with 99 other numbers (10^99 through 10^1). R implicitly repeats x 99 times, returning a vector (list) with 99 elements. (x %% 10^99, x %% 10^98, x %% 10^97, etc.)

We use 10^99 through 10^1. A more efficient implementation would use the value of number of digits in the longest number (check the edit history of this post; previous versions did this), but simply taking 99..1 uses fewer bytes.

For x = 1276 this gives us

1276 1276 1276 ... 1276 276 76 6

Next, we use integer division by descending powers of 10 to round out the numbers:

abs(x) %% 10^(99:1) %/% 10^(98:0)

This yields

0 0 0 ... 1 2 7 6

which is exactly the representation we want. In the code, we end up wanting to use 10^(98:0) again later, so we assign it to a variable:

abs(x) %% 10^(99:1) %/% (e = 10^(98:0))

(Wrapping an expression in parentheses in R generally evaluates the expression (in this case, assigning the value of 10^(98:0) to e), and then also returns the output of the expression, allowing us to embed variable assignments within other calculations.)

Next, we perform pairwise multiplication of the digits in the input. The output is then padded to two digits and concatenated. The padding to two digits and concatenating is equivalent to multiplying each number by 10^n, where n is the distance from the right edge, and then summing all the numbers.

 A = 0         0         1         2         7         6
 B = 9         9         3         0         2         4
 ->  0         0         3         0        14        24
 -> 00        00        03        00        14        24
 ->  0*10^6 +  0*10^5 +  3*10^4 +  0*10^3 + 14*10^2 + 24*10^1
 =  000003001424

Notably, because multiplication is commutative, we can perform the multiplication by 10^n before we multiply A by B. So, we take our earlier calculation and multiply by 10^(98:0):

abs(x) %% 10^(99:1) %/% 10^(98:0) * 10^(98:0)

which is equivalent to

abs(x) %% 10^(99:1) %/% (e = 10^(98:0)) * e

After applying this to A, we would then want to repeat this whole operation on B. But that takes precious bytes, so we define a function so we only have to write it once:

s = function(x) abs(x) %% 10^(99:1) %/% (e=10^(98:0)) * e

We do our embedding-in-parentheses trick to allow us to define and apply a function at the same time, to call this function on A and B and multiply them together. (We could have defined it on a separate line, but because we're eventually going to put all of this into an anonymous function, if we have more than one line of code then everything needs to be wrapped in curly braces, which costs valuable bytes.)

(s = function(x) abs(x) %% 10^(99:1) %/% (e=10^(98:0)) * e)(a) * s(b)

And we take the sum of all of this, and we're nearly finished:

sum((s = function(x) abs(x) %% 10^(99:1) %/% (e=10^(98:0)) * e)(a) * s(b))

The only thing to consider now is the sign of the input. We want to follow regular multiplication rules, so if one and only one of A and B is negative, the output is negative. We use the function sign which returns 1 when given a positive number and -1 when given a negative number, to output a coefficient that we multiply our entire calculation by:

sum((s = function(x) abs(x) %% 10^(99:1) %/% (e=10^(98:0)) * e)(a) * s(b)) * sign(a * b)

Finally, the whole thing is wrapped into an anonymous function that takes a and b as input:

function(a, b) sum((s = function(x) abs(x) %% 10^(99:1) %/% (e=10^(98:0)) * e)(a) * s(b)) * sign(a * b)

Remove the whitespace and it's 86 bytes.

\$\endgroup\$
3
  • \$\begingroup\$ It will be great if you could provide an ungolfed, explained version for everyone's benefit. \$\endgroup\$
    – rnso
    Dec 13, 2016 at 16:32
  • \$\begingroup\$ I've updated the post with an explanation. \$\endgroup\$
    – rturnbull
    Dec 14, 2016 at 11:39
  • \$\begingroup\$ Great job. Very clever method used. \$\endgroup\$
    – rnso
    Dec 15, 2016 at 0:55
1
\$\begingroup\$

Python 3, 92 bytes, 119 bytes

lambda m,n:(1-(n*m<0)*2)*int(''.join([f"{int(a)*int(b):02}"for a,b in zip(str(abs(n))[::-1],str(abs(m))[::-1])][::-1]))

Try it online!

Fix for handling negative numbers cost 29 bytes :/

\$\endgroup\$
5
  • \$\begingroup\$ Nice answer! I think you can replace the lstrip part by wrapping everything inside int() and returning a number. \$\endgroup\$
    – ArBo
    May 4, 2019 at 8:16
  • \$\begingroup\$ You're right. Then I felt like keeping a consistent interface. Taking strings as arguments instead of int, then returning an int looks weird to me ;) I rather hoped to change the zip+for loop for a map call, but that would not work :/ \$\endgroup\$
    – movatica
    May 4, 2019 at 8:30
  • \$\begingroup\$ I wouldn't worry too much about consistency in code golf, but it's up to you :). Mapping is usually not very golfy in Python if you'd need to make an extra lambda to do it. \$\endgroup\$
    – ArBo
    May 4, 2019 at 9:05
  • \$\begingroup\$ This function seems to fail for negative inputs \$\endgroup\$
    – ArBo
    May 4, 2019 at 10:51
  • \$\begingroup\$ You're right :/ The fix is quite expensive, maybe there's more potential for golfing it down. \$\endgroup\$
    – movatica
    May 4, 2019 at 11:13
1
\$\begingroup\$

05AB1E, 9 bytes

Tвí0ζPRтβ

Port of @user62131's Jelly answer

Try it online or verify all test cases.

Explanation:

Tв        # Convert both integers in the (implicit) input-pair to a base-10 list
  í       # Reverse each inner list
    ζ     # Zip/transpose; swapping rows/columns,
   0      # using 0 as filler for unequal length lists
     P    # Take the product of each inner pair
      R   # Reverse the list of integers
       тβ # Convert it from a base-100 list to a base-10 integer
          # (after which the result is output implicitly)
\$\endgroup\$
1
\$\begingroup\$

Julia, 70 64 bytes

Big thanx to @MarcMush for his advices!

~=digits∘abs
a\b=evalpoly(big(100),prod.(zip(~a,~b)))sign(a*b)

Attempt This Online!

\$\endgroup\$
2
  • \$\begingroup\$ -3 bytes with ~=digits∘abs instead of !a=... \$\endgroup\$
    – MarcMush
    Jul 11, 2022 at 20:17
  • \$\begingroup\$ and -3 bytes with prod.() instead of map \$\endgroup\$
    – MarcMush
    Jul 11, 2022 at 20:23
0
\$\begingroup\$

Pyke, 16 bytes

FY_),FBw�+`t)_sb

Try it here!

Where � is the byte 0x84 or 132

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

PHP, 84 bytes

for(list(,$a,$b)=$argv,$f=1;$a>=1;$a/=10,$b/=10,$f*=100)$r+=$a%10*($b%10)*$f;echo$r;

slightly longer with string concatenation (86 bytes):

for(list(,$a,$b)=$argv;$a>=1;$a/=10,$b/=10)$r=sprintf("%02d$r",$a%10*($b%10));echo+$r;
\$\endgroup\$
0
\$\begingroup\$

Racket 325 bytes

(let*((g string-append)(q quotient/remainder)(l(let p((a(abs a))(b(abs b))(ol'()))(define-values(x y)(q a 10))
(define-values(j k)(q b 10))(if(not(= 0 x j))(p x j(cons(* y k)ol))(cons(* y k)ol)))))(*(string->number
(apply g(map(λ(x)(let((s(number->string x)))(if(= 2(string-length s)) s (g "0" s))))l)))(if(<(* a b)0)-1 1)))

Ungolfed:

(define (f a b)
  (let* ((sa string-append)
         (q quotient/remainder)
         (ll (let loop ((a (abs a))
                        (b (abs b))
                        (ol '()))
               (define-values (x y) (q a 10))
               (define-values (j k) (q b 10))
               (if (not(= 0 x j))
                   (loop x j (cons (* y k) ol))
                   (cons (* y k) ol)))))
    (*(string->number (apply sa
                             (map (λ (x)
                                    (let ((s (number->string x)))
                                      (if (= 2 (string-length s))
                                          s
                                          (sa "0" s))))
                                  ll)))
      (if (< (* a b) 0) -1 1))))

Testing:

(f 1276 933024)
(f 302 40)
(f 0 6623)
(f 63196 21220)
(f 20643 -56721)

Output:

3001424
0
0
1203021800
-1000420803
\$\endgroup\$
0
\$\begingroup\$

PowerShell, 153 151 bytes

param($a,$b)do{$x,$y=$a[--$i],$b[$i]|%{if($_-eq45){$s+=$_;$_=0}$_}
$r=(+"$x"*"$y"|% t*g "00")+$r}while($x+$y)$s+$r-replace'(?<!\d)0+(?=\d)|--|-(?=0+$)'

Try it online!

Less golfed:

param($a,$b)
do{
    $x,$y=$a[--$i],$b[$i]|%{
        if($_-eq45){                                # [char]45 is '-'
            $signs+=$_
            $_=0
        }
        $_                                          # a digit or $null
    }
    $res=(+"$x"*"$y"|% toString "00")+$res          # "00" is the custom format to get 2-digit number
}
while($x+$y)
$signs+$res-replace'(?<!\d)0+(?=\d)|--|-(?=0+$)'    # cleanup and return
\$\endgroup\$
0
\$\begingroup\$

Perl 5 -MList::Util=min, 140 bytes

@a=<>=~/\S/g;@F=<>=~/\S/g;$q=1+min$#a,$#F;say+('-'x("@a@F"=~y/-//%2).sprintf'%02d'x$q,map$F[$_]*$a[$_],-$q..-1)=~s/^-?\K0+//r=~s/^-0*$//r||0

Try it online!

\$\endgroup\$

Your Answer

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

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