[Tool] Resistors combinaition helper

GroupDIY Audio Forum

Help Support GroupDIY Audio Forum:

This site may earn a commission from merchant affiliate links, including eBay, Amazon, and others.

Noth

Active member
Joined
Nov 19, 2021
Messages
36
Location
Fr
Hi,

Over the last weeks I regularly ended up with the following issue:

"I need that very specific resistor value, how can I achieve it with what I have in stock?"

Short story:

I ended up writing a tool which is available here: RADIX Studio - RPick

Step 1 : select what you have in stock
Step 2 : enter your "target value" (ie. what value you need), then click the button
Step 3 : the tool will propose the closest values you can achieve using 2 or 3 resistors (in series, parallel, 2 in parallel + 1 in series or 2 in series in parallel with 1.



Long story:

Initially the tool was a genetic algorithm, iterating from generations to generations, trying to get closer to the value.
I ended up realizing that the tool was finding a close value super fast. Thus I wrote a very dumb brute-force algorithm (test every possibiliy) and... it just works instantly.
Our machines are now too powerful, the fun is gone!


A similar tool may eventually be available somewhere else, but I was not able to find it, so if mine can be useful, enjoy!
 
I use the Wilmann tables published by Douglas Self, I've wanted to make a code that finds the best fit as found in the Wilmann tables, but I've been too lazy to get it done. Perhaps you are more motivated than I am.
 
Last edited:
Hi,

Over the last weeks I regularly ended up with the following issue:

"I need that very specific resistor value, how can I achieve it with what I have in stock?"

Short story:

I ended up writing a tool which is available here: RADIX Studio - RPick

Step 1 : select what you have in stock
Step 2 : enter your "target value" (ie. what value you need), then click the button
Step 3 : the tool will propose the closest values you can achieve using 2 or 3 resistors (in series, parallel, 2 in parallel + 1 in series or 2 in series in parallel with 1.



Long story:

Initially the tool was a genetic algorithm, iterating from generations to generations, trying to get closer to the value.
I ended up realizing that the tool was finding a close value super fast. Thus I wrote a very dumb brute-force algorithm (test every possibiliy) and... it just works instantly.
Our machines are now too powerful, the fun is gone!


A similar tool may eventually be available somewhere else, but I was not able to find it, so if mine can be useful, enjoy!
I once created an excel spreadsheet that allows entering any desired resistance, and it would green-highlight every possible combination of series or parallel standard commercial resistor values (1%, 0.1% and 0.01%) that fell within 1% of the entered value. It saved me considerable time when I was doing design work every day.
 
I once created an excel spreadsheet that allows entering any desired resistance, and it would green-highlight every possible combination of series or parallel standard commercial resistor values (1%, 0.1% and 0.01%) that fell within 1% of the entered value. It saved me considerable time when I was doing design work every day.
Pics or it didn't happen. No, but seriously, I have been wanting to publish several scripts here at GDIY, this is one of them. I haven't been able to do it for a variety of reasons, the death of a close family member is one of them. But if any of you have something to share, you would be doing a great service to the community,
 
If I still have the file, and can find it, I'd be happy to share it. I retired 5 years ago, so it's possible I didn't bring it home. I'll take a look.
Frankly, it wasn't very hard to create, took an hour or two.
 
Last edited:
If I still have the file, and can find it, I'd be happy to share it. I retired 5 years ago, so it's possible I didn't bring it home. I'll take a look.
Frankly, it wasn't very hard to create, took an hour or two.
I was meaning to do something like that in MATLAB which uses series or parallel combinations of commercial resistors in such a way that it minimizes the standard deviation of the resulting combination assuming a normal distribution centered on the mean. I will work on it, but it will take some time, which is something I lack.
 
Super simple to brute force in python. Even testing 3 parallel combinations of all 480 1% resistor values less than 1MEG only takes about 100 seconds on a cheap Chromebook. With 5% resistor values, less than a second.

Python:
import itertools
import sys

TARGET_VALUE = 982
NUM_RESISTORS = 3

# 1% Resistor values (E96).
RESISTOR_VALUES = [
    10.0, 10.2, 10.5, 10.7, 11.0, 11.3, 11.5, 11.8, 12.1, 12.4, 12.7, 13.0, 13.3, 13.7, 14.0, 14.3,
    14.7, 15.0, 15.4, 15.8, 16.2, 16.5, 16.9, 17.4, 17.8, 18.2, 18.7, 19.1, 19.6, 20.0, 20.5, 21.0,
    21.5, 22.1, 22.6, 23.2, 23.7, 24.3, 24.9, 25.5, 26.1, 26.7, 27.4, 28.0, 28.7, 29.4, 30.1, 30.9,
    31.6, 32.4, 33.2, 34.0, 34.8, 35.7, 36.5, 37.4, 38.3, 39.2, 40.2, 41.2, 42.2, 43.2, 44.2, 45.3,
    46.4, 47.5, 48.7, 49.9, 51.1, 52.3, 53.6, 54.9, 56.2, 57.6, 59.0, 60.4, 61.9, 63.4, 64.9, 66.5,
    68.1, 69.8, 71.5, 73.2, 75.0, 76.8, 78.7, 80.6, 82.5, 84.5, 86.6, 88.7, 90.9, 93.1, 95.3, 97.6
]

# 5% resistor values (E24).
#RESISTOR_VALUES = [
#    10.0, 11.0, 12.0, 13.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 27.0, 30.0, 33.0, 36.0, 39.0, 43.0,
#    47.0, 51.0, 56.0, 62.0, 68.0, 75.0, 82.0, 91.0
#]

# Number of multipliers to test.
MULTIPLIERS = [1, 10, 100, 1000, 10000]

all_resistors = []

def parallel_combo(res):
    # Compute denominator.
    den = 0.0
   
    for r in res:
        den += 1/float(r)
       
    return 1.0/den
   
# Create a composite list of all multipliers of all standard values.
for c in itertools.product(RESISTOR_VALUES, MULTIPLIERS):
    all_resistors.append(c[0]*c[1])

closest_combo = []
error = sys.float_info.max

# Run all combinations of resistors.
for n in itertools.product(all_resistors, repeat=NUM_RESISTORS):
    p = parallel_combo(n)
    e = abs(TARGET_VALUE - p)
    if e < error:
        closest_combo = n
        error = e

final_p = parallel_combo(closest_combo)
print(f"Best combo of {NUM_RESISTORS} resistors to make {TARGET_VALUE} ohms: {closest_combo} which is {final_p} ohms")

Python:
Best combo of 3 resistors to make 982 ohms: (1540.0, 249000.0, 2740.0) which is 981.9997315736329 ohms.
 
Super simple to brute force in python. Even testing 3 parallel combinations of all 480 1% resistor values less than 1MEG only takes about 100 seconds on a cheap Chromebook. With 5% resistor values, less than a second.

Python:
import itertools
import sys

TARGET_VALUE = 982
NUM_RESISTORS = 3

# 1% Resistor values (E96).
RESISTOR_VALUES = [
    10.0, 10.2, 10.5, 10.7, 11.0, 11.3, 11.5, 11.8, 12.1, 12.4, 12.7, 13.0, 13.3, 13.7, 14.0, 14.3,
    14.7, 15.0, 15.4, 15.8, 16.2, 16.5, 16.9, 17.4, 17.8, 18.2, 18.7, 19.1, 19.6, 20.0, 20.5, 21.0,
    21.5, 22.1, 22.6, 23.2, 23.7, 24.3, 24.9, 25.5, 26.1, 26.7, 27.4, 28.0, 28.7, 29.4, 30.1, 30.9,
    31.6, 32.4, 33.2, 34.0, 34.8, 35.7, 36.5, 37.4, 38.3, 39.2, 40.2, 41.2, 42.2, 43.2, 44.2, 45.3,
    46.4, 47.5, 48.7, 49.9, 51.1, 52.3, 53.6, 54.9, 56.2, 57.6, 59.0, 60.4, 61.9, 63.4, 64.9, 66.5,
    68.1, 69.8, 71.5, 73.2, 75.0, 76.8, 78.7, 80.6, 82.5, 84.5, 86.6, 88.7, 90.9, 93.1, 95.3, 97.6
]

# 5% resistor values (E24).
#RESISTOR_VALUES = [
#    10.0, 11.0, 12.0, 13.0, 15.0, 16.0, 18.0, 20.0, 22.0, 24.0, 27.0, 30.0, 33.0, 36.0, 39.0, 43.0,
#    47.0, 51.0, 56.0, 62.0, 68.0, 75.0, 82.0, 91.0
#]

# Number of multipliers to test.
MULTIPLIERS = [1, 10, 100, 1000, 10000]

all_resistors = []

def parallel_combo(res):
    # Compute denominator.
    den = 0.0
 
    for r in res:
        den += 1/float(r)
    
    return 1.0/den
 
# Create a composite list of all multipliers of all standard values.
for c in itertools.product(RESISTOR_VALUES, MULTIPLIERS):
    all_resistors.append(c[0]*c[1])

closest_combo = []
error = sys.float_info.max

# Run all combinations of resistors.
for n in itertools.product(all_resistors, repeat=NUM_RESISTORS):
    p = parallel_combo(n)
    e = abs(TARGET_VALUE - p)
    if e < error:
        closest_combo = n
        error = e

final_p = parallel_combo(closest_combo)
print(f"Best combo of {NUM_RESISTORS} resistors to make {TARGET_VALUE} ohms: {closest_combo} which is {final_p} ohms")

Python:
Best combo of 3 resistors to make 982 ohms: (1540.0, 249000.0, 2740.0) which is 981.9997315736329 ohms.
Nice, but, it would be better if the software could not only offer a combination that gets the value, but also a combination that minimizes the standard deviation (tolerance stackup) of the resulting value. This is achieved by selecting resistors very close in value to each other. For instance, in your example, one resistor is 1540 and the other 249000, they differ by more than 2 orders of magnitude.

So, take for instance 982 = 2.2K || 2.4K || 6.8K = 982.06 [E24] this is less precise than the value you obtained, but notice that if we consider 1% resistors, the resulting 982.06 ohm resistor has a 0.623% tolerance, your value of 981.9997 is more exact, but the if we consider 1% tol resistors, the resulting 981.9997 resistor has a tolerance of 0.732 %. So, that means that the combination I obtained can vary between 975.98 and 988.18, and the one you obtained can vary between 974.86 and 989.19; which represents a wider range. Of course, several assumptions are made in order for these figures to be true, like a normal distribution and a mean value centered at 982; which in many cases does happen, but it is not a guarantee....

Notice that the lowest tolerance values are obtained with resistors of the same value, for instance 1K || 1K || 1K = 333.333333 with 0.577735% tolerance (assuming each resistor is 1% tol).
 
Last edited:
It depends on the application, and if the half an ohm makes a difference. 99.99% of audio probably falls outside that bucket. 😃

What you are describing is something you might want to do to guarantee thousands of units are close where every ohm counts.

However the original poster said he had a stack of values on hand, and would it be possible to get close to a target value with some arbitrary combination of stuff he had, which is the script I made.

Another interesting sub-problem is when you permute amongst the different series/parallel combinations as well.
 
It depends on the application, and if the half an ohm makes a difference. 99.99% of audio probably falls outside that bucket. 😃

What you are describing is something you might want to do to guarantee thousands of units are close where every ohm counts.

However the original poster said he had a stack of values on hand, and would it be possible to get close to a target value with some arbitrary combination of stuff he had, which is the script I made.

Another interesting sub-problem is when you permute amongst the different series/parallel combinations as well.
You are totally correct. It is splitting hairs, I know. Your code will suffice for 99.99 % of audio applications as you say. IIRC you have a computer science background, so, in any case, what you code will probably be a lot better than mine. But I've been wanting to do that tolerance stack-up code for a while now, I just haven't been able to do it.
 
But I've been wanting to do that tolerance stack-up code for a while now, I just haven't been able to do it.
In case it helps, tolerance is indeed normally distributed, meaning that the 1% tolerance represents the 3-sigma window for resistor values (e.g. 98% of resistors will be between +- 1% of the value), with the mean at the expected nominal value. Since we know that, sums of resistors (e.g. series) will also be normally distributed, and products of resistors (e.g. parallel) will be proportional to a normal distribution.

If we minimize RMS error (meaning the square root of the average squared error away from the target value), we get slightly different results. For example, your solution has an RMS error of 8.018662783279485 ohms:

2.2K || 2.4K || 6.8K
Min: 972.2363238512035
Nominal: 982.0568927789934
Max: 991.8774617067835 (2200.0, 2400.0, 6800.0)
Std dev.: 192.89685849585447
RMS error: 8.018662783279485

However 270K || 68K || 1K has a slightly lower RMS error:

270K || 68K || 1K
Min: 972.1039683388599
Nominal: 981.9232003422826
Max: 991.7424323457055 (270000.0, 68000.0, 1000.0)
Std dev.: 192.85232883636257
RMS error: 8.017737187768184

You can see this is because although your min R value is closer to the target, the second solution is closer in nominal and maximum values, even accounting for tolerance.

Interestingly enough, the math shows that to minimize error, one of the resistors should be as close to the target value as possible (e.g. 1K is close to 982).
 
In case it helps, tolerance is indeed normally distributed, meaning that the 1% tolerance represents the 3-sigma window for resistor values (e.g. 98% of resistors will be between +- 1% of the value), with the mean at the expected nominal value. Since we know that, sums of resistors (e.g. series) will also be normally distributed, and products of resistors (e.g. parallel) will be proportional to a normal distribution.

If we minimize RMS error (meaning the square root of the average squared error away from the target value), we get slightly different results. For example, your solution has an RMS error of 8.018662783279485 ohms:

2.2K || 2.4K || 6.8K
Min: 972.2363238512035
Nominal: 982.0568927789934
Max: 991.8774617067835 (2200.0, 2400.0, 6800.0)
Std dev.: 192.89685849585447
RMS error: 8.018662783279485

However 270K || 68K || 1K has a slightly lower RMS error:

270K || 68K || 1K
Min: 972.1039683388599
Nominal: 981.9232003422826
Max: 991.7424323457055 (270000.0, 68000.0, 1000.0)
Std dev.: 192.85232883636257
RMS error: 8.017737187768184

You can see this is because although your min R value is closer to the target, the second solution is closer in nominal and maximum values, even accounting for tolerance.

Interestingly enough, the math shows that to minimize error, one of the resistors should be as close to the target value as possible (e.g. 1K is close to 982).
Interesting, what calculations are you using to obtain those std dev values, they don't make much sense to me, and the ranges between min max? In case of the tolerances, assuming each resistor value is centered at the mean, the resulting tolerance would be be the RMS sum of the variances. And the tolerance is essentially 3sigma, hence, for the values you proposed (since you are using parallel combinations, conductances should be used), the standard tolerance stackup equation (RSS) gives:

tol%= sqrt[(0.01*270K^(-1)/3)^2+(0.01*68K^(-1)/3)^2+(0.01*1K^(-1)/3)^2]/(981.9232003422826^(-1))X3X100= 0.982 %

Hence, the resulting resistor acts like a 981.923 resistor with a tolerance of 0.982% the max being 991.565 and min 972.374
 
Interesting, what calculations are you using to obtain those std dev values, they don't make much sense to me, and the ranges between min max?
It's RMS error, like would be used in a Kalman filter. You compute the min, nominal, and maximum resistance: min assumes all resistors at the extreme low end, nominal is the average case, and max assumes all resistances at the max end.

So RMS error would be:

sqrt( [(target-min)^2 + (target-nominal)^2 + (target-max)^2)]/3 )

How-to-Calculate-Root-Mean-Square-Error-001.png


EDIT: I think our equations are the same, it just that yours is normalized like R-squared as a percentage, rather than absolute like mine (which is directly interpreted as 'average error in ohms').
 
It's RMS error, like would be used in a Kalman filter. You compute the min, nominal, and maximum resistance: min assumes all resistors at the extreme low end, nominal is the average case, and max assumes all resistances at the max end.

So RMS error would be:

sqrt( [(target-min)^2 + (target-nominal)^2 + (target-max)^2)]/3 )

How-to-Calculate-Root-Mean-Square-Error-001.png


EDIT: I think our equations are the same, it just that yours is normalized like R-squared as a percentage, rather than absolute like mine (which is directly interpreted as 'average error in ohms').
I understand what you meant with the RMS error and the min max, but now I know where is the discrepancy. Yes, mine is normalized. I am basically summing the square of the std dev divided by 3 (since the tolerance is essentially 3sigma), which is the numerator of the RMSE, without dividing between n, so it is essentially the RSS (residual sum of squares); which is used for tolerance stack up analysis.

The main difference is that by taking the RMSE rather than the square root of RSS, you are taking the average of the variances and then taking the square root of that, which gives you the standard error but not the new standard deviation, while I am just summing the variances and taking the square root to obtain the standard deviation of the sum, they are not the same thing. When doing stack up analysis, I believe it is better to use the RSS method rather than the RMSE method, because we want to know what the final variance will be based on the individual variances, not the average of the resulting variance. By using RSS, with the resistor values I provided the min max values are 975.98 and 988.17, respectively (0.622% tolerance), which means a closer interval than the one you computed, while the min max values of the combination you provided yield 972.37 an 991.56 (0.982% tolerance) .

Here is an example applied to mechanical stuff using tolerance stackup analysis
 
Last edited:
By using RSS, with the resistor values I provided the min max values are 975.98 and 988.17, respectively (0.622% tolerance), which means a closer interval than the one you computed, while the min max values of the combination you provided yield 972.37 an 991.56 (0.982% tolerance) .
Is that correct? I compute the minimal value for your solution to be 972.23. It's 2.178K || 2.376K || 6.732K, right?
 
Is that correct? I compute the minimal value for your solution to be 972.23. It's 2.178K || 2.376K || 6.732K, right?
ahhh but that is different, you are taking the worst case scenario, that is a different method. That indeed is deemed to give worse results, however, it is less likely to occur. That is why I wanted to know how did you compute the min max values. You are taking the extremes, I was computing the min max based on a probabilistic or statistic distribution, you are taking the min max values based on the worst case scenario.

Edit: both what you (worst-case) and I (probabilistic) did are correct approaches, it depends on what one is looking for. In fact, in a mass production scheme one should do both, but the worst-case scenario is much less likely to occur.
 
Last edited:
I'm interested in your equation, but I can't parse out the text. Would you mind Latex'ing it so I can interpret it correctly?

I get that IF you assume that 1% means 3-sigma (not conclusively proven in the literature, because of how manufacturers do binning, meaning they are partitioning the normal distribution by price - in order words, there is a lot of evidence that 1% and higher tolerance parts are actually bi-modal in practice), so the std dev of 1% is just 1/3 of the resistance excursions. For example: 1K @ 1% is 1K +- 10 ohms, so the standard deviation is actually 3.33 ohms: 68% of values lie between plus or minus 3.33 ohms of 1K, etc, etc.

I just found an interesting write-up on sensitivity analysis that I'll peruse and see if I get more interesting answers.
 
Thr
I'm interested in your equation, but I can't parse out the text. Would you mind Latex'ing it so I can interpret it correctly?

I get that IF you assume that 1% means 3-sigma (not conclusively proven in the literature, because of how manufacturers do binning, meaning they are partitioning the normal distribution by price - in order words, there is a lot of evidence that 1% and higher tolerance parts are actually bi-modal in practice), so the std dev of 1% is just 1/3 of the resistance excursions. For example: 1K @ 1% is 1K +- 10 ohms, so the standard deviation is actually 3.33 ohms: 68% of values lie between plus or minus 3.33 ohms of 1K, etc, etc.

I just found an interesting write-up on sensitivity analysis that I'll peruse and see if I get more interesting answers.
The binning theory, from what Ive read, has been debunked. One main issue of that theory is that if you consistently make resistors and keep the ones that are within 1% and discard the rest and sell them as 5% resitors, for example, there would be ve a "hole" in the distribution near the mean value, as you say,but this is something not observed in practice from reputable sellers. Some people have went through the trouble of measuring many resistors of different tolerances and a standard normal distribution is observed. That is without considering that measuring resistors would make the process more expensive and slow, rather than just making lots of them. Also, if you are just binning, and your resistors vary wildly such that you can get 5%,2%,1% out of them, this implies that you will not get the same number out of them. On top of that there are other factors, such as voltage coefficient, tempco, etc... that are not equal among resistances of different tolerances. IIRC, Doug Self is one who is against the binning theory, and he has written extensively on the topic, conducting measurements, and exposing several reasons why it is not true.
 
I'm interested in your equation, but I can't parse out the text. Would you mind Latex'ing it so I can interpret it correctly?
I just cobbled this up in LaTeX, hope it clears things:

1692744619976.png
You could get rid of the "3" in all the equations and it will give you the same numerical answer, but I wanted to make it explicit that the product of the tolerance times resistance (conductance) in ohms (siemens) is 3 times the standard deviation.
 
Last edited:
Strange - where did all the replies go? @user 37518

http://paulorenato.com/index.php/electronics-diy/109-combining-resistors-to-improve-tolerance
After studying the site above I finally groked what was happening: at first, I was sure the RSS analysis was wrong, because the standard deviation of the sum of two normal distributions cannot be less than either singly, however I was neglecting to factor in that the standard deviation is actually an integer divisor of the tolerance (in %), which is always scaled by the (equivalent) resistance (which is different from either of the two source resistors).

So I modified the script, to search for the 'best' combo to make 982 ohms from three resistors: and it decided that actually:

2K7 || 2K7 || 3K6 was the best, being equivalent to 981.8 ohms (only 0.2ohms error), however having a composite tolerance of 0.582% (assuming 1% resistors to begin with).

I'd like to change the script to also test arbitrary series/parallel combinations of standard values, however that requires an abstract expressing tree which is more work than I'm willing to do ATM.
 
Over the last weeks I regularly ended up with the following issue:

"I need that very specific resistor value, how can I achieve it with what I have in stock?"

A similar tool may eventually be available somewhere else, but I was not able to find it, so if mine can be useful, enjoy!

Amazing,
Thank you so much mate, I've been needing a tool like this for ages.
it would be really useful but I can't seem to make you tool work.

I can't enter my resistor stock values...
 
Hi,

Do you have some values that are not on the list?
I should add a text box to add some specific values to the list, I'll add this to my TODO list!
 
Well, that was actually a quick fix..

You can now enter custom values at the bottom of predefined list. The input accepts integer values only, enventually with a K or M suffix.
Enjoy!
 
Back
Top