2
$\begingroup$

I have the following list:

points = Tuples[Range[0, 1.25, 0.25], 9]

I would like to select only those lists who have the following properties, naming the elements of each lists with slots:

  • 1) #1>0.4
  • 2) #9>#8,#8>#7,#7>#6
  • 3) #1+#2+#3< 1.22
  • 4) #5<#4
  • 5) #4<1.21
  • 6) 0.5 (#1 (2.42-#6+#7)+#2(#4+1.21-#6)+#3 (#5+#4))<0.625
  • 7) All #>0

Any suggestions to do this?

UPDATE

As Rashid pointed out, this is terribly slow to solve. If any brilliant lad can think of a way to directly construct such a list, chapeau!

In the spirit of making the code less slow as suggested by Rashid, I have written this code for the first list of lists:

points = Partition[#, 9] &@
   Flatten@Tuples[{Tuples[Range[0.25, 0.75, 0.25], 1], 
      Tuples[Range[0, 0.75, 0.25], 4], 
      Tuples[Range[0, Pi/3, Pi/12], 4]}];

Please use this code instead of the first. With this code condition 1 is implicitly given. Any other suggestions for building the list is welcomed. I also added some extra conditions to limit the length of this list.

$\endgroup$
  • $\begingroup$ You can drop condition 7 as well by starting the second inner Tuples at 0.25 and the third at Pi/12. $\endgroup$ – Edmund May 15 '16 at 16:12
  • $\begingroup$ Yeah, yet I just realized that the code I wrote then mixes the order of the elements of each list. This is not what I needed! $\endgroup$ – Mirko Aveta May 15 '16 at 16:16
2
$\begingroup$

Update for applying all test simultaneously.

There do not appear to be any lists that simultaneously satisfy all seven conditions.

oneSelectTests = MapAt[Function[{f}, f[Sequence @@ #] &], ;; -2]@tests;
oneSelectRes = Select[And @@ Through[oneSelectTests[#]] &]@points;
Dimensions@oneSelectRes
(* {0} *)

A solution with Slot.

tests = {
         #1 > 0.4 &, 
         And @@ {#9 > #8, #8 > #7, #7 > #6} &, 
         #1 + #2 + #3 < 1.22 &, 
         #5 < #4 &, #4 < 1.21 &, 
         0.5 (#1 (2.42 - #6 + #7) + #2 (#4 + 1.21 - #6) + #3 (#5 + #4)) < 0.625 &, 
         And @@ Positive[#] &
        }

Then build the set of Select functions that take each sublist as its list of parameters. The last test is not mapped as it needs to take the list.

selTests = Select /@ MapAt[Function[{f}, f[Sequence @@ #] &], ;; -2]@tests;

Then apply the selTests to points.

res = Through@selTests@points;
Dimensions /@ res

(* {{6718464, 9}, {116640, 9}, {1632960, 9}, {4199040, 9}, 
    {8398080, 9}, {1292004, 9}, {1953125, 9}} *)

Hope this helps.

| improve this answer | |
$\endgroup$
  • $\begingroup$ Dear Edmund, thank you for your suggestion. Does your code consider all the conditions together? I need all conditions to be considered. $\endgroup$ – Mirko Aveta May 15 '16 at 14:09
  • 2
    $\begingroup$ Oh, I thought you wanted 7 separate sets to each condition. Misread. Let me think about that. $\endgroup$ – Edmund May 15 '16 at 14:11
  • $\begingroup$ @MirkoAveta See update. There do not appear to be any list that simultaneously satisfy all conditions. $\endgroup$ – Edmund May 15 '16 at 15:22
  • $\begingroup$ Yes, thanks for pointing that out. I must firstly say that the first condition has been suppressed, as I have said in Rashed's reply update. I will see if this happens also in the new definition of 'points'. $\endgroup$ – Mirko Aveta May 15 '16 at 15:29
3
$\begingroup$
points = Tuples[Range[0, 1.25, 0.25], 9];

pnts1b = Select[points, #[[1]] > 0.4 &]; // AbsoluteTiming // First

11.617923

Pick instead of Select is faster:

pnts1a = Pick[points, UnitStep[points[[All, 1]] - .4], 1]; // AbsoluteTiming // First

0.545490

As suggested by @Rashid, using the conditions upfront is much faster:

pnts1c = Tuples[{Select[Range[0, 1.25, .25], # > .4 &], 
        ## & @@ (ConstantArray[Range[0, 1.25, 0.25], 8])}]; // AbsoluteTiming // First

0.126385

Update: constructing tuples bottom up using the conditions

In the following the list cxyz stands for a list of tuples satisfying conditions x, y and z. Since conditions 1, 3 and 7 relate to entries 1,2,3 and conditions 4,5,7 to entries 4 and 5, and conditions 2,7 apply to entries 6,7,8,9 we can piece together 9-tuples from 3-tuples satisying conditions 1,2,7 and, 2-tuples satisfying conditions 4,5,7 and 4-tuples satisfying conditions 2,7. Then we can filter the resulting list of 9-tuples using condition 6.

AbsoluteTiming[c137 = Pick[tt = Tuples[{Range[.5, 1.25, .25], Range[.25, 1.25, .25], 
      Range[.25, 1.25, .25]}], Total[#] < 1.22 & /@ tt];
 c457 = Partition[Flatten[Table[{j, i}, {j, .25, 1.0, .25}, {i, .25, j - .25, .25}]], 2];
 c13457 = Join @@@ Tuples[{c137, c457}];
 c27 = Partition[Flatten[Table[{i, j, k, l}, {i, .25, 1.25, .25}, 
   {j, i + .25, 1.25, .25}, {k, j + .25, 1.25, .25}, {l, k + .25, 1.25, .25}]],  4];
 c123457 = Join @@@ Tuples[{c13457, c27}]]

Mathematica graphics

Finally checking for condition 6, we get an empty set as the final result

Pick[c123457, 0.5 (#[[1]] (2.42 - #[[6]] + #[[7]]) + #[[2]] (#[[4]] + 
          1.21 - #[[6]]) + #[[3]] (#[[5]] + #[[4]])) < 0.625 & /@ c123457]

{}

Incidentally, identifying 9-tuples satisfying condition 6 only takes a long time:

lengthc6 = Length[Pick[tuples, 0.5 (#[[1]] (2.42 - #[[6]] + #[[7]]) + #[[2]] (#[[4]] + 
    1.21 - #[[6]]) + #[[3]] (#[[5]] + #[[4]])) < 0.625 & /@ tuples]] // AbsoluteTiming

{89.781723, 1292004}

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks for your suggestions. I didn't know that Pick was that faster!! $\endgroup$ – Mirko Aveta May 15 '16 at 14:34
2
$\begingroup$

Here is a direct approach using Select with Part([[]]) and Slot (#), but given the size of points, this will definitely be very slow. Each selection takes 2-3 minutes to run on my computer for a total of ~20 minutes:

points = Tuples[Range[0, 1.25, 0.25], 9];
points1 = Select[points, #[[1]] > 0.25 &]
points2 = Select[points, #[[9]] > #[[8]] && #[[8]] > #[[7]] && #[[7]] > #[[6]] &]
points3 = Select[points, #[[1]] + #[[2]] + #[[3]] < 1.22 &]
points4 = Select[points, #[[5]] < #[[4]] &]
points5 = Select[points, #[[4]] < 1.21 &]
points6 = Select[points,  0.5 (#[[1]] (2.42 - #[[6]] + #[[7]]) + #[[2]] (#[[4]] + 1.21 - #[[6]]) + #[[3]] (#[[5]] + #[[4]])) < 0.625 &]
points7 = Select[points, Apply[Times, #] > 0 &]

Note that points7 assumes all numbers are either zero or positive. A more generally implementation would be something like points7b = Select[points, Min[Sign[#]] > 0 &]

Since the tuples list in points is so large, I suspect you would be better off just constructing the final lists you want. For example, using points7=Tuples[Range[0.25,1.25,0.25],9] rather than selecting the ones without zeros.

Edit -by Mirko Aveta

In the spirit of making the code less slow as suggested by Rashid, I have written this code for the first list of lists:

points = Partition[#, 8] &@
   Flatten@Tuples[{Tuples[Range[0.25, 0.75, 0.25], 1], 
      Tuples[Range[0, 0.75, 0.25], 4], 
      Tuples[Range[0, Pi/3, Pi/12], 4]}];

Please use this code instead of the first. With this code condition 1 is implicitly given. Any other suggestions for building the list is welcomed. I also added some extra conditions to limit the length of this list. d

| improve this answer | |
$\endgroup$
  • $\begingroup$ Thanks for your suggestion. Indeed I used a step of 0.25 because I knew it was too slow for testing. I will have to change the step once the code is done, so I would have values much closer to the zero. It would be much more efficient to write down the list directly selected, that would be really perfect! $\endgroup$ – Mirko Aveta May 15 '16 at 13:40
  • 1
    $\begingroup$ @MirkoAveta, yes, I would definitely go with defining the lists you want upfront. For the more complicated cases, I would probably just use Table with 9 explicit indexes and integer ranges, and then divide in the end by the appropriate integer. For example, Table[{x1,x2,x3,...x9},{x1,0,5},{x2...}] then divide by 5*9 in this case.... $\endgroup$ – Rashid May 15 '16 at 13:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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