3

In order to simplify my expression, I face a list manipulation problem.

For example, given the input list

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

I can calculate the values of f[a[i]] and then combine the idential elements (f[] is just a function of the a[]). The condition of this transformation is

f[a[1]] == f[a[3]] == f[a[6]]; f[a[2]] == f[a[4]];

so that the output list becomes

newlist = {(cof[1] + cof[3] + cof[6]) a[1], (cof[2] + cof[4]) a[2], cof[5] a[5]};

How can I get this kind transformation to newlist for the general case?

m_goldberg
  • 107,779
  • 16
  • 103
  • 257
Orders
  • 1,247
  • 11
  • 20

5 Answers5

5

Starting with your list and a dummy f that produces output with the equivalences you specified:

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

f[a[1] | a[3] | a[6]] = "one";
f[a[2] | a[4]] = "two";

We might proceed:

GatherBy[list, f /@ Cases[#, _a] &]
{{a[1] cof[1], a[3] cof[3], a[6] cof[6]}, {a[2] cof[2], a[4] cof[4]}, {a[5] cof[5]}}
GatherBy[#, Head] & /@ List @@@ Join @@@ %
{{{a[1], a[3], a[6]}, {cof[1], cof[3], cof[6]}},
 {{a[2], a[4]}, {cof[2], cof[4]}}, {{a[5]}, {cof[5]}}}
First[#] Total[#2] & @@@ %
{a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), a[5] cof[5]}
Mr.Wizard
  • 271,378
  • 34
  • 587
  • 1,371
  • Thanks! This is very useful for me.In your second step, can you change it a little for a general case.Because cof[i_] is not a single Integer or Symbol, for example, cof[i_] := g[i] coff[i] const. – Orders Feb 24 '13 at 02:30
  • @Orders Does GatherBy[#, Head@# === a &] work for you? Also, be aware that GatherBy lists element groups by the order in which they first appear so if a[*] is not always first the third step will break; you may need to add a sort. If you include a more complex example in your question I'll try to address it. – Mr.Wizard Feb 24 '13 at 07:44
  • Sorry for the delay. Thanks for your help. You method is very general and it works for me now. Yes, I am dealing a complex expression, at least for me. I am facing a problem for simplification for large expression. If I cannot deal with it myself, I will post it again. – Orders Feb 26 '13 at 13:46
  • @Orders Okay, no problem. – Mr.Wizard Feb 26 '13 at 13:48
3

If you happen to dislike GatherBy as I do.

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};

Now similar to Mr. Wizard we define a function f which works as you suggest.

f[a[1] | a[3] | a[6]] = 1;
f[a[2] | a[4]] = 2;
f[_] := 0

And a second helper function g which does the actual work.

g[{0, expr_}] := expr
g[{i_, expr_}] := a[i]*Total[Cases[expr, _*r_cof :> r]]

Finally we put it all together...

With[{mask = f /@ Cases[list, a[r_]*_ :> a[r]]},
   g[{#, Pick[list, mask, #]}] & /@ DeleteDuplicates[mask]
 ]

(*{a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), {a[5] cof[5]}} *)
Andy Ross
  • 19,320
  • 2
  • 61
  • 93
  • Andy, +1 for a nice method, but why the dislike of GatherBy? Also, would a[r_]*__ (emphasis __) make this more general? – Mr.Wizard Feb 23 '13 at 04:37
  • @Mr.Wizard I suppose the __ would make it more general. I don't like GatherBy because it tends to be too slow for my taste. It may well have improved but my initial impression wasn't a good one. – Andy Ross Feb 23 '13 at 04:40
  • Strange; Tally and GatherBy have always seemed fast to me, compared to what I used before they were introduced. – Mr.Wizard Feb 23 '13 at 04:43
  • @Mr.Wizard Tally is incredibly fast, no complaints there (unless you want to transpose it). GatherBy I've never been happy with. – Andy Ross Feb 23 '13 at 04:47
  • Can you give me a simple example where GatherBy is slow and a faster alternative? I know one is if the compare function is much faster in vector mode; anything else? – Mr.Wizard Feb 23 '13 at 04:54
  • @Mr.Wizard now that you mention it, it seems it has either improved in recent versions or I can't remember where I was running into poor performance... – Andy Ross Feb 23 '13 at 16:47
2

This is a pretty clumsy way but it works:

# /. a[_] -> First@Cases[#, a[_], 2, 1] & /@ Apply[Plus, 
       GatherBy[list, f@First@Cases[#, a[_], 1, 1] &],1]//Simplify

Step by step:

list = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};
f[x_a] := MemberQ[{1, 3, 6}, First@x]

(* For each element of the list it picks out the first (and only) a[i]
   and applies f to it. Then does what the function name suggests *)
gb=GatherBy[list, f@First@Cases[#, a[_], 1, 1] &]
(* {{a[1] cof[1], a[3] cof[3], a[6] cof[6]},
    {a[2] cof[2], a[4] cof[4], a[5] cof[5]}} *)

(* Add the elemens of each group *)
ap=Apply[Plus,gb,1]
(* {a[1] cof[1] + a[3] cof[3] + a[6] cof[6], 
    a[2] cof[2] + a[4] cof[4] + a[5] cof[5]} *)

(* For each group replace all a[i] with just the first one *)
rep = # /. a[_] -> First@Cases[#, a[_], 2, 1] & /@ ap
(* {a[1] cof[1] + a[1] cof[3] + a[1] cof[6], a[2] cof[2] + a[2] cof[4] + a[2] cof[5]} *)

 Simplify@rep
 (* {a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4] + cof[5])} *)
ssch
  • 16,590
  • 2
  • 53
  • 88
1

Trying to write a compact version. Your initial data:

l = {a[1] cof[1], a[2] cof[2], a[3] cof[3], a[4] cof[4], a[5] cof[5], a[6] cof[6]};
p = {f[a[1]] == f[a[3]] == f[a[6]], f[a[2]] == f[a[4]]};

And then just:

List @@ Simplify[ Total[l /. Flatten[Thread[# -> #[[1]]] & /@ (List @@@ p /. f@x_ -> x)]]]

(*
{a[2] (cof[2] + cof[4]), a[5] cof[5], a[1] (cof[1] + cof[3] + cof[6])}
*)
Dr. belisarius
  • 115,881
  • 13
  • 203
  • 453
1
 list = Array[a[#] cof[#] &, {6}];

 Simplify /@ Total /@ GatherBy[#, #[[1, 1]] &] &@
   MapAt[# /. Join[Thread[{1, 3, 6} -> 1], Thread[{2, 4} -> 2]] &, list, {All, 1}]

or

 Collect[#, a[_]] & /@ Total /@ GatherBy[#, #[[1, 1]] &] &[
   list /. {PatternSequence[a[1 | 3 | 6] x_] :> a[1] x, 
        PatternSequence[a[2 | 4] x_] :> a[2] x}]
 (* {a[1] (cof[1] + cof[3] + cof[6]), a[2] (cof[2] + cof[4]), a[5] cof[5]} *)
kglr
  • 394,356
  • 18
  • 477
  • 896