Message Boards Message Boards

GROUPS:

Finding the combination of two elements nearest to a given value

Posted 8 years ago
3155 Views
|
4 Replies
|
2 Total Likes
|

I wrote a quick calculation for some electronic components (let's say a value of a capacitor). Now I would like to get the best available(!) capacitor calculated. I did this with an array of all available values and the function Nearest.

Unfortunately sometimes the available capacitors are far away from what you actually need. What you can do is to put two in parallel.

  1. Often you have a preference weather the chosen value should be greater or smaller than the calculated value. How can you tell Nearest to only chose smaller/larger values?
  2. Is there some way to allow Nearest to add two values from the list to come closer to the desired value?
POSTED BY: Master Aypac
4 Replies
Posted 8 years ago

Wow that looks neat! Thanks! I don't intuitively understand it, but I'll sit down tonight and try to understand it.

I sat down quite a while too and wrote some really ugly code (at least it works...):

(* Available components calculator *)

availCap = {[see above]};
availTrimCap = {[see above]}; 
availInd = {[see above]};


availTrimCapParallelMat = Outer[Plus, availTrimCap, availTrimCap];
availTrimCapParallel = Union[Flatten[availTrimCapParallelMat]];
availTrimCapSeriesMat = 
  Outer[(#1 #2)/(#1 + #2) &, availTrimCap, availTrimCap];
availTrimCapSeries = Union[Flatten[availTrimCapSeriesMat]];

availTrimCap2 = Join[availTrimCap, availTrimCapParallel];

availCapParallelMat = Outer[Plus, availCap, availCap];
availCapParallel = Union[Flatten[availCapParallelMat]];
availCapSeriesMat = Outer[(#1 #2)/(#1 + #2) &, availCap, availCap];
availCapSeries = Union[Flatten[availCapSeriesMat]];
availCap2 = Join[availCap, availCapParallel, availCapSeries];

availIndSeriesMat = Outer[Plus, availInd, availInd];
availIndSeries = Union[Flatten[availIndSeriesMat]];
availIndParallelMat = Outer[(#1 #2)/(#1 + #2) &, availInd, availInd];
availIndParallel = Union[Flatten[availIndParallelMat]];
availInd2 = Join[availInd, availIndParallel, availIndSeries];

availResSeriesMat = Outer[Plus, availRes, availRes];
availResSeries = Union[Flatten[availResSeriesMat]];
availResParallelMat = Outer[(#1 #2)/(#1 + #2) &, availRes, availRes];
availResParallel = Union[Flatten[availResParallelMat]];
availRes2 = Join[availRes, availResParallel, availResSeries];

findCombination[array_, matrix_, val_] := 
 Outer[array[[#1]] &, Position[matrix, val]]
findRealCombination[array_, matrix_, flatMatrix_, val_, n_] := 
 Outer[{val, #1, findCombination[array, matrix, #1]} &, 
  Nearest[flatMatrix, val, n]]

(* unit = {{-24, "y"}, {-21, "z"}, {-18, "a"}, {-15, "f"}, {-12, 
    "p"}, {-9, "n"}, {-6, "\[Micro]"}, {-3, "m"}, {3, "k"}, {6, 
    "M"}, {9, "G"}, {12, "T"}, {15, "P"}, {18, "E"}, {21, "Z"}, {24, 
    "Y"}}; *)
unitFormatNeg[val_] := 
  If[Abs[val] > 10^(-3), N[val/10^(-3)] "m", 
   If[Abs[val] > 10^(-6), N[Abs[val]/10^(-6)] "\[Micro]", 
    If[Abs[val] > 10^(-9), N[val/10^(-9)] "n", 
     If[Abs[val] > 10^(-12), N[val/10^(-12)] "p", 
      ScientificForm[val]]]]];
unitFormatPos[val_] := 
  If[Abs[val] < 10^3, N[val], 
   If[Abs[val] < 10^4, N[val/10^3] "k", 
    If[Abs[val] < 10^7, N[val/10^6] "M", ScientificForm[val]]]];
unitFormat[val_] := 
  If[Abs[val] < 1, unitFormatNeg[val], unitFormatPos[Abs[val]]];
SetAttributes[unitFormat, Listable];(*changes 10^x to SI prefix*)

printACombination[array_] := 
 Print[unitFormat[array[[2]]], " near ", unitFormat[array[[1]]], 
  " combine one of these pairs ", unitFormat[array[[3]]]] 
printCombination[comb_] := printACombination /@ comb;


findResCombSeries[val_, n_] := 
  findRealCombination[availRes, availResSeriesMat, availResSeries, 
   val, n];
findResCombParallel[val_, n_] := 
  findRealCombination[availRes, availResParallelMat, availResParallel,
    val, n];
findResComb[val_, n_] := 
  Join[findResCombSeries[val, n], findResCombParallel[val, n]];
findIndCombSeries[val_, n_] := 
  findRealCombination[availInd, availIndSeriesMat, availIndSeries, 
   val, n];
findIndCombParallel[val_, n_] := 
  findRealCombination[availInd, availIndParallelMat, availIndParallel,
    val, n];
findIndComb[val_, n_] := 
  Join[findIndCombSeries[val, n], findIndCombParallel[val, n]];
findCapCombSeries[val_, n_] := 
  findRealCombination[availCap, availCapSeriesMat, availCapSeries, 
   val, n];
findCapCombParallel[val_, n_] := 
  findRealCombination[availCap, availCapParallelMat, availCapParallel,
    val, n];
findCapComb[val_, n_] := 
  Join[findCapCombSeries[val, n], findCapCombParallel[val, n]];
findTrimCapCombSeries[val_, n_] := 
  findRealCombination[availTrimCap, availTrimCapSeriesMat, 
   availTrimCapSeries, val, n];
findTrimCapCombParallel[val_, n_] := 
  findRealCombination[availTrimCap, availTrimCapParallelMat, 
   availTrimCapParallel, val, n];
findTrimCapComb[val_, n_] := 
  Join[findTrimCapCombSeries[val, n], findTrimCapCombParallel[val, n]];

(*Usage example: *)
comb=findIndComb[47*10^(-9),3];
printCombination[comb];

Will output

47. n near 47. n combine one of these pairs {{11. n,36. n},{20. n,27. n},{27. n,20. n},{36. n,11. n}}
46.9 n near 47. n combine one of these pairs {{3.9 n,43. n},{43. n,3.9 n}}
47.2 n near 47. n combine one of these pairs {{39. n,8.2 n},{8.2 n,39. n}}
46.9997 n near 47. n combine one of these pairs {{47. n,8.7 m},{8.7 m,47. n}}
46.9997 n near 47. n combine one of these pairs {{47. n,8.2 m},{8.2 m,47. n}}
46.9997 n near 47. n combine one of these pairs {{47. n,7.5 m},{7.5 m,47. n}}

Which is fine. But this code is a mess. I'd love to hear suggestions on how to improve it! Especially since I basically have every piece of code three times...

POSTED BY: Master Aypac
Posted 8 years ago

This works, but should be simpler

In[1]:= r = {100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 
   300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910};
rseries = Union[Flatten[Outer[Plus, r, r]]];
rparallel = Union[Flatten[Outer[(#1 #2)/(#1 + #2) &, r, r]]];
Cases[Join[
   Map[{#, #, 0} &, r],
   Union[Flatten[Outer[{#1 + #2, Sort[{#1, #2}]} &, r, r], 1]], 
   Union[Flatten[Outer[{(#1 #2)/(#1 + #2), Sort[{#1, #2}]} &, r, r], 
     1]]], {Nearest[Join[r, rseries, rparallel], 139][[1]], __}] // N

Out[4]= {{138.947, {240., 330.}}}

What that does:

Do the same Nearest that I did in my first response to you to get a result.

Rebuild all those lists, but now as {combined r1 and r2, {r1, r2}} or as {r1, {r1, 0}} for single components.

Use Cases to extract all those tuples that match the result returned from Nearest.

This is what I would like to do

r = {100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 300, 330, 
   360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910};
rr = Map[{#, {#, 0}}&, r];
rseries = Union[Flatten[Outer[{#1 + #2, Sort[{#1, #2}]}&, r, r], 1]];
rparallel = Union[Flatten[Outer[{(#1 #2)/(#1 + #2), Sort[{#1, #2}]}&, r, r], 1]];
Nearest[Join[rr, rseries, rparallel], {139,{}}, DistanceFunction->Norm[First[#1]-First[#2]]&]//N

but I can't get Nearest to be happy with this.

I have had this problem in the past with a few other functions, where they are happy to work with two lists of scalars, but I can't figure out how to get them to behave the same way when they are given two lists of tuples. Perhaps someone can point out the solution to my lack of understanding.

POSTED BY: Bill Simpson
Posted 8 years ago

For anyone who wants to do the same, here are my values

(* Available components *)

availCap = {10*10^(-12), 100*10^(-12), 1.0*10^(-9), 10*10^(-9), 
   100*10^(-9), 1.0*10^(-6), 10*10^(-6), 100*10^(-6), 1.0*10^(-3), 
   10*10^(-3), 12*10^(-12), 120*10^(-12), 1.2*10^(-9), 12*10^(-9), 
   120*10^(-9), 1.2*10^(-6), 15*10^(-12), 150*10^(-12), 1.5*10^(-9), 
   15*10^(-9), 150*10^(-9), 1.5*10^(-6), 15*10^(-6), 150*10^(-6), 
   1.5*10^(-3), 15*10^(-3), 18*10^(-12), 180*10^(-12), 1.8*10^(-9), 
   18*10^(-9), 180*10^(-9), 1.8*10^(-6), 2.2*10^(-12), 22*10^(-12), 
   220*10^(-12), 2.2*10^(-9), 22*10^(-9), 220*10^(-9), 2.2*10^(-6), 
   22*10^(-6), 220*10^(-6), 2.2*10^(-3), 22*10^(-3), 27*10^(-12), 
   270*10^(-12), 2.7*10^(-9), 27*10^(-9), 270*10^(-9), 2.7*10^(-6), 
   3.3*10^(-12), 33*10^(-12), 330*10^(-12), 3.3*10^(-9), 33*10^(-9), 
   330*10^(-9), 3.3*10^(-6), 33*10^(-6), 330*10^(-6), 3.3*10^(-3), 
   33*10^(-3), 39*10^(-12), 390*10^(-12), 3.9*10^(-9), 39*10^(-9), 
   390*10^(-9), 3.9*10^(-6), 4.7*10^(-12), 47*10^(-12), 470*10^(-12), 
   4.7*10^(-9), 47*10^(-9), 470*10^(-9), 4.7*10^(-6), 47*10^(-6), 
   470*10^(-6), 4.7*10^(-3), 47*10^(-3), 56*10^(-12), 560*10^(-12), 
   5.6*10^(-9), 56*10^(-9), 560*10^(-9), 5.6*10^(-6), 6.8*10^(-12), 
   68*10^(-12), 680*10^(-12), 6.8*10^(-9), 68*10^(-9), 680*10^(-9), 
   6.8*10^(-6), 68*10^(-6), 680*10^(-6), 6.8*10^(-3), 68*10^(-3), 
   82*10^(-12), 820*10^(-12), 8.2*10^(-9), 82*10^(-9), 820*10^(-9), 
   8.2*10^(-6)} ;
(*In Farrad. Extracted from 
http://www.kennethkuhn.com/students/rlc_values.pdf with gedit's 
replace-tools. *)

availTrimCap = {3*10^(-12), 6*10^(-12), 10*10^(-12), 20*10^(-12), 
   25*10^(-12), 30*10^(-12), 40*10^(-12), 
   50*10^(-12)}; (* In Farrad. Extracted by hand from 
http://www.conrad.de/ce/de/overview/0241600/Dreh-Kondensatoren *)

availInd = {1.0*10^(-9), 10*10^(-9), 100*10^(-9), 1000*10^(-9), 
   1.1*10^(-9), 11*10^(-9), 110*10^(-9), 1100*10^(-9), 1.2*10^(-9), 
   12*10^(-9), 120*10^(-9), 1200*10^(-9), 1.3*10^(-9), 13*10^(-9), 
   130*10^(-9), 1300*10^(-9), 1.5*10^(-9), 15*10^(-9), 150*10^(-9), 
   1500*10^(-9), 1.6*10^(-9), 16*10^(-9), 160*10^(-9), 1600*10^(-9), 
   1.8*10^(-9), 18*10^(-9), 180*10^(-9), 1800*10^(-9), 2.0*10^(-9), 
   20*10^(-9), 200*10^(-9), 2000*10^(-9), 2.2*10^(-9), 22*10^(-9), 
   220*10^(-9), 2200*10^(-9), 2.4*10^(-9), 24*10^(-9), 240*10^(-9), 
   2400*10^(-9), 2.7*10^(-9), 27*10^(-9), 270*10^(-9), 2700*10^(-9), 
   3.0*10^(-9), 30*10^(-9), 300*10^(-9), 3000*10^(-9), 3.3*10^(-9), 
   33*10^(-9), 330*10^(-9), 3300*10^(-9), 3.6*10^(-9), 36*10^(-9), 
   360*10^(-9), 3600*10^(-9), 3.9*10^(-9), 39*10^(-9), 390*10^(-9), 
   3900*10^(-9), 4.3*10^(-9), 43*10^(-9), 430*10^(-9), 4300*10^(-9), 
   4.7*10^(-9), 47*10^(-9), 470*10^(-9), 4700*10^(-9), 5.1*10^(-9), 
   51*10^(-9), 510*10^(-9), 5100*10^(-9), 5.6*10^(-9), 56*10^(-9), 
   560*10^(-9), 5600*10^(-9), 6.2*10^(-9), 62*10^(-9), 620*10^(-9), 
   6200*10^(-9), 6.8*10^(-9), 68*10^(-9), 680*10^(-9), 6800*10^(-9), 
   7.5*10^(-9), 75*10^(-9), 750*10^(-9), 7500*10^(-9), 8.2*10^(-9), 
   82*10^(-9), 820*10^(-9), 8200*10^(-9), 8.7*10^(-9), 87*10^(-9), 
   870*10^(-9), 8700*10^(-9), 9.1*10^(-9), 91*10^(-9), 910*10^(-9), 
   9100*10^(-9), 1.0*10^(-6), 10*10^(-6), 100*10^(-6), 1000*10^(-6), 
   1.1*10^(-6), 11*10^(-6), 110*10^(-6), 1100*10^(-6), 1.2*10^(-6), 
   12*10^(-6), 120*10^(-6), 1200*10^(-6), 1.3*10^(-6), 13*10^(-6), 
   130*10^(-6), 1300*10^(-6), 1.5*10^(-6), 15*10^(-6), 150*10^(-6), 
   1500*10^(-6), 1.6*10^(-6), 16*10^(-6), 160*10^(-6), 1600*10^(-6), 
   1.8*10^(-6), 18*10^(-6), 180*10^(-6), 1800*10^(-6), 2.0*10^(-6), 
   20*10^(-6), 200*10^(-6), 2000*10^(-6), 2.2*10^(-6), 22*10^(-6), 
   220*10^(-6), 2200*10^(-6), 2.4*10^(-6), 24*10^(-6), 240*10^(-6), 
   2400*10^(-6), 2.7*10^(-6), 27*10^(-6), 270*10^(-6), 2700*10^(-6), 
   3.0*10^(-6), 30*10^(-6), 300*10^(-6), 3000*10^(-6), 3.3*10^(-6), 
   33*10^(-6), 330*10^(-6), 3300*10^(-6), 3.6*10^(-6), 36*10^(-6), 
   360*10^(-6), 3600*10^(-6), 3.9*10^(-6), 39*10^(-6), 390*10^(-6), 
   3900*10^(-6), 4.3*10^(-6), 43*10^(-6), 430*10^(-6), 4300*10^(-6), 
   4.7*10^(-6), 47*10^(-6), 470*10^(-6), 4700*10^(-6), 5.1*10^(-6), 
   51*10^(-6), 510*10^(-6), 5100*10^(-6), 5.6*10^(-6), 56*10^(-6), 
   560*10^(-6), 5600*10^(-6), 6.2*10^(-6), 62*10^(-6), 620*10^(-6), 
   6200*10^(-6), 6.8*10^(-6), 68*10^(-6), 680*10^(-6), 6800*10^(-6), 
   7.5*10^(-6), 75*10^(-6), 750*10^(-6), 7500*10^(-6), 8.2*10^(-6), 
   82*10^(-6), 820*10^(-6), 8200*10^(-6), 8.7*10^(-6), 87*10^(-6), 
   870*10^(-6), 8700*10^(-6), 9.1*10^(-6), 91*10^(-6), 910*10^(-6), 
   9100*10^(-9)}; (* In Henry. Extracted from 
http://www.rfcafe.com/references/electrical/inductor-values.htm with
gedit's replace-tools. *)

availRes = {1.0, 10, 100, 1.0*10^(3), 10*10^(3), 100*10^(3), 
   1.0*10^(6), 10*10^(6), 1.1, 11, 110, 1.1*10^(3), 11*10^(3), 
   110*10^(3), 1.1*10^(6), 11*10^(6), 1.2, 12, 120, 1.2*10^(3), 
   12*10^(3), 120*10^(3), 1.2*10^(6), 12*10^(6), 1.3, 13, 130, 
   1.3*10^(3), 13*10^(3), 130*10^(3), 1.3*10^(6), 13*10^(6), 1.5, 15, 
   150, 1.5*10^(3), 15*10^(3), 150*10^(3), 1.5*10^(6), 15*10^(6), 1.6,
    16, 160, 1.6*10^(3), 16*10^(3), 160*10^(3), 1.6*10^(6), 16*10^(6),
    1.8, 18, 180, 1.8*10^(3), 18*10^(3), 180*10^(3), 1.8*10^(6), 
   18*10^(6), 2.0, 20, 200, 2.0*10^(3), 20*10^(3), 200*10^(3), 
   2.0*10^(6), 20*10^(6), 2.2, 22, 220, 2.2*10^(3), 22*10^(3), 
   220*10^(3), 2.2*10^(6), 22*10^(6), 2.4, 24, 240, 2.4*10^(3), 
   24*10^(3), 240*10^(3), 2.4*10^(6), 2.7, 27, 270, 2.7*10^(3), 
   27*10^(3), 270*10^(3), 2.7*10^(6), 3.0, 30, 300, 3.0*10^(3), 
   30*10^(3), 300*10^(3), 3.0*10^(6), 3.3, 33, 330, 3.3*10^(3), 
   33*10^(3), 330*10^(3), 3.3*10^(6), 3.6, 36, 360, 3.6*10^(3), 
   36*10^(3), 360*10^(3), 3.6*10^(6), 3.9, 39, 390, 3.9*10^(3), 
   39*10^(3), 390*10^(3), 3.9*10^(6), 4.3, 43, 430, 4.3*10^(3), 
   43*10^(3), 430*10^(3), 4.3*10^(6), 4.7, 47, 470, 4.7*10^(3), 
   47*10^(3), 470*10^(3), 4.7*10^(6), 5.1, 51, 510, 5.1*10^(3), 
   51*10^(3), 510*10^(3), 5.1*10^(6), 5.6, 56, 560, 5.6*10^(3), 
   56*10^(3), 560*10^(3), 5.6*10^(6), 6.2, 62, 620, 6.2*10^(3), 
   62*10^(3), 620*10^(3), 6.2*10^(6), 6.8, 68, 680, 6.8*10^(3), 
   68*10^(3), 680*10^(3), 6.8*10^(6), 7.5, 75, 750, 7.5*10^(3), 
   75*10^(3), 750*10^(3), 7.5*10^(6), 8.2, 82, 820, 8.2*10^(3), 
   82*10^(3), 820*10^(3), 8.2*10^(6), 9.1, 91, 910, 9.1*10^(3), 
   91*10^(3), 910*10^(3), 9.1*10^(6)};
(*In Ohm. Extracted from 
http://www.kennethkuhn.com/students/rlc_values.pdf with gedit's
replace-tools. *)

availTrimCapParallel = 
  Union[Flatten[Outer[Plus, availTrimCap, availTrimCap]]];
(* not using availTrimCapSeries=Union[Flatten[Outer[(#1 
#2)/(#1+#2)&,availTrimCap,availTrimCap]]]; since it will be very hard 
to set those right *)

availTrimCap2 = Join[availTrimCap, availTrimCapParallel];

availCapParallel = Union[Flatten[Outer[Plus, availCap, availCap]]];
availCapSeries = 
  Union[Flatten[Outer[(#1 #2)/(#1 + #2) &, availCap, availCap]]];
availCap2 = Join[availCap, availCapParallel, availCapSeries];

availIndSeries = Union[Flatten[Outer[Plus, availInd, availInd]]];
availIndParallel = 
  Union[Flatten[Outer[(#1 #2)/(#1 + #2) &, availInd, availInd]]];
availInd2 = Join[availInd, availIndParallel, availIndSeries];

availResSeries = Union[Flatten[Outer[Plus, availRes, availRes]]];
availResParallel = 
  Union[Flatten[Outer[(#1 #2)/(#1 + #2) &, availRes, availRes]]];
availRes2 = Join[availRes, availResParallel, availResSeries];

Example of Code

C1=7.95775*10^(-10);
L1=110*10^(-6);
L2=230*10^(-6);

C1Trim = C1*0.2;
C1D = C1 - (C1RealTrim/2);
C1RealTrim = Nearest[availTrimCap, C1Trim];
C1Real = Nearest[availCap, C1D, 2];
LAntReal = Nearest[availInd, (L1 + L2), 2];

Print["Real Values with 1 Component: C1=", N[C1Real*10^(12)], "+", 
 N[C1RealTrim*10^(12)], "pF, L=", N[LAntReal*10^9], "nH"]

C1Real2 = Nearest[availCap2, C1D];
LAntReal2 = Nearest[availInd2, (L1 + L2), 2];
Print["Real Values with 1 or 2 Components: C1=", N[C1Real2*10^(12)], "+", 
 N[C1RealTrim*10^(12)], "pF,  L=", N[LAntReal2*10^9], "nH"]

And it works great! Thanks!

But still I have no clue on how to (neatly) get the two real values accordingly. My Idea is to use an associated List; But unfortunately I'm very new to Mathematica and don't really know how to do it.

P.S.: Would you rather use

pico=10^(-12);
nano=10^(-9);
[...]
availCap = {10*pico, 100*pico, 1.0*nano, 10*nano, [...]

or the way I did it?

POSTED BY: Master Aypac
Posted 8 years ago

A few lines of code to select a resistor closest to 139 ohms from the standard 10% values and using at most two resistors.

In[1]:= r = {100, 110, 120, 130, 150, 160, 180, 200, 220, 240, 270, 
   300, 330, 360, 390, 430, 470, 510, 560, 620, 680, 750, 820, 910};
rseries = Union[Flatten[Outer[Plus, r, r]]];
rparallel = Union[Flatten[Outer[(#1 #2)/(#1 + #2) &, r, r]]];
Nearest[Join[r, rseries, rparallel], 139] // N

Out[4]= {138.947}

That can at times find there is more than one combination that gives the closest value.

Then you need to write another couple of lines of code to find what combination gave that value.

Finding smaller or larger values could be done with another couple lines of code, sorting the joined list, selecting those values smaller than or larger than the desired value and then displaying the value from one end or the other of the resulting list.

POSTED BY: Bill Simpson
Reply to this discussion
Community posts can be styled and formatted using the Markdown syntax.
Reply Preview
Attachments
Remove
or Discard

Group Abstract Group Abstract