0
votes

So my main objective in this code is to check if a randomly generated number (s) is equal bigger than any other component of a list of randomly generated numbers. If this is the case, then the number that has been examined should be added to the single random generated number, marked as "used", retired from the list and then the process should start again. In the case that there are two or more numbers that are inferior to the single random generated number, both should be "marked" and retired from the list, but only the biggest of the two numbers should be added to the single generated one for the next loop. At the end it should say how many loops it took to either have all the random numbers in the list "marked" and used, or a loop passes without any number being "marked"

As this explanation might not be clear, I will put an example here. Imagine the single number generated (s) is 0.2, while the list of randomly generated numbers are (0.2, 0.3, 0.4, 0.5). The algorithm would first check s against 0.2, and as it is equal, s would become 0.2 + 0.2 = 0.4, 0.2 would be "marked" and retired from the pool, and a new loop would began. In the next loop, the algorithm would check that both 0.3 and 0.4 are equal or smaller than the new s (0.4), so both would be "marked", retired, and s would become 0.2 + 0.4 = 0.6. In the last cycle, 0.5 would be marked and retired, and s would become 0.2 + 0.5 = 0.7, with 3 loops being required to mark all the numbers.

So far I have come up with this ( disregard the specifications regarding the random number generation, please)

import random
import numpy as np

for x in range(0, 101):
    s = random.gauss(0.5, 1)
    s = round(s, 2)
    if s < 0:
        s = 0
    if s > 1:
        s = 1
    print("This is S")
    print(s)
    ai = []
    ai_size = 10
    for i in range(ai_size):
        num = float(random.gauss(0.5,1))
        num = round(num, 2)
        if num < 0:
            num = 0  
        if num > 1:
            num = 1
        ai.append(num)

    print("This is the vector of Ai")
    print(ai)
    ai = sorted(ai)
    print("This is the vector numerically ordered")
    print(ai)
    ai_mean = (np.mean(ai))
    ai_var = (np.var(ai))
    print(ai_mean)
    print(ai_var)
    rev = 0
    loop = 0
    for i in ai:
            if s >= i:
                s +=  i
                rev += 1 
                loop += 1
                print("This is evolving S")
                print(s)
            else:
                 break
    print("Loop is over")        
    print("This is the number of elements within the list that are marked")
    print(rev)
    print("This is the number of loops")
    print(loop)
    ai_percentage = rev / ai_size * 100
    print("This is the percentage")
    print(ai_percentage)
    

The problem with this code is mainly that, when there are more than one number that should be marked and retired within any given loop, it only checks the first one before going to the next loop, which means that it always ends with the same amount of loops and numbers that have been marked (while in a lot of cases the number of loops should be smaller). It also makes S a continous sum of all the marked numbers (in the previous example it would do 0.2 + 0.2 + 0.3 + 0.4 + 0.5) rather than keeping the original S constant and just adding the highest marked number, as I have previously explained in the example.

Any ideas or tips regarding this would be appreciated.

1
TLDR; what is your question? - deadshot
My main question would be that my code, rather than comparing all the numbers of a list against a single one, does it only with the first number of the list that fulfills the comparison requirements before starting the next loop, artificially increasing the number of loops that it takes for all the comparisons to be made - ffolkvar
A comment on the code style: variable names should start by lowercase letters, uppercase starts are reserved for class names in Python. - Adirio
I get the feeling that if this is part of a larger system, there may be an easier way to solve the problem. Is this the case? Or is it perhaps just an oddly specific coding challenge? - dantechguy

1 Answers

2
votes

So the question has two parts:

Lets first fix conceptually the continuous sum issue you mentioned. As we don't want to sum them continuously, we have to keep two different parts, the "base" and the "delta". I will be using base_s as the base and delta_s as the delta:

import random
import numpy as np

for x in range(0, 101):
    base_s = random.gauss(0.5, 1)
    base_s = round(s, 2)
    if base_s < 0:
        base_s = 0
    if base_s > 1:
        base_s = 1
    delta_s = 0.0
    print("This is S")
    print(base_s + delta_s)

    # ...

    rev = 0
    loop = 0
    for i in ai:
        if base_s + delta_s >= i:
            delta_s = i
            rev += 1
            loop += 1
            print("This is evolving S")
            print(base_s + delta_s)
        else:
            break

    # ...

Now that we solved the small part lets focus on how to handle multiple elements that verify the condition at the same time. First, python has a built-in function called filter that allows us to obtain a part of a sequence that verifies some condition. The first parameter to filter is a funcion that will be called for every element, if it returns True the element will be included, otherwise omitted. list(filter(lambda x: x <= base_s + delta_s, ai)) would return [0.2]. the list(...) is to convert it back into a list as it returns a special kind of sequence, and the lambda x: x <= base_s + delta_s is just an inline way of defining a function that returns true for elements lower or equal than s (base plus delta).

Now lets turn this into a useful function. It will execute a single loop iteration, so we will call it iterate. The first argument will be the list of numbers, the second will be the base S and the third the delta S (0.0 by default). We will also have the loop, and rev count (defaulting to 1) as 4th and 5th arguments. It will return the remaining items, the final s, and the loop and rev counters.

def iterate(items, base_s, delta_s=0.0, loop=0, rev=0):
    # Compute s
    s = base_s + delta_s
    # Get the elements lower or equal to S
    extracted = list(filter(lambda x: x <= s, items))

    # If there are no elements, we don't have to iterate any longer
    if len(extracted) == 0:
        return items, s, loop, rev

    # Remove the lower or equal elements
    items = list(filter(lambda x: x > s, items))
    # Find the biggest  extracted element as this will be the new delta_s
    delta_s = max(extracted)
    # Update loop and rev counters
    loop += 1
    rev += len(extracted)

    # Once everything is updated we can call the same function again to do another loop
    return iterate(items, base_s, delta_s, loop, rev)

We already have our function that when called once will do as many iterations as needed. These functions are called recursive functions because they call themselves. The first iterate call will call the second, and so on, and then when one reaches the if len(extracted) == 0 that means that no iteration needs to be done, the nexted calls will return one after another to give the final result.

It can actually be made shorter (I kept it longer so that it would be easier to understand):

def iterate(items, base_s, delta_s=0.0, loop=0, rev=0):
    s = base_s + delta_s
    extracted = list(filter(lambda x: x <= s, items))
    if len(extracted) == 0:
        return items, s, loop, rev
    return iterate(list(filter(lambda x: x > s, items)), base_s, max(extracted), loop + 1, rev + len(extracted))

To use it to solve your example you would call:

remaining_items, s, loop, rev = iterate([0.2, 0.3, 0.4, 0.5], 0.2)

I leave as your exercise to introduce it into your code, leave a comment if you find any issue with it and I will help you.