What is the easiest and efficient way to get positive or negative combinations of a list?

Consider the following simple case:

list={1,2,2};
Subsets[{Splice[list],Splice[-list]},{1,Length@list}]


This produces the following combinations:

{{1}, {2}, {2}, {-1}, {-2}, {-2}, {1, 2}, {1, 2}, {1, -1}, {1, -2}, {1, -2}, {2, 2}, {2, -1}, {2, -2}, {2, -2}, {2, -1}, {2, -2}, {2, -2}, {-1, -2}, {-1, -2}, {-2, -2}, {1, 2, 2}, {1, 2, -1}, {1, 2, -2}, {1, 2, -2}, {1, 2, -1}, {1, 2, -2}, {1, 2, -2}, {1, -1, -2}, {1, -1, -2}, {1, -2, -2}, {2, 2, -1}, {2, 2, -2}, {2, 2, -2}, {2, -1, -2}, {2, -1, -2}, {2, -2, -2}, {2, -1, -2}, {2, -1, -2}, {2, -2, -2}, {-1, -2, -2}}

The problem I am trying to solve requires me to have all possible combinations of list but in any given combination, if an entry appears then it either appears as positive or negative and not both.

So for example {2, -2, -2} is invalid since a given 2 should appear as positive or negative but here there are three 2s appearing even though the original list had two 2s.

Also, I am looking for an efficient way to generate the required combination.

• Does tal = Lookup[Counts[list], Union[list], 0]; Select[Subsets[{Splice[list], Splice[-list]}, {1, Length[list]}], And @@ Thread[Lookup[Counts[Abs[#]], Union[list], 0] <= tal] &] do what you want? Feb 24, 2021 at 7:26
• @J.M. Yes, it seems to be producing the correct result for the cases I tested. But is there a way to directly produce this outcome instead of first finding all combinations and then filtering the relevant ones? Feb 24, 2021 at 7:49
• I am not sure, and that's why I left a comment and not an answer. I agree it can quickly get combinatorically prohibitive. Feb 24, 2021 at 7:50

list = {1, 2, 2};
result = Subsets[{Splice[list], Splice[-list]}, {1, Length@list}]
GroupBy[result, Length[Intersection[#, -#]] < 1 &]

<|True -> {{1}, {2}, {2}, {-1}, {-2}, {-2}, {1, 2}, {1,
2}, {1, -2}, {1, -2}, {2,
2}, {2, -1}, {2, -1}, {-1, -2}, {-1, -2}, {-2, -2}, {1, 2,
2}, {1, -2, -2}, {2, 2, -1}, {-1, -2, -2}},
False -> {{1, -1}, {2, -2}, {2, -2}, {2, -2}, {2, -2}, {1,
2, -1}, {1, 2, -2}, {1, 2, -2}, {1, 2, -1}, {1, 2, -2}, {1,
2, -2}, {1, -1, -2}, {1, -1, -2}, {2, 2, -2}, {2,
2, -2}, {2, -1, -2}, {2, -1, -2}, {2, -2, -2}, {2, -1, -2}, {2, \
-1, -2}, {2, -2, -2}}|>

• Your GroupBy's condition is correctly discarding things like {1,2,-1} which is ok since we can't have more than one 1s but it is also incorrectly discarding {1,2,-2} which is not correct since the original list does allow for two 2s. Feb 24, 2021 at 7:04

Here is a way to construct the list directly:

list = {1,2,2};

Splice[Curry[Times][#] /@ Tuples[{1, -1}, Length@#]]& /@
DeleteDuplicates@Subsets[list, {1,Length@list}]

(*
{{1},{-1},{2},{-2},
{1,2},{1,-2},{-1,2},{-1,-2},{2,2},{2,-2},{-2,2},{-2,-2},
{1,2,2},{1,2,-2},{1,-2,2},{1,-2,-2},{-1,2,2},{-1,2,-2},{-1,-2,2},{-1,-2,-2}}
*)


If the input list can contain elements that are equal but with opposite signs, then DeleteDuplicates ought to be replaced by DeleteDuplicatesBy[Abs].

On the other hand, if it is desirable to retain repeated result elements in those cases where an input element is repeated (e.g. 2 in the example), then remove DeleteDuplicates@ altogether:

Splice[Curry[Times][#] /@ Tuples[{1, -1}, Length@#]]& /@
Subsets[list, {1,Length@list}]

(*
{{1},{-1},{2},{-2},{2},{-2},
{1,2},{1,-2},{-1,2},{-1,-2},{1,2},{1,-2},{-1,2},{-1,-2},{2,2},{2,-2},{-2,2},{-2,-2},
{1,2,2},{1,2,-2},{1,-2,2},{1,-2,-2},{-1,2,2},{-1,2,-2},{-1,-2,2},{-1,-2,-2}}
*)


You can multiply each subset by all combinations of signs:

result = Flatten[
Transpose[#*Transpose@Tuples[{1, -1}, Length@#]] & /@
Subsets[list, {1, Length@list}]
, 1] // DeleteDuplicates;
result // Sort

(*
{{-2}, {-1}, {1}, {2},
{-2, -2}, {-2, 2}, {-1, -2}, {-1, 2}, {1, -2}, {1, 2}, {2, -2}, {2, 2},
{-1, -2, -2}, {-1, -2, 2}, {-1, 2, -2}, {-1, 2, 2}, {1, -2, -2},
{1, -2, 2}, {1, 2, -2}, {1, 2, 2}}
*)


As with @WReach's answer, if you wish to retain the multiple occurrences of the same set such the two instances of {2}, omit DeleteDuplicates.

You may construct you answer by treating each case separately.

The one element cases:

t1 = Subsets[list, {1}]
t2 = Subsets[-list, {1}]


and the two element cases:

t3 = Flatten[Outer[List, list, list], 1]
t4 = Flatten[Outer[List, list, -list], 1]
t5 = Flatten[Outer[List, -list, -list], 1]


And then combine everything:

Join[t1, t2, t3, t4, t5]