Search code examples
pythonloops

Minimum number of steps to reach a given number


I need to calculate the minimum number of ways to reach a value, x, from value n, by adding/subtracting a list of values, l, to n.

For example: Value n = 100, value X = 45 List, l,: 50,6,1

The best way to do this is to say: 100-50-6+1 = 45

I want a programme to work this out for any value of x and n given list, l

I am really struggling to outline how I would write this.

I am confused about how to overcome the following issues:

  • How to inform the programme if I should attempt an addition or subtraction and how many times this should be done. For example I might need to subtract, then add, then subtract again to reach a solution
  • How do I include enough for/while loops to ensure I can provide a solution for all possible input values

Has anyone come across an issue like this before and have any ideas how I could outline the code for such a solution (I am using Python if it helps direct me towards learning about particular functions available that could assist me)

Thanks

This is my attempt so far but I am stuck

inputA = ""
while inputA == "":
    inputA = input("""Please enter two numbers, separated by a comma.
                The first value should indicate the number of jugs:

                The second value should indicate the volume to be measured

                """)

itemList = list(inputA.split(","))
valueToMeasure = int(itemList[1])

inputB = ""

while inputB == "":
    inputB = input("Plese enter the volumes for the {} jug(s) listed: ".format((itemList[0])))

    if len(inputB.split(",")) != int(itemList[0]):
        inputB = ""

TargetVolume = itemList[1]
jugSizes = inputB.split(",")

print("Calculating: smallest number of steps to get", TargetVolume, "ml using jugs of sizes:", jugSizes)


jugSizes.sort()
jugSizes.reverse()
largestJug = int(jugSizes[0])

ratioTable = {}
for item in jugSizes:
    firstVal = int(jugSizes[0])

    itemV = int(item)
    valueToAssign = firstVal/itemV

    ratioTable[int(item)] = int(valueToAssign)

taskPossible = True

if valueToMeasure > largestJug:
    print ("Impossible task")
    taskPossible = False

newList = jugSizes
if taskPossible == True:
    for item in jugSizes:
        if item < TargetVolume: break
        newList = newList[1:]
        newDict  = {}
        for itemA in ratioTable:
            if int(itemA) < int(item):
                newDict[itemA]= ratioTable[itemA]
        print ("Do work with these numbers:", newDict)


Solution

  • This is how I would approach the problem if I understand correctly.

    X = 45
    largest_jug = measured = 100
    jug_sizes = [50, 6, 1]
    steps = []
    jug_to_use = 0
    
    while measured != X:
        if jug_to_use < len(jug_sizes) - 1: # we have smaller jugs in reserve
            error_with_large_jug = min([abs(measured - jug_sizes[jug_to_use] - X), abs(measured + jug_sizes[jug_to_use] - X)])
            error_with_small_jug = min([abs(measured - jug_sizes[jug_to_use + 1] - X), abs(measured + jug_sizes[jug_to_use + 1] - X)])
            if error_with_small_jug < error_with_large_jug:
                jug_to_use += 1
        if measured > X:
            measured -= jug_sizes[jug_to_use]
            steps.append(('-', jug_sizes[jug_to_use]))
        else:
            measured += jug_sizes[jug_to_use]
            steps.append(('+', jug_sizes[jug_to_use]))
    print(steps)
    

    Yielding

    [('-', 50), ('-', 6), ('+', 1)]
    

    It basically starts by using the largest jug, until it's in range of the next size and so on. We can test it with randomly sized jugs of [30, 7, 1] and see it again results in an accurate answer of [('-', 30), ('-', 30), ('+', 7), ('-', 1), ('-', 1)].

    Important notes:

    • jug_sizes should be ordered largest to smallest
    • This solution assumes the X can be reached with the numbers provided in jug_sizes (otherwise it will infinitely loop)
    • This doesn't take into account that a jug size can make the target unreachable (i.e. [50, 12, 5] where the 12 size should be skipped, otherwise the solution is unreachable
    • This assumes every jug should be used (related to above point)

    I'm sure you could figure out solutions for all these problems based on your specific circumstances though