Zipper multiplication

20

1

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

Zgarb

Posted 2016-12-09T07:50:18.870

Reputation: 39 083

Answers

8

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.

user62131

Posted 2016-12-09T07:50:18.870

Reputation:

3You can replace b⁵ with D to get the 10 bytes. :P – PurkkaKoodari – 2016-12-09T09:37:32.550

4

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.

mathmandan

Posted 2016-12-09T07:50:18.870

Reputation: 943

3

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.

Neil

Posted 2016-12-09T07:50:18.870

Reputation: 95 035

@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. – Neil – 2016-12-09T10:50:28.587

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. – Jakube – 2016-12-09T11:03:03.427

2

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

Karl Napf

Posted 2016-12-09T07:50:18.870

Reputation: 4 131

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 – ceilingcat – 2019-05-02T18:27:26.707

1

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.

Sherlock9

Posted 2016-12-09T07:50:18.870

Reputation: 11 664

1

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.

rturnbull

Posted 2016-12-09T07:50:18.870

Reputation: 3 689

It will be great if you could provide an ungolfed, explained version for everyone's benefit. – rnso – 2016-12-13T16:32:30.600

I've updated the post with an explanation. – rturnbull – 2016-12-14T11:39:10.753

Great job. Very clever method used. – rnso – 2016-12-15T00:55:15.787

1

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

Kelly Lowder

Posted 2016-12-09T07:50:18.870

Reputation: 3 225

1

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 :/

movatica

Posted 2016-12-09T07:50:18.870

Reputation: 635

Nice answer! I think you can replace the lstrip part by wrapping everything inside int() and returning a number. – ArBo – 2019-05-04T08:16:57.320

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 :/ – movatica – 2019-05-04T08:30:36.157

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. – ArBo – 2019-05-04T09:05:49.220

This function seems to fail for negative inputs – ArBo – 2019-05-04T10:51:35.557

You're right :/ The fix is quite expensive, maybe there's more potential for golfing it down. – movatica – 2019-05-04T11:13:14.480

0

Pyke, 16 bytes

FY_),FBw�+`t)_sb

Try it here!

Where � is the byte 0x84 or 132

Blue

Posted 2016-12-09T07:50:18.870

Reputation: 26 661

0

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;

Titus

Posted 2016-12-09T07:50:18.870

Reputation: 13 814

0

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

rnso

Posted 2016-12-09T07:50:18.870

Reputation: 1 635

0

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!

Xcali

Posted 2016-12-09T07:50:18.870

Reputation: 7 671

0

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

mazzy

Posted 2016-12-09T07:50:18.870

Reputation: 4 832