ArrayList Min/Max Percentage

Hello

In the following example I am trying to:

  1. Create an arraylist of random float values from 0 to 100,
  2. Calculate the minimum and maximum values of the arraylist,
  3. Map the values of the arraylist to another numerical range,
  4. If a new value is added to the arraylist, recalculate the percentage values.

This means that at the end I want to end up with this: Incoming Arraylist [100, 10, 50, 20], Out Arraylist [1., 0.1, 0.5, 0.2]. However, if a new value is added, the out arraylist should change accordingly: Incoming Arraylist [100, 10, 50, 20, 200], Out Arraylist [0.5, 0.05, 0.25, 0.1].

float randomNum = 0.;
ArrayList<Float> arrangeNums = new ArrayList<Float>();
ArrayList<Float> mappedNums = new ArrayList<Float>();

float min = Float.MAX_VALUE;
float max = Float.MIN_VALUE;

void keyReleased() {
  if (key == 'a') {
    randomNum = random (0., 100.);
    arrangeNums.add(randomNum);

    for (int i = 0; i < arrangeNums.size(); i++) {

      //Find the minimum value of the arraylist
      if (arrangeNums.get(i) < min) {
        min = arrangeNums.get(i);
        println("min " + min);
      }

      //Find the maximum value of the arraylist
      if (arrangeNums.get(i) > max) {
        max = arrangeNums.get(i);
        println("max " + max);
      }

      //Map values from minimum to maximum
      float mapper = map(arrangeNums.get(i), min, max, 0., 1.);

      mappedNums.add(mapper);
    }
    println(mappedNums);
  }
}

Answers

  • edited November 2013

    Structure FloatList got methods min() & max() already! :P

    And since your final goal is to map() all values between 0 & 1, which is the same as norm(); why not merely use random(1)???

  • Thanks for the FloatList suggestion, I didn't know. The min and max is not the problem, as I get the values I want, from the above example, my issue is that I can't figure out how to convert them to the new values, store them in the arraylist, and if a new value enters, to re-calculate the stored values.

    The incoming values may vary - my example simply illustrates the problem, it is not the actual values that I will be expecting.

    So, my main concern is how to do the following (as I also mention above):

    Incoming Arraylist [100, 10, 50, 20], Out Arraylist [1., 0.1, 0.5, 0.2]. However, if a new value is added, the out arraylist should change accordingly: Incoming Arraylist [100, 10, 50, 20, 200], Out Arraylist [0.5, 0.05, 0.25, 0.1].

  • you can't just add the new number (as in line 30), you have to go through the entire arrangeNums list and do that calculation on those. they have all (potentially) changed

    actually, you can skip this if the new number is within the existing range but if it's less than min or more then max then you have to recalculate everything

  • The posted code does what you want so what is the question?

    If you are looking at a better way to do it I have come observations

    1) The mapping (line 28) simplifies dividing the percentage by 100.0 to get the proportion - so why not just do the division.

    2) Since the mapping is so simply do you need to keep both arraylists can you not simply convert the value before adding to the arraylist?

    3) When you add a value you scan the whole arraylist for min/max but if you keep hold of the min max values then you only need to compare them with new values when added.

  • there's a problem with the question in that he assumes there's a 0 in the incoming array, but there's not.

    Incoming Arraylist [100, 10, 50, 20], Out Arraylist [1., 0.1, 0.5, 0.2].

    surely 10 is the min here and will map to 0?

    However, if a new value is added, the out arraylist should change accordingly: Incoming Arraylist [100, 10, 50, 20, 200], Out Arraylist [0.5, 0.05, 0.25, 0.1].

    same problem. and you've forgotten to add the 1.0 at the end for the new 200

    gotoloop and quark would be right but for the changing of the domains, but it's not statically constrained 0-100, it changes. you're almost there with the map(), just need to do the recalculation of everything.

  • edited November 2013

    I thought that my explanation was sufficient. I want based on the minimum and maximum values of the incoming number, to calculate a new arraylist that correspond to a percentage value according to min & max values of the whole sequence.

    Incoming Arraylist1 [100, 10, 50, 20], Out Arraylist1 [1., 0.1, 0.5, 0.2]. Minimum: 10, Maximum, 100

    Incoming Arraylist2 [100, 10, 50, 20, 200], Out Arraylist2 [0.5, 0.05, 0.25, 0.1, 1.] Minimum: 10, Maximum: 200

    So the problem is how to re-calculate the results

  • Answer ✓

    Unfortunately the out values you provide do not match your description because the out array will always have a 0.0 and a 1.0 value provided max > min.

    Incoming Arraylist1 [100, 10, 50, 20], Out Arraylist1 [1., 0.1, 0.5, 0.2]. Minimum: 10, Maximum, 100

    should be

    Incoming Arraylist1 [100, 10, 50, 20], Out Arraylist1 [1.0, 0.0, 0.44, 0.11]. Minimum: 10, Maximum, 100

    Anyway sorry about that missed the dynamic use of map I think that this code does what you want

    float randomNum = 0.0, mapper;
    
    // Keep the next four
    ArrayList<Float> arrangeNums = new ArrayList<Float>();
    ArrayList<Float> mappedNums = new ArrayList<Float>();
    
    float min = Float.MAX_VALUE;
    float max = Float.MIN_VALUE;
    
    void draw() {
    }
    
    void keyReleased() {
      if (key == 'a') {
        randomNum = random (0.0, 100.0);
        arrangeNums.add(randomNum);
    
        if (randomNum >= min && randomNum <= max) { // In range so simply add the new value
          mapper = map(randomNum, min, max, 0.0, 1.0);
          mappedNums.add(mapper);
        }
        else {
          // Test both min and max just in case it is the first value
          if (randomNum < min)
            min = randomNum;
          if (randomNum > max)
            max = randomNum;
          // dump the old mapped values
          mappedNums.clear(); 
          // calculate the new values
          for (int i = 0; i < arrangeNums.size(); i++) {
            //Map values from minimum to maximum
            mapper = map(arrangeNums.get(i), min, max, 0.0, 1.0);
            mappedNums.add(mapper);
          }
        }
        println(min, max, randomNum, mapper);
      }
      if (key == 'b') {
        println(mappedNums);
      }
    }
    
  • Ok, now I got it. Thanks a lot for the example!

  • edited November 2013

    To promote usage of latest Processing's composite structures, same program above but using FloatList now! :\">
    Also using norm() in place of map(); since range is 0..1 anyways! ;))
    Plus min() & max() functions too!

    // forum.processing.org/two/discussion/1075/arraylist-minimummaximum-percentage
    
    final FloatList inList = new FloatList(), outList = new FloatList();
    float minimum = Float.MAX_VALUE, maximum = Float.MIN_VALUE;
    
    void setup() {
      noLoop();
    }
    
    void keyTyped() {
      if (key == RETURN | key == ENTER)   println("\n" + outList + "\n");
      else                                redraw();
    }
    
    void draw() {
      final float rnd = random(100);
      inList.append(rnd);
    
      // If within range simply add new value:
      if (rnd >= minimum & rnd <= maximum)   outList.append(norm(rnd, minimum, maximum));
    
      else {
        // Update both minimum & maximum values:
        minimum = min(minimum, rnd);
        maximum = max(maximum, rnd);
    
        // Dump old mapped values:
        outList.clear();
    
        // Recalculate new mapped values:
        for (float in: inList)  outList.append(norm(in, minimum, maximum));
      }
    
      println(rnd + " \t " + minimum + " \t " + maximum);
    }
    
  • edited November 2013

    It is accepted practice to use the logical operators rather than the bitwise operators in conditional statements unless it is necessary to evaluate both boolean expressions. So could we use

    if (key == RETURN || key == ENTER)

    and

    if (rnd >= minimum && rnd <= maximum)

    Thanks

  • Yes, I haven't understood why GoToLoop, so obsessed of performance, doesn't use the short-circuit boolean operators.

    In

    if (key == RETURN | key == ENTER)
    

    both comparisons are done, while in

    if (key == RETURN || key == ENTER)
    

    if key is RETURN, Java won't try to compare it to ENTER...

  • edited November 2013

    I've already told many times. The time Java takes to setup a branching shortcut decision isn't faster than comparing simple variables! [-O<

  • edited November 2013

    Oh, yes, I recall now. I am skeptical. Beside this performance issue, I don't think spreading this kind of practice in a newbies forum is a good idea, one day we will see

    if (x != null & x.foo() > 5)
    

    or

    if (x.foo() < 3 | x.lengthlyOperation() > 7)
    

    which are just incorrect or not so much optimized.

    I know you won't make such errors, but people trying to learn from your code might overlook such considerations... All this to gain a new nanoseconds per conditional...

  • edited November 2013

    @GoToLoop

    I've already told many times. The time Java takes to setup a branching shortcut decision isn't faster than comparing simple variables!

    Yes you have and I did challenge this in a previous post here.

    I would be interested in seeing the evidence that the time Java takes to setup a branching shortcut decision isn't faster than comparing simple variables!. Even if there is evidence, the nanoseconds saved in setup would hardly be worth overturning accepted programming practice.

    I agree with @PhiLho that in a newbie forum like this we should be promoting good practice. So I find it strange that in other posts you are more than willing to promote good practice e.g. using constants, interfaces, capitilizing classnames etc but in this case you are advocating bad practice.

Sign In or Register to comment.