Linear combination of two vectors

11

1

Executive summary

Given input representing two vectors and their respective "weights", produce output that also represents the weighted sum of those vectors.

Challenge

The input will consist of one or more lines of the following characters:

  • exactly one occurrence of the digit 0, which represents the origin in a two-dimensional plane;
  • exactly two other digits (1-9; may or may not be the same digit), whose positions relative to the origin represent vectors, and whose values represent the weights attached to thse vectors;
  • some number of "background characters". The solver may choose a specific background character; for example, I will choose "." (mostly for human readability). Alternately, the background characters can be anything that look like blank space.

(The solver can choose whether the input is a single multi-line string or an array of one-line strings.)

For example, the input

....2
.0...
...3.

represents a vector at coordinates (3,1) with weight 2, and a vector at coordinates (2,-1) with weight 3.

The output should be almost the same as the input, with the following changes:

  • a "result character", chosen by the solver, to be added at the position specified by weighted sum of the input vectors (equivalently, at the position that is the appropriate linear combination of the input vectors);
  • as many background characters as are needed to fit the origin, the two input vectors, and the output vector in the same picture. Extra background characters can be included if desired; the only constraint is that, if the background character is a visible character, then the entire output must be rectangular in shape and every character not representing a vector must be the background character. (If blank space is used as background characters, then these constraints don't need to be enforced.)

(In general, if we have one vector (v,w) with weight a and second vector (x,y) with weight b, their weighted sum is a(v,w)+b(x,y) = (av+bx,aw+by).)

In the previous example, the appropriate linear combination is 2*(3,1) + 3*(2,-1) = (12,-1). If we use "X" as the result character, then the output could look like

....2.........
.0............
...3.........X

or

................
...2............
0...............
..3.........X...
................
................

Usual scoring: the shortest answer, in bytes, wins.

Example input and output

If blank space is used, the above input would look like

    2
 0
   3

and the output would look like

    2
 0
   3         X

Leading/trailing whitespace characters/lines are irrelevant; if they're invisible to the reader, it's fine. (That being said, for the rest of the examples I'll go back to using "." for the background character, to make it easier to read.)

If both vectors have weight 1, then the result will look like a parallelogram: the input

.1.
...
1.0

leads to the output

X.1.
....
.1.0

Note this parallelogram can be degenerate if the input vectors are collinear: the input

0.1..1

leads to the output

0.1..1.X

It is possible for the result vector to equal one of the input vectors or the origin; in this case, it simply overwrites the input character. For example, the input

..2.0.1...

yields the output

..X.0.1...

(where in input and/or output, the leading and trailing periods could be deleted). The input

.....3
......
...0..
......
......
2.....

yields the output

.....3
......
...X..
......
......
2.....

Finally, the input

90
.8

yields the output

........90
.........8
..........
..........
..........
..........
..........
..........
X.........

Greg Martin

Posted 2016-07-29T18:10:52.873

Reputation: 13 940

1Welcome to PPCG! Nice first challenge. – AdmBorkBork – 2016-07-29T18:29:15.193

@TimmyD Thank you for the welcome and encouragement :) – Greg Martin – 2016-07-29T18:30:12.153

1

Finally, since I'm sure others will bring it up, this is flirtingly close to a chameleon challenge since a sizable chunk of code is going to be simply parsing the input, when that's not really the main thrust of the challenge.

– AdmBorkBork – 2016-07-29T18:41:02.713

is there any limit on the number of rows/columns in the input or correct output? – Sparr – 2016-07-29T19:13:59.870

@TimmyD I've added the general formula for the weighted sum, and also clarified that either input format is fine. I agree that this is close to a chameleon challenge (though I was hoping that some language(s) might have the capability of "walking" directly on the board to solve the problem); however the feedback on Sandbox was modestly more positive than negative, so I decided to go with it. – Greg Martin – 2016-07-29T20:38:43.023

Regarding max input size: I don't particularly want to have a hard limit, but I also don't want solvers to worry about overflowing string/array/number sizes. What's the best way to proceed? – Greg Martin – 2016-07-29T20:39:38.450

@LuisMendo quite right!—fixed – Greg Martin – 2016-07-29T23:45:45.640

Answers

7

MATL, 48 bytes

tZyyX:UX>*Yat48-tt0>*3#fbbhb~2#fh-*s7M+'X'wZ}4$(

The background character is space. Input is a 2D char array with rows separated by semicolons. So the tests cases have respective inputs:

['    2'; ' 0   '; '   3 ']
[' 1 '; '   '; '1 0']
['0 1  1']
['  2 0 1   ']
['     3'; '      '; '   0  '; '      '; '      '; '2     ']
['90'; ' 8']

The output includes a significant amount of padding whitespace.

Try it online!

Luis Mendo

Posted 2016-07-29T18:10:52.873

Reputation: 87 464

2

Python 3, 374 355 bytes

Not too refined python solution that is very generous with padding (uses maximum chessboard distance). Input is a single line where rows are separated with pipes | (although the algorithm can easily use anything not alphanumerical that isn't a newline or a EOF). Anything non alphanumeric or | works for input padding, output padding uses periods. Feedback and improvement from more seasoned python golfers is appreciated.

Edit: Some improvements thanks to @TheBikingViking. Also added even more margins since I wasn't quite generous enough with padding.

s=input()
l=[len(s),1+s.find('|')]['|'in s]
P=sorted([int(c),i%l,i//l]for i,c in enumerate(s)if c.isalnum())
L=X=Y=0
P[0][0]=-sum(p[0]for p in P)
for w,x,y in P:L=max(abs(x),abs(y),L);X+=x*w;Y+=y*w
P+=[['X',P[0][1]+X,P[0][2]+Y]]
P[0][0]=0
L=2*max(abs(X),abs(Y),L)
S=[2*L*["."]for _ in[0]*2*L]
for w,x,y in P:S[L+y][L+x]=str(w)
for s in S:print(''.join(s))

algmyr

Posted 2016-07-29T18:10:52.873

Reputation: 858

Nice answer! Take a look at the Python tips. Some pointers: 1.It's a good idea to specify if you used Python 2/3, since some features differ. 2.You can do [a,b][condition] instead of b if condition else c on line 2. sorted takes any iterator, including a generator statement, so you can drop the outer pair of square brackets. 3. zip(p) should work instead of p[0] for p in P.

– TheBikingViking – 2016-07-30T19:24:38.930

start="4">

  • You can do P+=[stuff] instead of P.append([stuff]) on line 7. 5. Do ["."] instead of list("."). (3. should have been zip(p)[0].)
  • < – TheBikingViking – 2016-07-30T19:29:17.400

    Sorry, should be capital P in zip. – TheBikingViking – 2016-07-30T19:35:19.430

    start="5">

  • You should be able to do S=[stuff]*2*L on line 10.
  • < – TheBikingViking – 2016-07-30T19:37:53.147

    [1] Good point, will add python version. [2] Good pattern, but it won't work with index (error on nothing found). Will work with find though. [Re. sorted] Thank you, missed removing those when adding the sorted. [3] zip(*P)[0] does not work in python 3 (zip object not indexable). [4] P+=[stuff] won't work, although P+=[[stuff]] will. [5] Thank you. [the other 5] Does not work. I need new lists, not references. – algmyr – 2016-07-30T20:34:43.537

    2

    JavaScript, 534 528 502 bytes

    n="indexOf"
    J="join"
    B=X=>X.split(O)
    O='\n'
    w=i[n](O)+1
    h=B(i).length
    Z=(X,Y,R)=>{C[R]+=X-1;return Array(X)[J](Y)}
    C=[0,0,0]
    G=(X,E,T,U,R)=>X>0&E>=0?Z(X+E+1+T,U,R):""
    o=i[n]("0")
    L=X=>Math.floor(X/(w-1))
    l=L(o)
    c=o%w
    x=y=0
    j=i
    for(z="1";z<="9";z++){while(p=~j[n](z)){j=j.replace(z," ")
    x+=~p%w-l
    y+=L(~p)-c}}
    I=B(i).map(X=>G(-x,-l,0," ",0)+X+G(x,l-w+2,0," ",2))
    N=Z(I[0].length+1," ",2)
    A=B(G(-y,-c,0,N+O,1)+I[J](O)+G(y,c-h,1,O+N,2))
    M=y+c+C[1]
    O=""
    m=B(A[M])
    m[x+l+C[0]/h]="x"
    A[M]=m[J]("")
    A[J]("\n")
    

    Note that the padding is optimal. This program assumes that i contains the raw string, with the lines separated by \n characters. The padding is done with spaces, and the result character is a lowercase x.

    This is my first attempt at code-golfing.

    Technical stuff: - The program's size roughly doubled (and its complexity went up dramatically) to just take into account the result character, mostly because JavaScript strings are immutable.


    Line by line explanation:

    n="indexOf"
    J="join"
    B=X=>X.split(O)
    

    I use those a lot, so storing them in strings saved me some space. You can see below that for the split function, I have simply created an alias; this is because I only needed one argument, the other one being constant. For indexOf and join, it would however have been longer.

    O='\n'
    w=i[n](O)+1
    h=B(i).length
    

    Nothing complicated here, I'm reading the width and height of the initial array. Note the use of i[n] to access indexOf, while split is handled differently.

    Z=(X,Y,R)=>{C[R]+=X-1;return Array(X)[J](Y)}
    

    This is getting interesting. This function basically creates concatenates J-1 times the string X and returns it. This is used to generate strings of spaces for the padding.

    C=[0,0,0]
    

    This array will contain the number of lines and columns added by the padding (off by a factor h in the first case). The last cell is a junk one, and prevents me from having an additional argument in the function below.

    G=(X,E,T,U,R)=>X>0&E>=0?Z(X+E+1+T,U,R):""
    

    This function alone handles the padding (both lines and columns); it determines, based on a coordinate of the result vector (X), and the number of lines/columns to generate (E), whether it is necessary to create one. the X+E+1+T is just a trick to save some space, U is the filling string (a space for columns, and an entire line for lines), and we'll come back to R. This function basically returns, in the case of a line, the padding required at the beginning or the end of said line, and, in the case of a column, it returns the padding lines required either before or after the original lines.

    o=i[n]("0")
    L=X=>Math.floor(X/(w-1))
    l=L(o)
    c=o%w
    

    Here we read the position of the origin, and we retrieve its coordinates. L is a function to convert an index into a line number.

    x=y=0
    j=i
    for(z="1";z<="9";z++){
        while(p=~j[n](z)){
            j=j.replace(z," ")
            x+=~p%w-l
            y+=L(~p)-c
        }
    }
    

    I added some whitespace to make this easier to read. What happens here is that for each possible number, we keep looking for it in the original string. The ~ trick is relatively common in Javascript; it is the bitwise NOT operator, but all that matters here is that ~-1==0, which allows me to test for the end of the loop. I then erase the character in the string (which is why I made a copy), this allowing me to continue the search as long as needed. I then add the coordinates of the vector to (x, y), using a simple substraction.

    I=B(i).map(X=>G(-x,-l,0," ",0)+X+G(x,l-w+2,0," ",2))
    

    I here split the original string into lines, and for each line, I call G which will generate the padding before and after the lines. The l-w+2 and so on come from a simple index calculation which allows me to test whether I need to add padding or not. For example, if x>0 and x+l-w+1>0, then (x+l-w+1)+1 spaces must be added after the line. The +x is being removed due to it being the first parameter, and the X+E+1+T used in the definition of G.

    A similar thing is done for the first characters, and then for the columns. There is a lot of factorization here allowing me to use only one function. Note the last parameter; in the first case, I want to write to C[0] to be able to know later how many columns I added at the beginning of each line; this allows me to retrieve the final position of the result character. I do however not care about the columns added after the original line, which is why the second call to G writes to the junk cell C[2] which is unused.

    N=Z(I[0].length+1," ",2)
    

    Here I simply read the new length of the lines, and create a line of spaces from it. This will be used to create the vertical padding.

    A=B(G(-y,-c,0,N+O,1)+I[J](O)+G(y,c-h,1,O+N,2))
    

    This is exactly the same as two lines above. The only difference is writing to C[1] this time, and using the separators N+O and O+N. Remember that O is a newline, and N is a line of spaces. I then apply B on the result to split it again (I need to retrieve the line containing the result character to edit it).

    M=y+c+C[1]
    

    This is the vertical index of the resulting character.

    O=""
    m=B(A[M])
    m[x+l+C[0]/h]="x"
    

    Here I am forced to modify O to be able to split the appropriate line into an array of characters. This is because JavaScript strings are immutable; the only way to edit a string is to convert it into an array (which is what I'm doing here), edit at the right position, and join the string again. Also note the h factor, which is because the G function was called once per initial line.

    A[M]=m[J]("")
    A[J]("\n")
    

    I finally replace the new string in the array, and join it again into a string. Woohoo!

    pie3636

    Posted 2016-07-29T18:10:52.873

    Reputation: 121