The cross product is a peculiarly 3-dimensional phenomenon. There are multiple ways of generalizing it, but each of them have trade offs.
If you require that a cross product be a product of two vectors \$\mathbf{v}_1\times\mathbf{v}_2\$ such that it is:
- linear, \$a\mathbf{v}_1\times\mathbf{v}_2 = a\left(\mathbf{v}_1\times\mathbf{v}_2\right)\$ and \$\left(\mathbf v_1+\mathbf v_2\right)\times\mathbf v_3 = \left(\mathbf v_1\times\mathbf v_3\right)+\left(\mathbf v_2\times\mathbf v_3\right)\$
- anticommutative, \$\left(\mathbf{v}_1\times\mathbf{v}_2\right) + \left(\mathbf{v}_2\times\mathbf{v}_1\right) = \mathbf{0}\$
- orthogonal, \$\left(\mathbf{v}_1\times\mathbf{v}_2\right)\cdot\mathbf v_1 = \mathbf 0\$
- not everywhere zero
Then there are two dimensions that have cross products:
- There is the ordinary cross product in 3D.
- There is also a cross product in 7D.
Today our challenge is to implement this 7-dimensional cross product.
Definition
Although the above is a technically unambiguous challenge specification, I will give a complete definition of a 7D cross product here which you can readily implement as an algorithm.
In order to determine the cross product of two vectors you only need to know the behavior on the basis vectors. You can write each vector as a combination of basis vectors, e.g.
\$ \left[a,b,c,d,e,f,g\right]^\top = a\mathbf e_1 + b\mathbf e_2 + c\mathbf e_3 + d\mathbf e_4 + e\mathbf e_5 + f\mathbf e_6 + g\mathbf e_7 \$
Since the product is linear you can distribute until any cross product is written as the sum of products of basis vectors. For example here's a 3D case:
\begin{align} \left[a,b,c\right]^\top\times\left[x,y,z\right]^\top &= \left(a\mathbf e_1 + b\mathbf e_2 + c\mathbf e_3\right)\times\left(x\mathbf e_1 + y\mathbf e_2 + z\mathbf e_3\right) \\ &= \begin{matrix}& ax\left(e_1\times e_1\right) &+& bx\left(e_2\times e_1\right) &+& cx\left(e_3\times e_1\right) \\+& ay\left(e_1\times e_2\right) &+& by\left(e_2\times e_2\right) &+& cy\left(e_3\times e_2\right) \\+& az\left(e_1\times e_3\right) &+& bz\left(e_2\times e_3\right) &+& cy\left(e_3\times e_3\right)\end{matrix} \end{align}
Then for the product of basis vectors you can use the following product table:
| ×ばつ | \$\mathbf e_1\$ | \$\mathbf e_2\$ | \$\mathbf e_3\$ | \$\mathbf e_4\$ | \$\mathbf e_5\$ | \$\mathbf e_6\$ | \$\mathbf e_7\$ |
|---|---|---|---|---|---|---|---|
| \$\mathbf e_1\$ | 0 | \$\mathbf e_3\$ | \$-\mathbf e_2\$ | \$\mathbf e_5\$ | \$-\mathbf e_4\$ | \$-\mathbf e_7\$ | \$\mathbf e_6\$ |
| \$\mathbf e_2\$ | \$-\mathbf e_3\$ | 0 | \$\mathbf e_1\$ | \$\mathbf e_6\$ | \$\mathbf e_7\$ | \$-\mathbf e_4\$ | \$-\mathbf e_5\$ |
| \$\mathbf e_3\$ | \$\mathbf e_2\$ | \$-\mathbf e_1\$ | 0 | \$\mathbf e_7\$ | \$-\mathbf e_6\$ | \$\mathbf e_5\$ | \$-\mathbf e_4\$ |
| \$\mathbf e_4\$ | \$-\mathbf e_5\$ | \$-\mathbf e_6\$ | \$-\mathbf e_7\$ | 0 | \$\mathbf e_1\$ | \$\mathbf e_2\$ | \$\mathbf e_3\$ |
| \$\mathbf e_5\$ | \$\mathbf e_4\$ | \$-\mathbf e_7\$ | \$\mathbf e_6\$ | \$-\mathbf e_1\$ | 0 | \$-\mathbf e_3\$ | \$\mathbf e_2\$ |
| \$\mathbf e_6\$ | \$\mathbf e_7\$ | \$\mathbf e_4\$ | \$-\mathbf e_5\$ | \$-\mathbf e_2\$ | \$\mathbf e_3\$ | 0 | \$-\mathbf e_1\$ |
| \$\mathbf e_7\$ | \$-\mathbf e_6\$ | \$\mathbf e_5\$ | \$\mathbf e_4\$ | \$-\mathbf e_3\$ | \$-\mathbf e_2\$ | \$\mathbf e_1\$ | 0 |
Rules
This is code-golf so the goal is to minimize the size of your source code as measured in bytes.
You should take input as two seven-dimensional vectors. You should output one seven-dimensional vector in the same format. You may approximate real numbers in any reasonable manner.
You may choose to take input in a way that permutes the basis vectors or multiply the result by -1, so long as it satisfies the definition of the cross product above.
Test cases
The table above provides 49 basic test cases. In addition I suggest you test the two linearity properties with random vectors. If your product is bilinear and satisfies the multiplication table above then it must be correct.
That is:
- Take two random vectors and two random real numbers and test that \$a\mathbf{v}_1\times b\mathbf{v}_2 = ab\left(\mathbf{v}_1\times\mathbf{v}_2\right)\$
- Take 3 random vectors and test that \$\left(\mathbf v_1+\mathbf v_2\right)\times\mathbf v_3 = \left(\mathbf v_1\times\mathbf v_3\right)+\left(\mathbf v_2\times\mathbf v_3\right)\$
-
\$\begingroup\$ "Although the above is technically unambiguous, I will give a complete definition of the 7D cross product here which you can readily implement as an algorithm." << This seems to be in contradiction with the wikipedia page, which if I understand it correctly, tells me that there can be many choices of seven-dimensional cross-product? \$\endgroup\$Stef– Stef2025年07月29日 07:56:08 +00:00Commented Jul 29 at 7:56
-
\$\begingroup\$ @Stef The constraints are an unambiguous challenge, not a definition of the cross product. The point of this sentence is "ok I've technically given the challenge, but here's a solution you can use". And likewise I'm not trying to say that permutations are exhaustive, just that they are one way to get more products. I've tried tweaking the wording a bit, let me know if there's something else you think could be changed, or feel free to edit the question directly. \$\endgroup\$Wheat Wizard– Wheat Wizard ♦2025年07月29日 12:04:55 +00:00Commented Jul 29 at 12:04
-
\$\begingroup\$ can output be 15-dimensional vector (codegolf.stackexchange.com/a/282853)? \$\endgroup\$Lucenaposition– Lucenaposition2025年07月30日 02:32:11 +00:00Commented Jul 30 at 2:32
-
2\$\begingroup\$ @Lucenaposition Mathematically speaking, the condition for orthogonality requires the output of the cross product operation to have the same dimensionality as the inputs. And it doesn't look like there's anything in the challenge statement that allowed a 15-component output to be interpreted as a 7-dimensional mathematical vector (say, by dropping components or some such thing), but I'm not the authority.... \$\endgroup\$David Z– David Z2025年07月30日 22:11:59 +00:00Commented Jul 30 at 22:11
-
\$\begingroup\$ @Stef In 3-d the properties above don't define the cross product uniquely, there is still a sign ambiguity. In 7-d you only need the cross product to be orthogonal to the 2 input vectors, which still gives you a 5-d space to choose from. But up to that choice, the cross products are all the same. So the multiplication table chooses a particular realization but all realizations are equivalent. \$\endgroup\$quarague– quarague2025年07月31日 08:46:16 +00:00Commented Jul 31 at 8:46
7 Answers 7
JavaScript (ES6), 83 bytes
Expects (u)(v).
u=>v=>[0,1,2,4].reduce((p,i)=>p.map(n=>i&&n+u[i%=7]*v[j%=7]-u[j++]*v[i++],j=i*3),u)
Commented
u => // u[] = first vector
v => // v[] = second vector
[0, 1, 2, 4] // lookup list
.reduce((p, i) => // for each value i with p[] as the accumulator:
p.map(n => // for each value n in p[]:
i && // force to zero if i = 0
n + // otherwise, take the current value n
u[i %= 7] * // and add u[i] * v[j] - u[j] * v[i]
v[j %= 7] - // where both i and j are reduced modulo 7
u[j++] * // and incremented afterwards
v[i++], //
j = i * 3 // start with j = i * 3, which gives (modulo 7):
// (i,j) = (1,3), (2,6), (4,5)
), // end of map()
u // start with p[] = u[] (but the content of p[]
// is cleared on the first iteration)
) // end of reduce()
Wolfram Language (Mathematica), 62 bytes
s&@Do[s[[t]]+=#[[t=Mod[i+{0,1,3},7,1]]]#2[[t]],{i,s=0#;7}]&
-1 bytes thanks to @att.
(\[Cross], unicode U+F4A0) is a special character in Mathematica that represents cross product.
First initialize the result \$\mathbf{s}\$ to a zero vector of length 7. And then:
- \$(s_1, s_2, s_4) \leftarrow (s_1, s_2, s_4) + (a_1, a_2, a_4) \times (b_1, b_2, b_4)\$ (where \$\times\$ is the 3D cross product);
- \$(s_2, s_3, s_5) \leftarrow (s_2, s_3, s_5) + (a_2, a_3, a_5) \times (b_2, b_3, b_5)\$;
- \$\ldots\ldots\$
- \$(s_7, s_1, s_3) \leftarrow (s_7, s_1, s_3) + (a_7, a_1, a_3) \times (b_7, b_1, b_3)\$.
Now \$\mathbf{s}\$ becomes the 7D cross product of \$\mathbf{a}\$ and \$\mathbf{b}\$.
-
1
-
\$\begingroup\$ Does Mathematica have a build in Octonian multiplication that one could use (even if it is not shorter)? \$\endgroup\$quarague– quarague2025年07月31日 08:48:10 +00:00Commented Jul 31 at 8:48
-
\$\begingroup\$ @quarague There are some 3rd-party packages for that, but there isn't a built-in. \$\endgroup\$alephalpha– alephalpha2025年07月31日 09:52:07 +00:00Commented Jul 31 at 9:52
Maple, (削除) 187 (削除ここまで) 176 bytes
(s,t)->eval(s^+.Matrix([[],[-e3],[e2,-e1],[-e5,-e6,-e7],[e4,-e7,e6,-e1],[e7,e4,-e5,-e2,e3],[-e6,e5,e4,-e3,-e2,e1,0]],shape=antisymmetric).t,{seq(e||i=Vector(7,{i=1}),i=1..7)});
If the two vectors are s and t, and Q is the multiplication table as a matrix, then the result in the form a*e1+b*e2+... is sT.Q.t. Then substitute e1, e2 etc with the unit vectors [1,0,0,0,0,0,0]T, [0,1,0,0,0,0,0]T etc. to get the final result.
PARI/GP, 74 bytes
f(a,b)=[sum(n=0,2,a[u=(i+2^n)%7+1]*b[v=(i+3<<n)%7+1]-b[u]*a[v])|i<-[0..6]]
A port of Arnauld's JavaScript solution.
PARI/GP, 97 bytes
f(a,b)=Vecrev(sum(t=0,6,matdet([x^t,x^u=t++%7,x^v=(t+2)%7;a[t],a[u++],a[v++];b[t],b[u],b[v]])),7)
Using the algorithm in my Mathematica answer.
Charcoal, (削除) 63 (削除ここまで) 35 bytes
×ばつ⎇&λ⊖λ§θ−ιλ±§θ+μλ§ημ
Try it online! Link is to verbose version of code. No test suite because that requires renaming the variables. Explanation: Uses the C(+) octonion multiplication table that I found here (warning: no SSL available).
7 Literal integer `7`
E Map over implicit range
7 Literal integer `7`
E Map over implicit range
μ Inner index
− Subtract
ι Outer value
% Modulo
7 Literal integer `7`
⊗ Doubled
E Map over values
λ Inner value
∧ Logical And
λ Inner value
& Bitwise And
λ Inner value
⊖ Decremented
⎇ If true then
θ First input
§ Cyclically indexed by
ι Outer value
− Subtract
λ Inner value
θ Else first input
§ Cyclically indexed by
μ Inner index
+ Plus
λ Inner value
± Negated
×ばつ Multiplied by
η Second input
§ Indexed by
μ Inner index
Σ Take the sum
I Cast to string
Implicitly print
Python 2, (削除) 165 (削除ここまで) 146 bytes
def g(x,y):
x*=2;y*=2;z=[0]*10;p,q,r=0,1,3
for a in range(7)*3:p,q,r=q,r,p;z[a+p]+=x[a+q]*y[a+r]-x[a+r]*y[a+q]
exec'print sum(z[p::7]);p+=1;'*7
Changed my approach completely.
This is now a port of Seven-dimensional cross product.
Prints output to stdout.
Jelly, 28 bytes
ṙJ$(Ç¢œ?N5ẒƇ¤¦_W{JœṖ"$ZṚẎŒḌḋ
A dyadic Link that accepts \$\mathbf v_1\$ on the left and \$\mathbf v_2\$ on the right and yields \$\mathbf v_1\times\mathbf v_2\$
- See the orthonormal multiplication table.
- See two random vectors and two random scalars satisfying \$a\mathbf{v}_1\times b\mathbf{v}_2 = ab\left(\mathbf{v}_1\times\mathbf{v}_2\right)\$ here.
- See three random vectors satisfying \$\left(\mathbf v_1+\mathbf v_2\right)\times\mathbf v_3 = \left(\mathbf v_1\times\mathbf v_3\right)+\left(\mathbf v_2\times\mathbf v_3\right)\$ here.
How?
Uses this multiplication table of orthonormal basis vectors:
| ×ばつ | \$\mathbf e_1\$ | \$\mathbf e_2\$ | \$\mathbf e_3\$ | \$\mathbf e_4\$ | \$\mathbf e_5\$ | \$\mathbf e_6\$ | \$\mathbf e_7\$ |
|---|---|---|---|---|---|---|---|
| \$\mathbf e_1\$ | 0 | \$-\mathbf e_4\$ | \$-\mathbf e_7\$ | \$\mathbf e_2\$ | \$-\mathbf e_6\$ | \$\mathbf e_5\$ | \$\mathbf e_3\$ |
| \$\mathbf e_2\$ | \$\mathbf e_4\$ | 0 | \$-\mathbf e_5\$ | \$-\mathbf e_1\$ | \$\mathbf e_3\$ | \$-\mathbf e_7\$ | \$\mathbf e_6\$ |
| \$\mathbf e_3\$ | \$\mathbf e_7\$ | \$\mathbf e_5\$ | 0 | \$-\mathbf e_6\$ | \$-\mathbf e_2\$ | \$\mathbf e_4\$ | \$-\mathbf e_1\$ |
| \$\mathbf e_4\$ | \$-\mathbf e_2\$ | \$\mathbf e_1\$ | \$\mathbf e_6\$ | 0 | \$-\mathbf e_7\$ | \$-\mathbf e_3\$ | \$\mathbf e_5\$ |
| \$\mathbf e_5\$ | \$\mathbf e_6\$ | \$-\mathbf e_3\$ | \$\mathbf e_2\$ | \$\mathbf e_7\$ | 0 | \$-\mathbf e_1\$ | \$-\mathbf e_4\$ |
| \$\mathbf e_6\$ | \$-\mathbf e_5\$ | \$\mathbf e_7\$ | \$-\mathbf e_4\$ | \$\mathbf e_3\$ | \$\mathbf e_1\$ | 0 | \$-\mathbf e_2\$ |
| \$\mathbf e_7\$ | \$-\mathbf e_3\$ | \$-\mathbf e_6\$ | \$\mathbf e_1\$ | \$-\mathbf e_5\$ | \$\mathbf e_4\$ | \$\mathbf e_2\$ | 0 |
ṙJ$(Ç¢œ?N5ẒƇ¤¦_W{JœṖ"$ZṚẎŒḌḋ - Link: V1; V2 e.g. abcdefg; tuvwxyz
ṙJ$ - rotate {V1} left by its 1-indices
-> bcdefga cdefgab defgabc efgabcd fgabcde gabcdef abcdefg
(Ç¢œ? - 4502nd lexicographic permutation of {that}
-> abcdefg cdefgab efgabcd fgabcde bcdefga gabcdef defgabc
N5ẒƇ¤¦ - negate values in the 2nd, 3rd, and 5th lists (primes up to 5)
_W{ - replace the 1st with zeros (subtract [V1])
JœṖ"$ - partition each before its 1-index
ZṚẎ - transpose, reverse and tighten
ŒḌ - form a matrix from these diagonals
-> 0 -d -g b -f e c
d 0 -e -a c -g f
g e 0 -f -b d -a
-b a f 0 -g -c e
f -c b g 0 -a -d
-e g -d c a 0 -b
-c -f a -e d b 0
ḋ - dot-product {V2} (vectorsies)
-> [ 0 - ud - vg + wb - xf + ye + zc,
td + 0 - ve - wa + xc - yg + zf,
tg + ue + 0 - wf - xb + yd - za,
-tb + ua + vf + 0 - xg - yc + ze,
tf - uc + vb + wg + 0 - ya - zd,
-te + ug - vd + wc + xa + 0 - zb,
-tc - uf + va - we + xd + yb + 0,
]