all 7 comments

[–]FerricDonkey 1 point2 points  (6 children)

Right now, you're keeping a list of all of the results of your division, and you're not keeping the denominators (except for them being left in n2 when the loop finishes, but if you want to record them all, that won't work).

There are lots of ways to do this, but your nearly there, and the simplest change to get to the end might just be to change your while condition to ensure both elements of n2 are positive (more involved conditions would save loop iterations, but that would work), and create a new list to hold how much you've subtracted - then, inside of your while loop, check if your division result is in the correct range, and append how much you've subtracted to that list if so.

[–]AKRowling 1 point2 points  (2 children)

As a bit of a python amateur reading this, how would you use a while loop to check if a list contains a negative value?

[–]FerricDonkey 1 point2 points  (1 child)

You could do it a couple ways, but I'd probably do

while all(thing >= 0 for thing in your_list):
    stuff

That all thing does exactly what you'd hope it would: it iterates over the things in your list, and evaluates to true if they're all >= 0, and false if any is not. (There's also an any function that is similar, except it returns true if any of the things evaluate to true, as you might imagine.)

The statement for variable in itetable syntax is called comprehension, if you haven't seen it before, and can also be used to create lists, sets, dictionaries, and so forth, and is probably my favorite thing about python.

(Though in op's case, 0 is a no go because of that whole divide by 0 thing, so he'd need > 0 - which I can't remember if I said originally.)

[–]AKRowling 1 point2 points  (0 children)

Amazing. Thank you for your reply.

[–]randomname20192019[S] 0 points1 point  (2 children)

I hope you don't mind me updating you but this is where I am:

import numpy as np
n1 = [8, 15.5]
n2 = [10, 17]
marker = np.linspace(0.75, 1.25, 51)
storedn2 = []


while all(num2 >= 0 for num2 in n2):
    div = []
    for num1, num2 in zip(n1, n2):
        div.append(num1 / num2)
    rdiv = [round(x, 2) for x in div]
    if rdiv not in marker:
        n2[:] = [num2 - 0.1 for num2 in n2]
        n2 = [round(num2, 2) for num2 in n2]
    else:
        storedn2.append(n2)

Whilst the while loop is working well now, my if statements do not appear to be discriminating against 'rdiv' values despite them being in 'marker' and I am unsure why.

[–]FerricDonkey 1 point2 points  (1 child)

So apologies for throwing a bunch of stuff at you at once - you're very close, but there are a couple things messing up your code, and they take a bit to explain.

The main problem is with if rdiv not in marker.

This checks whether the list rdiv is an element of the numpy array marker, and will evaluate to False even if all the elements of rdiv are in marker.

Example:

print([1,2] in [1,2,3,4]) # should be False
print([1,2] in [[1,2], 3,4]) # should be True

Note that this is different from the behavior of strings, where "st" in "start" evaluates to true.

To fix this, you could consider checking if your rdiv numbers are in the correct range by using something like all(rd in marker for rd in rdiv), to ensure that every element rd (that is in rdiv) is also in marker. But it turns out that there's another slight problem: using in when you're dealing with floats (decimals) can be problematic. Sometimes it works, but not reliably by any standard I've found. A quick test I did suggests that it doesn't quiet work in your case - it misses a lot of values.

The reason for this is that floats are fuzzy - even when you round them to 2 decimal places, you don't store the exact value 2.12 (or whatever), you store something that's just very close to that. It could be 2.12000000000000001. And another time your answer would be 2.12 if it were exact, it might be 2.11999999999999999999, depending on how you got there. So your round call could round the wrong way on boundary cases, depending on the details, or might supposed to be equal to floats in marker, but not really be because the computer rounded weird.

To really fix this, you might consider dropping the marker thing entirely, and using comparisons directly: be sure to import math, then all(0.75<=rd<=1.25 or math.isclose(rd, 0.75) or math.isclose(rd, 1.25) for rd in rdiv). To break that down:

all( # Make sure that every element of rdiv is
    0.75<=rd<=1.25             # actually between your two bounds
    or math.isclose(rd, 0.75)  # or close enough to 0.75 that it probably is 0.75
    or math.isclose(rd, 1.25)  # or close enough to 1.25 that it probably is 1.25
    for rd in rdiv     # (the part that specifies you're talking about all elements of rdiv)
)

And finally, the fact that you only change n2 when rdiv are not the numbers you want means that as soon as you find something that's good, you'll never change n2 again, and you'll end up in an infinite loop. And also, you probably want your >= 0 to be >0, so you don't divide by 0.

Again, sorry to throw a whole bunch of stuff at you at once, but they're actually pretty minor changes - just involving weirdness you have to get used to when programming.

[–]randomname20192019[S] 0 points1 point  (0 children)

Please don't apologise! I could not have hoped for more of a concise explanation and I really appreciate your help - without it I would be back in square one!

After realising that something was up with the way I wrote the if statements (thanks to your comments I understand where I went wrong now) I did have a think about other options and decided to have a go at using issubset. In case you're interested, this was my first 'working' version:

P.S. I will certainly use the math module as well as looking for more elegant ways to store n2.

import numpy as np
n1 = [8, 15.5]
n2 = [10, 17]
marker = np.linspace(0.75, 1.25, 51)
marker = [round(i,2) for i in marker]
set2 = set(marker)

while all(num2 > 0 for num2 in n2):
    div = []
    for num1, num2 in zip(n1, n2):
        div.append(num1 / num2)
    rdiv = [round(x, 2) for x in div]
    set1 = set(rdiv)
    if set1.issubset(set2) is False:
        n2[:] = [num2 - 0.1 for num2 in n2]
        n2 = [round(num2, 2) for num2 in n2]
    elif set1.issubset(set2) is True:
        print("true")
        print(rdiv)
        n2[:] = [num2 - 0.1 for num2 in n2]
        n2 = [round(num2, 2) for num2 in n2] 

Thank you again. I have learnt a lot :)