Python tool to reduce number of resistors in circuit
with open(filename) as json_data:



Good!




        lst = json.load(json_data)
        totalperms *= len(lst)
        pairlists.append(lst)



lst isn't a great name, but I think it's forgiveable with such a tight scope. Maybe worth sanity-checking that the structure is what you expect?




print("Calculating permutations: %d" % totalperms)
perms = list(itertools.product(*pairlists))



Why bother with the code to calculate totalperms if you can just extract it as len(perms)?

Although, on the flip side, why coerce perms to a list and force it all into memory?




# combo is a list of flattened lists of every resistor combination
combo = [[0 for rows in range(len(pairlists)*2)] for cols in range(len(perms))]



I'm not sure what the point of this is. Perhaps the comment should explain why we need it.




for i in range(0,len(perms)):



You're right to think there's a more Pythonic way of doing this:

    for i, perm in enumerate(perms):


This doesn't require perms to be a list, because it removes the need to index into it.




    for j in range(0,len(pairlists)):
        combo[i][2*j] = perms[i][j][0]
        combo[i][2*j+1] = perms[i][j][1]
        price[i] += perms[i][j][2]



This really isn't doing much. Why not inline the calculation of the best option?




        count += 1
        if ( count % 1000000 == 0 ):
            print("%d of %d" % (count,totalperms))



That's buggy: it should be at one less level of indentation.




# Find the combination with the least number of unique resistor values



This is a pet peeve of mine: unique means that there is only one in existence. Distinct expresses precisely and unambiguously what you mean here.




minlen = 999
for s in combo:
    if ( len(set(s)) &lt; len(pairlists)*2 ):
        if ( len(set(s)) &lt; minlen ):
            minlen = len(set(s))



It's not standard Python style to use brackets around the condition. The conditions can be merged into one as

    if len(set(s)) &lt; min(len(pairlists)*2, minlen):
        minlen = len(set(s))


but then that points to just initialising minlen to len(pairlists)*2 instead of the magic number 999.



Finally, this doesn't strike me as likely to be an efficient approach.

I would think it's likely to be more efficient (although still not very) to build a set of the distinct values and then look at subsets in order of increasing size, maybe guided by frequencies of the values.

The best option is probably to try to work out an encoding to a standard problem and use an optimised solver. The obvious one is SAT: you've basically got a Boolean formula already: each resistor value corresponds to a variable, and each file is an OR of a bunch of two-variable ANDs. Then you just need to add a count of the number of variables which are TRUE and require it to be less than a threshold, and search for the critical value of the threshold. For the prices you can use the same SAT formulation and loop:

while sat_instance.solve():
    yield sat_instance.solution()
    add a rule to sat_instance to exclude the solution